단일 문자 읽기, null <EOF>
과 \n
? 를 구별하는 방법은 무엇입니까?
예를 들어:
f() { read -rn 1 -p "Enter a character: " char &&
printf "\nYou entered '%s'\n" "$char"; }
인쇄 가능한 문자 포함:
$ f
Enter a character: x
You entered 'x'
누를 때 Enter:
$ f
Enter a character:
You entered ''
Ctrl+를 누를 때 D:
$ f
Enter a character: ^D
You entered ''
$
마지막 두 경우의 출력이 동일한 이유는 무엇입니까? 어떻게 구별할 수 있나요?
POSIX 쉘과 비교하여 다른 접근 방식이 있습니까 bash
?
답변1
(POSIX 기능이 아님) stdin read -n "$n"
이 터미널 장치 인 경우 터미널을 해당 모드 read
에서 꺼냅니다 . 그렇지 않으면 터미널 줄 규칙의 내부 줄 편집기에서 반환된 전체 줄만 확인한 다음 한 번에 한 바이트씩 읽습니다 . 문자 또는 개행 문자를 읽었습니다(잘못된 문자를 입력하면 예상치 못한 결과가 나타날 수 있습니다).icanon
read
$n
$n
한 줄에서 최대 한 문자를 읽습니다. 또한 $IFS
입력에서 IFS 문자가 제거되는 것을 방지하려면 이를 지워야 합니다 .
icanon
이제 우리는 패턴 에서 벗어났기 때문에 ^D
더 이상 특별하지 않습니다. 그래서 누르면 Ctrl+D문자가 읽혀집니다.^D
어떤 방식으로든 터미널 연결이 끊어지지 않는 한 터미널 장치에서 eof를 볼 수 없습니다. stdin이 다른 유형의 파일인 경우 eof가 표시될 수 있습니다(예: : | IFS= read -rn 1; echo "$?"
stdin이 빈 파이프이거나 stdin이 에서 리디렉션됨 /dev/null
).
read
$n
문자(유효한 문자의 일부를 구성하지 않는 바이트는 1문자로 계산됨) 또는 전체 행을 읽은 경우 0이 반환됩니다.
따라서 하나의 문자만 요청하는 특별한 경우에는 다음과 같습니다.
if IFS= read -rn 1 var; then
if [ "${#var}" -eq 0 ]; then
echo an empty line was read
else
printf %s "${#var} character "
(export LC_ALL=C; printf '%s\n' "made of ${#var} byte(s) was read")
fi
else
echo "EOF found"
fi
POSIXly는 수행하기가 매우 복잡합니다.
이는 다음과 같습니다(EBCDIC가 아닌 ASCII 기반 시스템을 가정).
readk() {
REPLY= ret=1
if [ -t 0 ]; then
saved_settings=$(stty -g)
stty -icanon min 1 time 0 icrnl
fi
while true; do
code=$(dd bs=1 count=1 2> /dev/null | od -An -vto1 | tr -cd 0-7)
[ -n "$code" ] || break
case $code in
000 | 012) ret=0; break;; # can't store NUL in variable anyway
(*) REPLY=$REPLY$(printf "\\$code");;
esac
if expr " $REPLY" : ' .' > /dev/null; then
ret=0
break
fi
done
if [ -t 0 ]; then
stty "$saved_settings"
fi
return "$ret"
}
전체 문자를 읽은 후에만 반환된다는 점에 유의하세요. 잘못된 인코딩(로케일 인코딩과 다름)을 입력한 경우, 예를 들어 터미널이 é
iso8859-1(0xe9) 인코딩을 전송하고 UTF-8(0xc3 0xa9)을 예상하는 경우 임의의 숫자 내용을 입력할 수 있지만 é
함수는 반품. bash
은 read -n1
두 번째 0xe9를 반환하고 둘 다 변수에 저장하는데 이는 약간 더 나은 동작입니다.
(스크립트를 종료하는 대신, , ...에서도 작동) 또는 / on (흐름 제어 대신) ^C
에서 문자를 읽으려는 경우 줄 에 a를 추가 할 수 있습니다. 에서도 이 작업을 수행 하지 않는다는 점에 유의하세요 ( 다운되면 다시 시작될 수도 있음).Ctrl+C^Z
^\
^S
^Q
Ctrl+S/Q-isig -ixon
stty
bash
read -n1
isig
스크립트가 종료되면(예를 들어 누르는 경우) tty 설정이 복원되지 않습니다 Ctrl+C. 을 추가할 수 있지만 이로 인해 스크립트의 다른 설정이 trap
재정의될 수 있습니다 .trap
zsh
대신 bash
where read -k
(before ksh93
또는 bash
's )를 사용할 수도 있습니다 read -n/-N
. 이는 터미널에서 문자를 읽고 ^D
직접 처리하고(문자가 입력되면 0이 아닌 값을 반환함) 개행 문자를 특별히 처리하지 않습니다.
if read -k k; then
printf '1 character entered: %q\n' $k
fi
답변2
다음으로 변경 f()
하세요 .%s
%q
f() { read -rn 1 -p "Enter a character: " char && \
printf "\nYou entered '%q'\n" "$char"; }
f;f
사용자가 입력하면 출력새로운 팀, 그 다음에'Ctrl-D':
Enter a character:
You entered ''''
Enter a character: ^D
You entered '$'\004''
`man printf에서:
%q ARGUMENT is printed in a format that can be reused as shell input,
escaping non-printable characters with the proposed POSIX $'' syntax.
답변3
실제로 read -rn1
Bash에서 실행하고 을 클릭 하면 ^D
EOF 조건이 아닌 리터럴 제어 문자로 처리됩니다. 제어 문자는 인쇄 시 보이지 않으므로 와 함께 표시되지 않습니다 printf "'%s'"
. 출력을 비슷한 것에 연결하면 이미 언급된 다른 답변 od -c
과 같이 표시됩니다 .printf "%q"
실제로 입력이 없으면 결과가 다릅니다. 다음을 사용하더라도 여기서는 비어 있습니다 printf "%q"
.
$ f() { read -rn 1 x ; printf "%q\n" "$x"; }
$ printf "" | f
''
여기서 개행 문자가 반환되지 않는 데에는 두 가지 이유가 있습니다 read
. 첫째, 읽기를 위한 기본 줄 구분 기호이므로 출력으로 반환됩니다. 둘째, 이는 기본값의 일부이기도 하며 IFS
의 read
일부인 경우 선행 및 후행 공백을 제거합니다 IFS
.
read -d
따라서 기본 구분 기호를 변경 해야 합니다 .그리고분명한 IFS
:
$ g() { IFS= read -rn 1 -d '' x ; printf "%q\n" "$x"; }
$ printf "\n" | g
$'\n'
read -d ""
구분 기호를 효과적으로 NUL 바이트로 만듭니다. 이는 여전히 입력 없음과 NUL 바이트 입력 간의 차이를 알 수 없음을 의미합니다.
$ printf "" | g
''
$ printf "\000" | g
''
입력이 없더라도 read
false가 반환되므로 $?
이를 감지할 수 있는지 확인할 수 있습니다.
답변4
read -r var
status=$?
echo "\$var='$var':\$?=$status"
개행 및 Ctrl-D 케이스는 상태 변수로 구별됩니다.
줄 바꿈 문자인 경우 상태는 true(0)이고, Ctrl-D를 누르면 상태는 false(1)입니다.