Bourne 쉘에 해당하는 `read -n` 명령이 있습니까?

Bourne 쉘에 해당하는 `read -n` 명령이 있습니까?

사용자가 아무 키나 누를 때까지 기다렸다가 를 누르지 않고도 스크립트를 계속할 수 있는 방법이 있습니까 Enter? shBash가 아닌 Bourne 쉘( )에서 작동하도록 만들고 싶습니다 .

답변1

예비 참고 사항

요즘 sh반드시 레거시 Bourne 쉘일 필요는 없습니다.. 요즘에는 sh다음을 지원하는 쉘이 있습니다.적어도POSIX에 필요한 기능(올바른 구현을 가정) POSIX는 다음을 지정합니다.sh공익사업그리고쉘 명령 언어.

나는 당신의 코드가 POSIX 셸에서 작동하고 일반적으로 이식 가능하길 원한다고 가정합니다.


문제

dd ibs=1 count=…정확한 바이트 수를 읽는 POSIX 방법입니다.. 작업을 안정적으로 수행할 수 있는 유일한 휴대용 명령줄 유틸리티인 것 같습니다. 그러나 Bash에서는 dd바이트를 읽습니다.read -n문자.POSIX는 다음 이외의 로케일에서 멀티바이트 문자를 허용합니다.POSIX. 를 사용하여 전체 스크립트를 실행하더라도 LC_ALL=POSIX터미널(터미널 에뮬레이터)은 단일 키 입력 시 여전히 멀티바이트 시퀀스를 생성할 수 있습니다. 이러한 시퀀스는 멀티바이트 문자가 아닐 수 있습니다. 이스케이프 시퀀스일 수 있습니다(예를 들어F1, 또한 참조이 답변).

1바이트만 읽은 후 나머지는 그대로 유지되며 나중에 터미널에서 읽으려고 시도하는 모든 항목에서 읽혀집니다(스크립트의 일부이거나 스크립트를 실행하는 대화형 셸일 수 있음).

또한 단독으로 실행하는 경우 초과할 때까지 dd ibs=1 count=1아무 것도 얻을 수 없다는 것을 알게 될 것입니다.dd{MAX_INPUT}또는{MAX_CANON}, 또는 Enter또는Ctrl+Dcount( 보다 큰 경우 더 적은 바이트를 제공하려면 +를 1눌러야 할 수도 있습니다 .CtrlD 두 배).

이러한 문제를 해결하려면 다음이 필요합니다.비정규 모드. 사용stty -icanon. 일반적으로 시작하는 것이 stty raw좋은 생각인 것 같습니다.


암호

다음 예는 개념 증명입니다. 터미널의 라인 설정을 조작하고 터미널에서 읽기 때문에해서는 안 된다대화형 셸에 붙여넣으면 작동하지 않습니다.. 파일에 붙여넣고 파일을 실행합니다.

#!/bin/sh

echo "Press any key to continue."

saveterm="$(stty -g)"                     # save terminal state
stty raw
stty -echo -icanon min 1 time 0           # prepare to read one byte
dd ibs=1 count=1 >/dev/null 2>/dev/null   # read one byte
stty -icanon min 0 time 0                 # prepare to read lefotvers
while read none; do :; done               # read leftovers
stty "$saveterm"                          # restore terminal state

노트

  • 스크립트는 표준 입력이 터미널인지 확인하지 않지만 일반적으로 ([ -t 0 ]).

  • 정상적인 설정에서 수정자 키(예: Shift)만 누르면 터미널에서 읽는 내용에 입력을 보내지 않습니다. 따라서 우리 코드는 "모든 키"와 같은 키를 등록하지 않습니다.

  • "남은 음식 읽기" 트릭은 다음에서 가져왔습니다.이 답변.

  • stty raw Ctrl+ C또는 Ctrl+ 덕분에 또는 각각을 Z보내는 대신 "모든 키"가 될 수도 있습니다 . 여전히 스크립트를 해석하는 쉘은 다른 곳에서 신호를 받을 수 있으므로 일반적으로 에 도달하기 전에 종료될 수 있습니다 . 따라서 터미널이 대화형 사용에 적합하지 않은 상태가 될 수 있습니다. 어쨌든 관련 신호를 트랩하고 터미널의 초기 상태를 복원할 수 있습니다.SIGINTSIGSTOPstty "$saveterm"

  • 사실이야변수는 큰따옴표로 묶어야 합니다일반적으로. 지정되지 않은 출력을 생성하므로 $saveterm의도적으로 인용되지 않았습니다 . stty -g구현에서는 stty공백이 포함된 출력을 생성할 수 있으며 나중에 쉘이 이를 분할하고 여러 인수를 전달할 것으로 예상합니다. 지정된 것은 따옴표가 없는 경우 출력이 stty -g안전해야 하며 쉘에서 단어 확장을 트리거해서는 안 된다는 제약 조건입니다. 이식성을 위해서는 인용되지 않은 $saveterm것이 더 좋습니다.

    편집하다:그것은 POSIX 사양의 결함이었습니다. 위의 코드가 수정되었습니다.

  • 어떤 바이트를 읽었는지 확인하려면 나중에 검사할 dd일반 파일( )이나 변수( ) 에 의 출력을 저장해야 합니다 . 하지만:>some_filevariable="$(dd …)"

    • 터미널은 널 바이트(일반적으로 Ctrl+ 사용 @)를 생성할 수 있지만 대부분의 구현은 sh널 바이트를 변수에 저장할 수 없습니다. 파일에 저장하는 것은 변수로 읽지 않고도 내용을 검사/조작할 수 있는 경우에만 가능합니다.
    • dd위 코드에서는 1바이트만 제공하므로 문자나 키를 알려주는 데 충분하지 않을 수 있습니다. OTOH 단일 바이트는 예를 들어 Q다른 것과 구별하기에 충분해야 하므로 더 많은 바이트를 분석하지 않고도 구현할 수 있는 것처럼 보입니다 Press Q to quit or any other key to continue..P - proceed; B - back; Q - quit; H - help

바이트가 더 많으면 어떨까요?

위의 내용은 "아무 키나 누르십시오"에 적합합니다. 실제 등가물은 read -n한 번에 한 바이트를 읽고 원하는 수의 바이트를 얻을 때까지 시퀀스를 디코딩해야 합니다.문자. 나는 여기에 그것을 구축하려고하지 않을 것입니다.

관련 정보