次は何をすべきですか?
- ループスクリプトがあります。
- キーを押すと中止したいです。それ以外の場合は5秒後に続行されます。
答え1
覚えておくべきことは、通常、端末で実行されるシェルやアプリケーションはキーボードや画面と対話しないことです。
標準入力からバイトストリームに入力を受け取ります。標準入力が通常のファイルから来ると、バイトがそこから出て、パイプの場合、データは通常別のプロセスによって送信され、コンピュータに接続されている物理デバイスに到達する可能性がある一部のデバイスファイルの場合。たとえば、tty 文字デバイスの場合、通常は一部のシリアル回線を介して端末から送信されるデータです。端末は、キーボードイベントをバイトシーケンスに変換する一種のデバイスです。
これがターミナルアプリのすべてです。入力メカニズムが抽象化され、スクリプトでインタラクティブにまたは自動的に使用できます。
ここでこの種のプロンプトを発行して調べるには鍵記者会見では、おそらくあなたのアプリケーション(スクリプト)がインタラクティブなアプリケーションになりたいと思うでしょう。 stdinが端末になることを期待するか、どのstdinが開いているかに関係なく、端末から入力を受け取ります。
上記のように、すべてのアプリケーションが表示するバイトストリームは、ターミナル(またはターミナルエミュレータ)とttyデバイスラインルールが実行するアクションであり、押したキーをバイトシーケンスに変換します。いくつかの例は次のとおりです。
- このキーを押すと、aASCII端子は0x61バイトを送信します。
- このキーを押すと、£UTF-8 端末は 0xc2 と 0xa3 の 2 バイトを送信します。
- このキーを押すと、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
ループは続行されます。