일반적인 질문들
파이프라인 체인 중간에 있더라도 사용자와 상호 작용하는 스크립트를 작성하고 싶습니다.
구체적인 예
file
구체적으로 OR을 사용하여 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
grep 입력에서 선택 항목을 읽으려고 시도합니다.
내 방법
나는 다음 과 같은 -u
플래그를 사용해 보았습니다.read
exec 4< /proc/$PPID/fd/0
exec 4> /proc/$PPID/fd/1
nl $INPUT >4
read -u4 -p"Select options: "
그러나 이것은 내가 기대했던 효과를 가져오지 못했습니다.
묻다:실제 사용자 상호 작용을 얻는 방법은 무엇입니까?
답변1
사용이 /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: "
답변2
나는 작은 함수를 작성했습니다. 귀하가 요청한 파이프 연결 질문에 대답하지는 않지만 문제는 해결될 것입니다.
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
. 쉘 글롭을 사용하여 읽어야 할 파일을 지정하는 경우 글롭 순서의 첫 번째 항목부터 시작하여 마지막 일치 항목으로 끝나는 모든 파일의 모든 일치 항목을 반환합니다.
grep
출력을 nl
which 에 전달하고 각 행에 번호를 매기고 해당 출력을 tee
which 에 복사하여 to stdout
및 에 전달합니다 /dev/tty
. 이는 파이프의 출력이 작업하는 동안 함수의 인수 배열(줄로 분할) \n
과 터미널 모두에 인쇄된다는 것을 의미합니다.
다음으로 함수는 이전 작업에서 최대 5번까지 결과가 1개 이상 있는지 _in()
선택을 시도합니다 . read
선택 항목에는 공백으로 구분된 숫자 또는 공백으로 구분된 숫자 범위만 포함될 수 있습니다 -
. 다른 것이 있다면read
(빈 줄 포함)다시 시도하지만 이전과 마찬가지로 최대 5번만 시도할 수 있습니다.
마지막으로 _out()
함수는 사용자의 선택을 구문 분석하고 그 안의 범위를 확장합니다. 결과를 각각의 형식으로 인쇄하므로 "${[num]}"
arg 배열에 저장된 행의 값과 일치합니다. inf()
이 출력은 eval
args로 편집 되므로 printf
사용자가 선택한 줄만 인쇄됩니다.
이는 read
터미널에서 명시적으로 제공되며 menu 만 인쇄하므로 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