나는 다음과 같은 스크립트에서 변수에 무언가를 읽을 수 있다는 것을 알고 있습니다. 변수 = 읽기 그러나 변수에 값을 제출하려면 Enter 키를 눌러야 합니다. Enter 키를 누르지 않고 키 누르기 값을 변수에 제출하려면, 또는 키를 눌렀을 때 반응하지 않으려면 무엇을 알아야 합니까?
답변1
를 사용하면 내장 함수에 대한 인수를 bash
사용하여 개행 없이 읽는 문자 수를 제한할 수 있습니다.-n
read
#!/bin/bash
echo "Ready? [Y/n]: "
read -n 1 y_or_n
echo
case "$y_or_n" in
[Yy]|"")
echo "you said yes"
;;
*)
echo "you said no"
;;
esac
bash
sh
이것은 as 또는 호출 여부에 관계없이 작동합니다 bash
.
자세한 내용은 help read
또는 맨페이지를 참조하십시오.bash
다른 쉘은 -n
매개변수를 지원하지 않을 수도 있습니다 read
. dash
, 예를 들어 아니요.
답변2
셸에 관한 문제는 read
총 바이트 수를 제한하든 안 하든 상관없이 read
여전히 정보를 제공하지 않는다는 것 입니다.단추각자 read
는 일부만 얻을 것이다안정적인수량수치매번 read
하나 얻으려고단추필요 에 따라 read
입력을 차단하는 것이 아마도 가장 좋은 방법일 것입니다 dd
. 다음은 몇 달 전 dd
터미널 I/O를 차단하려고 할 때 작성한 쉘 함수입니다.
printkeys()( IFS= n=1
set -f -- "$(tty <&2)"
exec <${1##[!/]*};set \\t
_dd()( c=conv=sync,unblock i=ibs=72 b=bs=8
dd bs=7 ${c%,*} | dd $i o$b c$b $c
) 2>/dev/null
trap " trap '' TTOU TTIN
stty '$(stty -g
stty raw isig inlcr)' " INT EXIT
_dd |while ${2:+:} read -r c || exit 2
do case $#:$c in (1:): ;;
(*:?*) set ":%d$@" \
\'${c%"${c#?}"} ;
c=${c#?} ;;
(*) printf "$@" ;
[ 0 -eq $((n=n<4?n+1:0)) ] &&
set '\n\r'||set \\t ;; esac
done
)
위의 경우 터미널 입력이 dd
파이프의 두 프로세스 모두에서 필터링되는 것을 볼 수 있습니다. 터미널이 다음 stty
으로 설정되었습니다.날것의의 패턴은 trap
오류 발생 시 최종 상태를 복원 INT
하거나 EXIT
함수가 호출되었을 때 함수의 상태로 복원 할 수도 있습니다.(아마 CTRL+C나 인터럽트 키를 사용하여 수행할 수 있음). 존재하다날것의모드에서 터미널은 입력이 도착하자마자 모든 판독기에게 입력을 플러시합니다(키 입력마다 키 입력). 한 번에 1바이트만 푸시하는 것이 아닙니다. 가능한 한 빨리 전체 버퍼를 푸시합니다. 예를 들어 위쪽 화살표를 누르면 키보드는 다음과 같은 이스케이프 시퀀스를 보냅니다.
^[[A
...3바이트인데 한꺼번에 밀어넣습니다.
dd
일단 제공된 모든 읽기를 충족하도록 지정됩니다(설정한 높이에 관계 없음) ibs=
. 이는 을 사용한 설정에도 불구하고 ibs=7
터미널이 3바이트만 푸시할 때 읽기가 여전히 완료된다는 것을 의미합니다. 이제 대부분의 다른 유틸리티에서는 이를 처리하는 데 문제가 있지만 dd
이것이 conv=sync
bytes 사이의 차이를 채워줍니다 \0NUL
. 따라서 키보드의 위쪽 화살표를 누르면 dd
3바이트가 읽히고 다음 바이트에 7이 기록됩니다. 즉 dd
, 이스케이프 시퀀스의 3바이트와 또 다른 4 \0NUL
바이트입니다.
그러나 쉘의 읽기와 함께 이 데이터를 가져오려면 이를 다시 차단해야 하므로 다음 데이터는 dd
입력 버퍼를 72바이트로 동기화합니다. 그러나 실제로는 conv=unblock
그렇습니다. unblock
변환을 통해 dd
입력을 \n
ewline 구분 기호 로 분할하여 cbs=
개수를 얻습니다. 여기서는 8입니다 sync
.unblock
(또는 block
)변환은 s에서는 동기화 dd
되지 않지만 \0NUL
후행 공백에서는 동기화됩니다. 따라서 7바이트마다 첫 번째는 dd
두 버퍼 사이의 파이프에 dd
72바이트를 씁니다. 처음 몇 바이트는 키의 내용이고 그 다음은 \0NUL
s, 그 다음에는 65개의 공백 읽기의 끝입니다.
또 다른 점은 후행 공백을 제거하는 것입니다. 이는 각 변환 블록 unblock
끝에 존재할 수 있는 만큼의 공백을 차지하게 됩니다. cbs=
따라서 각 dd
쓰기는 바이트 단위로 출력에 쓰므 obs=8
로 각 읽기는 9줄을 쓰므로 총 2번의 쓰기가 출력 파이프에 기록됩니다. 첫 번째 쓰기는 입력 파이프에서 읽은 7바이트와 후행 개행 문자(또 다른 바이트)로 구성된 첫 번째 줄입니다. 다음 쓰기는 8개의 개행(연속 쓰기)입니다. 8바이트가 더 많습니다. 왜냐하면 dd
8개의 변환 블록 각각이 8개의 공백을 모두 차지하기 때문입니다.
dd
두 sa 쉘의 반대쪽을 while
한 줄씩 반복합니다 read
. 빈 줄을 무시할 수 있습니다. 왜냐하면 터미널은 옵션에 따라 모든 줄 바꿈을 출력의 캐리지 리턴으로 변환하기 때문입니다 inlcr
stty
. 그러나 $c
쉘의 입력이 해당 값을 입력한 후 단일 바이트라도 감지 하면 read
키보드가 키 입력당 많은 바이트를 전송하지 않는 한 키 누르기와 전체 키 누르기도 가능합니다. 7바이트 이상(단지 다른 차단 요소가 필요하지만).
셸에 키 누르기가 있으면 $c
바이트 단위로 반복하고 '
문자로 구분된 배열로 분할한 다음 printf
각 바이트의 10진수 값을 한 번에 가져옵니다. $c
함수를 실행하면 다음과 같은 출력이 표시됩니다.
a:97 b:98 c:99 d:100 e:101
f:102 ;:59 ^M:13 ^M:13 ^M:13
s:115 a:97 d:100 f:102 :32
':39 ':39 ':39 a:97 s:115
d:100 f:102 ;:59 ':39 ^[[A:27:91:65
^[[D:27:91:68 ^[[B:27:91:66 ^[[C:27:91:67 ^[[D:27:91:68 ^[[C:27:91:67
^[[A:27:91:65 ^[[D:27:91:68 ^[[C:27:91:67 ^[[B:27:91:66 ^[[D:27:91:68
^[[C:27:91:67 ^[[A:27:91:65 ^[[D:27:91:68 ^[[C:27:91:67 ^[[B:27:91:66
쉘이 변수의 값을 입력으로 채우면 키 누름을 방지하기 위해 삽입된 s가 \0NUL
사라지기 때문에 s를 쉘 변수에 넣을 수 없습니다.dd
\0NUL
(그러나 어떤 쉘에서든 zsh
- 이 경우에는 여전히 다음과 같이 구성할 수 있습니다). 내가 아는 한, 이것은 모든 쉘에서 작동해야 하며 , 및 에서도 bash
작동 dash
합니다 ksh93
. 멀티바이트 입력도 안정적으로 처리합니다. 하지만 그렇다고 장담할 수는 없습니다.
위의 데모 출력에서는 작성될 때마다 함수의 실제 출력 앞에 작성되지 않은 추가 정보가 표시됩니다. :
위의 순서에서 처음으로 나타나기 전에 표시되는 각 문자는 실제로 터미널 문자이며 사용 하거나 구성 echo
할 수 있습니다 . 나머지는 함수의 출력으로 인쇄됩니다. 보시다시피 입력 직후에 인쇄됩니다.stty echo
-echo