다음에는 무엇을 해야 할까요?
- 루프 스크립트가 있습니다.
- 키를 누르면 중지하고 싶습니다. 그렇지 않으면 5초 후에 계속됩니다.
답변1
기억해야 할 점은 일반적으로 터미널에서 실행되는 셸이나 응용 프로그램은 키보드 및 화면과 상호 작용하지 않는다는 것입니다.
표준 입력에서 바이트 스트림으로 입력을 받습니다. 표준 입력이 일반 파일에서 오면 바이트가 거기에서 나오고, 파이프인 경우 데이터는 일반적으로 다른 프로세스에 의해 전송되며, 컴퓨터에 연결된 물리적 장치에 도달할 수 있는 일부 장치 파일인 경우. 예를 들어, tty 문자 장치인 경우 일반적으로 일부 직렬 회선을 통해 터미널에서 전송되는 데이터입니다. 터미널은 키보드 이벤트를 바이트 시퀀스로 변환하는 일종의 장치입니다.
이것이 터미널 앱의 전부입니다. 입력 메커니즘이 추상화되어 스크립트에서 대화형으로 또는 자동으로 사용할 수 있습니다.
여기서 이런 종류의 프롬프트를 발행하고 살펴보려면열쇠기자 회견에서 아마도 귀하의 애플리케이션(스크립트)이 대화형 애플리케이션이 되기를 원할 것입니다. stdin이 터미널이 될 것으로 예상하거나, 어떤 stdin이 열려 있는지에 관계없이 터미널에서 입력을 받으세요.
이제 위에 표시된 대로 모든 애플리케이션이 보는 바이트 스트림은 터미널(또는 터미널 에뮬레이터) 및 tty 장치 라인 규칙이 수행하는 작업이며 누른 키를 바이트 시퀀스로 변환합니다. 몇 가지 예를 들면 다음과 같습니다.
- 이 키를 누르면 aASCII 터미널은 0x61 바이트를 보냅니다.
- 이 키를 누르면 £UTF-8 터미널은 0xc2와 0xa3의 두 바이트를 보냅니다.
- 이 키를 누르면 EnterASCII 터미널은 0x0d 바이트를 전송하며, 이는 ASCII 기반 시스템(예: Linux)에서 일반적으로 tty 라인 규칙에 따라 0x0a로 변환됩니다.
- 단독으로 누르면 단말기는 아무 것도 보내지 않지만 Ctrl, 다음과 같이 누르면C
- 를 누르면 Left터미널은 일반적으로 일련의 바이트를 보냅니다(터미널마다 다르며 응용 프로그램은 이를 변환하기 위해 terminfo 데이터베이스를 쿼리할 수 있음). 그 중 첫 번째는 0x1b입니다. 예를 들어,
xterm
ASCII 기반 시스템에서는 해당 모드에 따라 0x1b 0x4f 0x44 또는 0x1b 0x5b 0x44(<ESC>[A
또는<ESC>OA
) 가 전송됩니다.
그래서 여기서 내가 묻는 질문은 다음과 같다.
- stdin이 터미널이 아닌 경우에도 사용자에게 메시지를 표시하시겠습니까?
- 1에 대한 대답이 '예'라면 터미널에서 사용자에게 메시지를 표시하시겠습니까, 아니면 stdin/stdout을 통해 메시지를 표시하시겠습니까?
- 1에 대한 대답이 '아니요'인 경우에도 각 반복 사이에 5초를 기다리시겠습니까?
- 2번의 답이라면터미널을 통해, 제어 터미널을 감지할 수 없거나 비터미널 모드로 돌아갈 수 없는 경우 스크립트가 중단되어야 합니까?
- 프롬프트가 표시된 후 누른 키만 고려하시겠습니까? IOW, 프롬프트가 표시되기 전에 사용자가 실수로 키를 입력한 경우.
- 단일 키 누르기에 의해 방출된 바이트만 읽도록 보장하기 위해 얼마나 오래 갈 의향이 있습니까?
여기서는 스크립트가 터미널 대화형 애플리케이션으로만 사용하고 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
루프가 계속됩니다.