일정 시간이 지나도 사용자가 명령을 입력하지 않는 경우

일정 시간이 지나도 사용자가 명령을 입력하지 않는 경우

바라보다아래 답변zsh 솔루션의 경우


~/.bash_aliases시스템을 일시 중단하기 전에 다음을 확인해야 한다고 가정해 보겠습니다 .

function suspend()
{ #
    echo "Please confirm/cancel system suspension:"
    select confirmation in "confirm" "cancel"; do
        case ${confirmation} in
        confirm )
            echo "System suspending..."
            systemctl suspend
            break;;
        cancel )
            echo "Canceled suspension."
            break;;
        esac
    done
}

systemctl suspend사용자가 대답하지 않으면 계속 실행되기를 원합니다. 예를 들어 10초 후에도 사용자 입력이 없으면 "confirm" 케이스의 내용이 실행됩니다.

sleep서브셸에서 다음을 시도했습니다.

function suspend()
{ #
    flag_cancel=0
    echo "Please confirm/cancel system suspension:"
    (
    sleep 10 &&
        if [ $flag_cancel -eq 0 ]; then
        echo "System suspending..."
        systemctl suspend
        fi &
    )
    select confirmation in "confirm" "cancel"; do
    case ${confirmation} in
        confirm )
        echo "System suspending..."
        systemctl suspend
        break;;
        cancel )
        flag_cancel=1
        echo "Canceled suspension."
        break;;
    esac
    done
}

flag_cancel그러나 값 변경은 고려되지 않으므로 명령은 항상 에 있습니다 sleep.

내가 원하는 것을 달성하는 방법은 무엇입니까?

답변1

read런타임에 사용자로부터 입력을 받으려면 명령을 사용해 보십시오 . 시간 초과 옵션이 있기 때문입니다.

남성의 경우:

-t timeout 입력의 전체 줄을 시간 초과 초 내에 읽지 않으면 읽기가 시간 초과되고 반환 오류가 발생합니다. timeout은 소수점 뒤에 소수 부분이 있는 십진수일 수 있습니다. 이 옵션은 읽기가 터미널, 파이프 또는 기타 특수 파일에서 입력을 읽는 경우에만 효과가 있습니다. 일반 파일에서 읽을 때는 효과가 없습니다. 시간 제한이 0인 경우 읽기는 지정된 파일 설명자에서 입력이 가능하면 성공을 반환하고 그렇지 않으면 실패를 반환합니다. 시간 초과가 초과되면 종료 상태는 128보다 큽니다.

답변2

sleep호출 sleep및 후속 테스트가 $flag_cancel백그라운드 작업에서 발생하기 때문에 시도가 작동하지 않습니다. 코드의 주요 부분에 있는 변수를 변경해도 flag_cancel백그라운드 서브셸에 있는 변수의 값에는 영향을 주지 않으며 코드는 10초 후에 무조건 시스템을 정지시킵니다.

readselect대신 몇 초 후에 시간이 초과된다는 사실을 사용할 수 있습니다 .$TMOUTbash

다음은 첫 번째 코드 부분의 주제에 대한 변형입니다.

suspend_maybe ()
{
        local PS3='Please confirm/cancel system suspension: '
        local TMOUT=10

        local do_suspend=true

        select confirmation in confirm cancel; do
                case $REPLY in
                        1)
                                # default case
                                break ;;
                        2)
                                do_suspend=false
                                break ;;
                        *)
                                echo 'Sorry, try again' >&2
                esac
        done

        if "$do_suspend"; then
                echo 'Suspending...'
                systemctl suspend
        else
                echo 'Will not suspend'
        fi
}

변경사항:

  1. 이 함수는 지금 호출됩니다 suspend_maybe.suspendbash
  2. 프롬프트를 반복합니다 select.PS3
  3. 몇 초 select후에 루프 시간이 초과됩니다 $TMOUT.
  4. 우리는 명세서에 숫자를 사용합니다 case. 이렇게 하면 모든 문자열을 두 번 입력할 필요가 없습니다. 값은 $REPLY사용자가 입력하는 대로 적용됩니다.
  5. select사용자가 원하는지 알려주는 루프가 필요합니다 .취소시스템이 일시중지되었습니다. 일시 중지를 기본 작업으로 처리합니다.
  6. 루프에서 벗어나면 select사용자가 작업 취소를 선택하지 않는 한 시스템을 일시 중지합니다.

같은 일이지만 입력 루프를 read다음의 드롭인 대체품으로 사용합니다 select.

suspend_maybe ()
{
        local PS3='Confirm system suspension [y]/n: '
        local TMOUT=10

        local do_suspend=true

        while true; do
                if ! read -p "$PS3"; then
                        # timeout
                        break
                fi

                case $REPLY in
                        [yY]*)
                                # default case
                                break ;;
                        [nN]*)
                                do_suspend=false
                                break ;;
                        *)
                                echo 'Sorry, try again' >&2
                esac
        done

        if "$do_suspend"; then
                echo 'Suspending...'
                systemctl suspend
        else
                echo 'Will not suspend'
        fi
}

여기에서 사용자는 일시정지 n또는 N일시정지 해제로 시작하는 문자열을 입력할 수 있습니다. 시간 초과, 또는로 시작하는 단어를 입력 read하거나 누르는 경우 시스템이 일시 중지됩니다.yYCtrl+D

위의 루프를 사용하면 사용자가 프롬프트에 응답하지 않아 일시 중지가 발생할 때 특별한 작업을 수행하려는 경우 시간 초과 상황을 더 쉽게 캡처할 수 있습니다. - 문은 호출이 실패할 때마다(또는 입력 스트림이 닫힐 때) 실행됩니다 break.ifread

답변3

일반적으로 선택할 수 있는 대안은 다음 timeout과 같습니다.GNU 핵심 도구. 내장 명령에 대한 시간 제한을 설정하고 있지만 외부 유틸리티는 전체 셸을 생성해야만 이 작업을 수행할 수 있으므로 이는 최선의 옵션이 아닙니다.

if ! timeout 10 bash -c '
  select conf in yes no
  do
    case $conf in
      (yes) exit 1;;
      (no) exit 0;;
    esac
  done'
then
  echo 'Suspending'
fi

일부 외부 도구(아마도 그래픽 도구)를 통해 사용자 입력을 요청한다면 더 흥미로울 수 있습니다(이러한 도구 중 일부는 자체 시간 제한 메커니즘을 구현하지만).

timeout이 옵션 은 --foreground쉘 프롬프트에서 직접 호출하지 않을 때(예: subshell: 에서 호출하는 경우 ( timeout ... )) 터미널에서 읽을 때 필요합니다.

답변4

을 사용하여 이 작업을 수행할 수도 있습니다 at. 장점: 새로운 도구를 배워야 한다면 가장 다재다능한 도구를 배우십시오. at나중에 언제든지 명령을 예약하십시오. 대기열에서 예약된 작업을 제거하는 데 사용할 수 있습니다 atrm.

귀하의 경우:

function suspend()
{ #
    JOBNO=$(echo "systemctl suspend" | at now + 0.2 seconds 2>&1 > /dev/null | awk '{print $2}')
    echo "Please confirm/cancel system suspension:"
    select confirmation in "confirm" "cancel"; do
    case ${confirmation} in
        confirm )
            echo "System suspending..."
            systemctl suspend
            break;;
        cancel )
            echo "Canceled suspension."
        break;;
    esac
    atrm $JOBNO
    done
}

관련 정보