POSIX 쉘과 awk가 입력 문자를 한 줄씩 읽지 않고 문자별로 읽도록 하려면 어떻게 해야 합니까?
로마자에서 가나로의 음역 응용 프로그램을 만들고 있는데 awk에 대한 입력을 문자별로 즉시 처리하고 싶습니다.
awk가 문자를 처리하기 전에 return이나 enter를 누르지 않고 이 작업을 수행하는 올바른 방법을 모르겠습니다.
답변1
쉘 스크립트에서 이 도구를 사용하여 TTY 상태를 조작할 수 있습니다 stty
.
먼저 stty -g
현재 상태를 나타내는 문자열을 생성합니다. 다른 작업을 수행하기 전에 이 출력을 캡처하여 어딘가에 저장하십시오. 나중에 이 문자열을 stty
TTY 설정을 복원 하는 유일한 인수로 전달할 수 있습니다 . 문자열에는 따옴표가 필요하지 않습니다. POSIX 표준에서는 stty -g
쉘 스크립트에서 따옴표로 묶을 필요가 없는 표현을 생성해야 합니다.
stty raw
한 번에 한 문자씩 입력할 수 있는 원시 모드로 들어가는 방법입니다.
savetty=$(stty -g)
stty raw
...
stty $savetty
스크립트가 어딘가에서 종료되거나 중단된 경우에도 설정을 복원하는 trap
핸들러를 설정하려면 이 명령을 사용하는 것이 좋습니다.tty
stty
이제 이 댄스를 Awk 코드로 래핑한다고 가정해 보겠습니다 . 일반성을 잃지 않고 Awk 외부에서 이 작업을 수행해 보겠습니다. awk가 한 번에 한 문자씩 읽도록 하려면 어떻게 해야 합니까?
awk는 암시적 스캔 전략이나 연산자를 사용하여 행을 읽을 수만 있습니다 getline
. getchar
아니요 아, 하지만 그 줄은 실제로는기록. GNU Awk에는 POSIX 표준의 일부가 아닌 두 가지 도구가 있습니다:
변수
RS
에는 여러 문자가 포함될 수 있으며, 이 경우 정규식입니다.이
RT
변수는 레코드 종결자와 일치하는 텍스트 조각을 보유합니다.
보다:
$ awk 'BEGIN { RS = "(.)" } { print NF, RT }'
How now brown cow.
0 H
0 o
0 w
0
0 n
0 o
0 w
0
0 b
0 r
0 o
0 w
0 n
0
0 c
0 o
0 w
0 .
정규식 (.)
(모든 문자와 일치)을 레코드 구분 기호로 사용하면 필드가 전혀 포함되지 않은 빈 레코드를 얻게 되며 빈 레코드를 종료하는 문자는 RT
GNU Awk에서 사용할 수 있습니다.
불행히도 이것은 완전히 작동하지 않습니다. 이를 완전한 프로그램에 통합하면 다음과 같습니다.
#!/bin/sh
trap 'stty $ttysave' EXIT INT TERM
ttysave=$(stty -g)
stty raw -echo
awk 'BEGIN { RS = "(.)" }
RT ~ /q/ { exit }
{ printf("[%s]", RT) }'
이는 이전 문자를 읽는 Gawk의 레코드 구분 정규식 기계에 문제가 있음을 보여줍니다. 예를 들어 를 입력하여 즉시 종료하려면 q
이것 q
만으로는 충분하지 않습니다. 현재 레코드를 구분할 수 있고 RT
로 설정할 수 있더라도 Gawk는 레코드를 전달하기 전에 문자를 읽기 위해 TTY에서 다른 레코드를 q
호출합니다 .read
for
따라서 우리는 또는 while
루프를 반복하고 dd
유틸리티를 호출하는 것과 같은 정말 보기 흉한 방법에 의존해야 합니다 .
#!/bin/sh
trap 'stty $ttysave' EXIT INT TERM
ttysave=$(stty -g)
stty raw -echo
awk 'BEGIN { cmd = "dd bs=1 count=1 2> /dev/null"
for (;;)
{ cmd | getline ch
close(cmd)
if (ch == "q")
exit
printf("[%s]", ch) } }'