- 출처 : 유닉스 리눅스 쉘스크립트 예제사전_한빛미디어
명령어: stty, case, dd
키워드: 키보드, 입력, 줄바꿈, Enter
사용처: 키보드에서 글자 하나가 입력되면Enter
입력 없이 처리를 속행하고 싶을 때
실행예제
$ ./getchar.sh
Type Your Answer [y/n] : y
Input : Yes
스크립트
#!/bin/sh
echo -n "Type Your Answer [y/n]: "
# 현재 터미널 설정을 셸 변수 tty_state에 백업하고
# 터미널을 raw 설정함
tty_state=$(stty -g) #--------------------- 1
stty raw #--------------------------------- 2
# 키보드에서 문자 하나 읽기
char=$(dd bs=1 count=1 2> /dev/null) #----- 3
# 터미널 설정을 원래대로 돌림
stty "$tty_state" #------------------------ 4
echo
# 입력된 문자에 따라 처리 분기
case "$char" in
[y/Y])
echo "Input: YES"
;;
[n/N])
echo "Input: NO"
;;
*)
echo "Input: What?"
;;
esac
해설
이 스크립트는 사용자에게 Yes/No를 물어서 입력된 문자로 Yes인지 No인지 판단해서 메시지를 표시합니다.
스크립트 실행 중에 계속 진행할지 여부를 확인하는 등 사용자에게 무언가를 입력받고 싶을 때 read 명령어를 써서 키 입력을 얻을 수 있는데 read 명령어는 반드시 줄바꿈이 필요합니다. 즉, 사용자는 문자를 입력한 다음 Enter
키를 눌러야만 합니다.
하지만 단순히 Yes/No를 묻는다면 Enter
키를 누르지 않고 y 또는 n을 누르면 처리를 진행하고 싶습니다.이럴 때는 터미널(단말) 상태를 설정하는 stty 명령어로 터미널을 raw 모드로 하면 키 버퍼 처리를 하지 않으므로 문자마다 처리할 수 있습니다.
이 예제에서 줄 바꿈이 없는 메시지를 echo -n으로 표시한 다음, 현재 터미널 설정을 stty -g 명령어 출력 결과를 가지고 저장합니다1
. stty -g 출력은 다음과 같으며 현재 터미널 설정이 저장됩니다.
-
현재 터미널 설정 표시
$ stty -g 2d00:5:bf:ca3b:3:1c:7f:15:4:0:1:0:11:13:1a:ff:12:f:17:16:ff:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0
이후 raw 모드로 할 때 현재 터미널 설정이 지워지므로 나중에 설정을 원래대로 돌릴 수 있도록 현재 설정을 일시적으로 셸 변수 tty_state에 저장합니다.
이어서 2
에서 stty raw 명령어로 터미널을 raw 모드로 전환합니다. 이러면 실제 키 입력값은 다음 dd 명령어에 직접 전달됩니다. 3
의 dd 명령어에서는 입출력 블록 크기를 1(bs=1), 입력에서 출력으로 복사하는 블록 수를 1(count=1)로 해서 입력된 문자를 셀 변수 char에 설정합니다.
또한, dd 명령어 실행 메시지는 필요 없으므로 /dev/null로 리다이렉트해서 버립니다. 이대로는 터미널이 raw 모드이므로 스크립트 실행 이전 상태로 되돌려야 합니다. 4
에서 stty 명령어를 써서 stty “$tty_state”로 지정하면 사용자 터미널 상태는 raw 모드를 벗어나서 처음에 저장한 터미널 상태로 돌아갑니다. 입력된 값은 셸 변수 char에 들어있으므로 5
에서 case 분기합니다. 입력된 문자가 y 또는 Y라면 Yes 처리, n 또는 N이라면 No 처리를 합니다.
Mac에서 echo 명령어
Mac에서 echo 명령어는 조금 성가십니다. Mac에서 echo 명령어는 명령행에서 직접 사용할 때는 줄바꾸지 않는 -n 옵션이 동작합니다. 하지만 셸 스크립트 내부에서 echo -n은 옵션으로 지정한 “-n”이 문자열로 그대로 출력됩니다.
이것은 echo 명령어가 셸의 내부 명령어로 실행되는 것이 원인입니다. 셸 스크립트에서 이용하는 명령어로는 ‘외부 명령어’와 ‘셸 내부 명령어’ 두 종류가 있습니다. 외부 명령어는 /bin/echo 처럼 실행 파일이 존재합니다. 한편, 셸 내부 명령어는 셸 자체 내부에 있는 명령어로 실행 파일이 존재하지 않습니다.
이 예제처럼 echo를 쓰면 셸 내부의 echo 명령어가 사용됩니다. Mac에서는 기본 로그인 셀이 bash이므로 명령행에서는 bash의 셸 내부 echo 명령어가 실행됩니다. 이것은 -n에 대응하므로 원하는 대로 줄바꿈이 없게 실행됩니다.
한편, 스크립트 내부에 echo를 쓰면 bash가 아니라 sh 셸 내부인 echo 명령어가 실행됩니다. Mac의 sh 셸 내부 echo 명령어는 옵션 -n에 대응하지 않으므로 지정해도 인수로 해석되어서 “-n”이 그대로 출력됩니다. 따라서 Mac에서 개행하지 않는 메시지를 echo로 출력하려면 다음처럼 외부 명령어 echo를 사용합니다.
/bin/echo -n "Type Toyr Answer [y/n] : "
또는 다음처럼 printf 명령어를 쓰는 방법도 있습니다. printf 명령어는 \n으로 지정하지 않으면 줄바꾸지 않으므로 그대로 출력됩니다.
printf "Type Your Answer [y/n] : "