루프를 중지하려면 아무 키나 누르세요.

루프를 중지하려면 아무 키나 누르세요.

다음을 수행하는 방법은 무엇입니까?

  • 루프 스크립트가 있습니다.
  • 키를 누르면 중지하고 싶습니다. 그렇지 않으면 5초 후에 계속됩니다.

답변1

염두에 두어야 할 점은 일반적으로 터미널에서 실행하는 셸이나 응용 프로그램은 키보드 및 화면과 상호 작용하지 않는다는 것입니다.

그들은 stdin에서 바이트 스트림으로 입력을 받습니다. 해당 stdin이 일반 파일에서 오면 바이트가 거기에서 나오고, 파이프인 경우 일반적으로 다른 프로세스에서 전송되는 데이터이고, 컴퓨터에 연결된 물리적 장치에 올 수 있는 일부 장치 파일인 경우입니다. 예를 들어, tty 문자 장치인 경우 일반적으로 터미널에서 일부 직렬 회선을 통해 전송되는 데이터입니다. 터미널은 키보드 이벤트를 바이트 시퀀스로 변환하는 일종의 장치입니다.

바로 여기에 터미널 애플리케이션의 모든 기능이 존재합니다. 입력 메커니즘이 추상화되어 있으므로 스크립트에서 대화형으로 또는 자동으로 사용할 수 있습니다.

여기서 이런 종류의 프롬프트를 발행하고 다음을 기대한다면열쇠보도 자료 이벤트를 통해 귀하의 응용 프로그램(스크립트)이 대화형 응용 프로그램이 되기를 원할 것입니다. stdin이 터미널이 될 것으로 예상하거나 stdin이 무엇에 열려 있는지에 관계없이 터미널에서 입력을 받습니다.

이제 위에서 볼 수 있듯이 모든 애플리케이션은 바이트 스트림을 보게 됩니다. 이는 터미널(또는 터미널 에뮬레이터)의 역할과 누른 키를 바이트 시퀀스로 변환하는 tty 장치 라인 분야의 역할입니다. 몇 가지 예:

  • 키를 누르면 aASCII 터미널은 0x61 바이트를 보냅니다.
  • 키를 누르면 £UTF-8 터미널은 2바이트 0xc2와 0xa3을 보냅니다.
  • 키를 누르면 EnterASCII 터미널은 Linux와 같은 ASCII 기반 시스템의 tty 라인 규정이 일반적으로 0x0a로 변환되는 0x0d 바이트를 보냅니다.
  • 단독으로 누르면 Ctrl터미널은 아무 것도 보내지 않지만 와 함께 누르면 C터미널은 tty 라인 규칙이 가로채서 SIGINT 신호를 포그라운드 작업에 보내는 0x03 바이트를 보냅니다.
  • 을 누르면 Left터미널은 일반적으로 바이트 시퀀스를 보냅니다(터미널에 따라 다르며 응용 프로그램은 이를 변환하기 위해 terminfo 데이터베이스를 쿼리할 수 있음). 그 중 첫 번째는 0x1b입니다. 예를 들어, ASCII 기반 시스템에서는 해당 모드에 따라 0x1b 0x4f 0x44 또는 0x1b 0x5b 0x44( 또는 ) xterm를 보냅니다 .<ESC>[A<ESC>OA

그래서 여기서 제가 묻고 싶은 질문은 다음과 같습니다.

  1. stdin이 터미널이 아닌 경우에도 사용자에게 메시지를 표시하시겠습니까?
  2. 1에 대한 대답이 '예'이면 터미널에서 사용자에게 메시지를 표시하시겠습니까, 아니면 stdin/stdout을 통해 메시지를 표시하시겠습니까?
  3. 1에 대한 대답이 '아니요'인 경우에도 각 반복 사이에 5초를 기다리시겠습니까?
  4. 2번의 답이라면터미널을 통해, 제어 터미널을 감지할 수 없거나 비터미널 모드로 돌아갈 수 없는 경우 스크립트가 중단되어야 합니까?
  5. 프롬프트를 표시한 후 누른 키만 고려하시겠습니까? IOW, 프롬프트가 표시되기 전에 사용자가 실수로 키를 입력한 경우.
  6. 단일 키 누름에 대해 발행된 바이트만 읽을 수 있도록 어느 정도까지 하시겠습니까?

여기서는 스크립트가 터미널 대화형 애플리케이션으로만 작동하고 stdin/stdout은 그대로 두고 제어 터미널을 통해서만 상호작용하기를 원한다고 가정합니다.

#! /bin/sh -

# ":" being a special builtin, POSIX requires it to exit if a
# redirection fails, which makes this a way to easily check if a
# controlling terminal is present and readable:
:</dev/tty

# if using bash however not in POSIX conformance mode, you'll need to
# change it to something like:
exec 3< /dev/tty 3<&- || exit

read_key_with_timeout() (
  timeout=$1 prompt=$2
  saved_tty_settings=$(stty -g) || exit

  # if we're killed, restore the tty settings, the convoluted part about
  # killing the subshell process is to work around a problem in shells
  # like bash that ignore a SIGINT if the current command being run handles
  # it.
  for sig in INT TERM QUIT; do
    trap '
      stty "$saved_tty_settings"
      trap - '"$sig"'
      pid=$(exec sh -c '\''echo "$PPID"'\'')
      kill -s '"$sig"' "$pid"

      # fall back if kill failed above
      exit 2' "$sig"
  done

  # drain the tty's buffer
  stty -icanon min 0 time 0; cat > /dev/null

  printf '%s\n' "$prompt"

  # use the tty line discipline features to say the next read()
  # should wait at most the given number of deciseconds (limited to 255)
  stty time "$((timeout * 10))" -echo

  # do one read and count the bytes returned
  count=$(dd 2> /dev/null count=1 | wc -c)

  # If the user pressed a key like the £ or Home ones described above
  # it's likely all the corresponding bytes will have been read by dd
  # above, but not guaranteed, so we may want to drain the tty buffer
  # again to make sure we don't leave part of the sequence sent by a
  # key press to be read by the next thing that reads from the tty device
  # thereafter. Here allowing the terminal to send bytes as slow as 10
  # per second. Doing so however, we may end up reading the bytes sent
  # upon subsequent key presses though.
  stty time 1; cat > /dev/null

  stty "$saved_tty_settings"

  # return whether at least one byte was read:
  [ "$(($count))" -gt 0 ]

) <> /dev/tty >&0 2>&0

until
  echo "Hello World"
  sleep 1
  echo "Done greeting the world"
  read_key_with_timeout 5 "Press any key to stop"
do
  continue
done

답변2

while true; do
    echo 'Looping, press Ctrl+C to exit'
    sleep 5
done

그보다 더 복잡하게 만들 필요는 없습니다.

다음이 필요합니다 bash.

while true; do
    echo 'Press any key to exit, or wait 5 seconds'
    if read -r -N 1 -t 5; then
        break
    fi
done

read시간 초과로 인해 실패 하면 루프가 계속됩니다.

관련 정보