일반적인 문제
파이프 체인의 중간에 있더라도 사용자와 상호 작용하는 스크립트를 작성하고 싶습니다.
구체적인 예
구체적으로, 그것은 취 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