파이프에서 스크립트를 사용할 때 사용자 입력을 읽는 방법 > myfile.tmp foo포함하는 경우 blabcbla foo abc bar quux xyzzy abc 그런

일반적인 문제

파이프 체인의 중간에 있더라도 사용자와 상호 작용하는 스크립트를 작성하고 싶습니다.

구체적인 예

구체적으로, 그것은 취 file하거나 stdin(행 번호), 디스플레이 라인을 입력하도록 요청하는 선택 또는 광고 번호, 다음에 대응하는 라인을 인쇄 stdout. 이 스크립트를 호출 해 봅시다 selector. 그런 다음 기본적으로 할 수 있기를 원합니다

grep abc foo | selector > myfile.tmp

foo포함하는 경우

blabcbla
foo abc bar
quux
xyzzy abc

그런 다음 옵션이 selector있는 터미널이 아닌 터미널에 표시됩니다 myfile.tmp!

1) blabcbla
2) foo abc bar
3) xyzzy abc
Select options:

그 후 내가 입력

2-3

그리고 결국

foo abc bar
xyzzy abc

의 내용으로 myfile.tmp.

선택기 스크립트가 설치되어 실행 중이며 기본적으로 입력 및 출력을 리디렉션하지 않으면 완벽하게 작동합니다. 그래서

selector foo

내가 원하는 것처럼 행동합니다. 그러나 위 예제와 같이 함께 파이핑 할 때 selector제시된 옵션을 인쇄 myfile.tmp하고 grepped 입력에서 선택 항목을 읽으려고합니다.

내 접근 방식

-u에서 read와 같이 의 플래그 를 사용하려고 했습니다.

exec 4< /proc/$PPID/fd/0
exec 4> /proc/$PPID/fd/1
nl $INPUT >4
read -u4 -p"Select options: "

그러나 이것은 내가 원하는 바를 수행하지 않습니다.

Q : 실제 사용자 상호 작용은 어떻게 받습니까?



답변

/proc/$PPID/fd/0신뢰할 수없는 사용 : selector프로세스 의 부모가 터미널을 입력으로 사용하지 못할 수 있습니다.

항상 현재 프로세스의 터미널을 참조 하는 표준 경로/dev/tty있습니다.

nl "$INPUT" >/dev/tty
read -p"Select options: " </dev/tty

또는

exec </dev/tty >/dev/tty
nl "$INPUT"
read -p"Select options: "


답변

나는 작은 기능을 작성했다 : 그것은 당신이 파이프 체인에 요청한 것에 대해서는 대답하지 않지만 문제를 해결할 것입니다.

inf() ( [ -n "$ZSH_VERSION" ] && emulate sh
        unset n i c; set -f; tab='      ' IFS='
';      _in()   until [ "$((i+=1))" -gt 5 ] && exit 1
                printf '\nSelect: '
                read -r c && [ -n "${c##*[!- 0-9]*}" ]
                do echo "Invalid selection."
                done
        _out()  for n do i=; [ "$n" = . ]  &&
                printf '"${%d#*$tab}" ' $c ||
                until c="${c#*.} ${i:=${n%%-*}}"
                [ "$((i+=1))" -gt "${n#*-}" ]
                do :; done; done
set -- $(grep "$@"|nl -w1 -s "$tab"|tee /dev/tty)
i=$((($#<1)*5)); _in </dev/tty >/dev/tty
eval "printf '%s\n' $(c=$c\ . IFS=\ ;_out $c)"
)

이 함수는 사용자가 즉시 제공하는 모든 인수를 뒤집습니다 grep. 쉘 glob을 사용하여 읽을 파일을 지정하면 glob 순서의 첫 번째로 시작하여 마지막 일치로 끝나는 모든 파일의 모든 일치 항목을 리턴합니다.

grep그 출력 전달 nl각 라인이되는 그 출력 통과하는 숫자 tee를 모두 출력하는 중복 stdout하고이 /dev/tty. 이는 파이프 라인의 출력이 함수의 인수 배열 \n과 함께 ewline 으로 분할되고 작동 할 때 터미널로 동시에 인쇄 됨을 의미합니다.

다음으로 _in()함수 read는 이전 조치의 결과가 최대 5 회 이상인 경우 선택을 시도합니다 . 선택은 공백으로 구분 된 숫자 만 또는 숫자로 구분 된 숫자 범위로 구성 될 수 있습니다 -. read 빈 줄을 포함하여 다른 것이 있으면 다시 시도하지만 이전과 마찬가지로 최대 5 번 시도합니다.

마지막으로 _out()함수는 사용자의 선택을 구문 분석하고 그 범위를 확장합니다. 결과 "${[num]}"를 각각 의 형식으로 인쇄하여 inf()arg 배열에 저장된 행의 값과 일치시킵니다 . 이 출력은 eval인수로 printf사용되므로 사용자가 선택한 행만 인쇄합니다.

그것은 분명히 read터미널에서 왔으며 Select:메뉴 만 인쇄 stderr하므로 파이프 라인 친화적입니다. 예를 들어 다음이 작동합니다.

seq 100 |inf 3|grep 8
1       3
2       13
3       23
4       30
5       31
6       32
7       33
8       34
9       35
10      36
11      37
12      38
13      39
14      43
15      53
16      63
17      73
18      83
19      93

Select: 6 9 12-18
38
83

그러나 원하는 옵션 grep과 원하는 수의 파일 이름을 사용할 수 있습니다. 즉, $IFS빈 줄을 검색하는 경우 구문 분석 입력의 부작용으로 한 가지 유형을 제외한 모든 유형을 사용할 수 있습니다 . 그러나 번호가 매겨진 빈 줄 목록에서 누가 선택하고 싶습니까?

마지막으로 이것은 숫자 사용자 입력을 함수의 인수 배열에 저장된 숫자 위치 매개 변수로 직접 변환하여 작동하므로 사용자가 선택한 횟수, 사용자가 선택한 횟수 및 사용자가 선택한 순서대로 출력이 출력됩니다 그것.

예를 들면 다음과 같습니다.

seq 1000 | inf 00\$

1       100
2       200
3       300
4       400
5       500
6       600
7       700
8       800
9       900
10      1000

Select: 4-8 1 1 3-6
400
500
600
700
800
100
100
300
400
500
600


답변