나는 다음과 같은 "메타 명령"xyz를 찾고 있습니다.
(echo "foo"; echo "bar") | xyz rev
다음을 반환합니다:
foo oof
bar rab
임시 파일을 피하고 싶습니다. 즉, 다음보다 더 깔끔한 솔루션을 찾고 있습니다.
tempfile=$(mktemp)
cat > $tempfile
cat $tempfile | rev | paste $tempfile -
(물론 일반적인 솔루션을 원합니다. , 뿐만 아니라 모든 명령에 대해 rev
명령이 각 입력 줄에 대해 한 줄을 출력한다고 가정할 수 있습니다.)
Zsh 솔루션도 허용됩니다.
답변1
stdio 버퍼링이 작동하는 방식으로 인해 대부분의 경우 많은 문제가 발생합니다. Linux에 대한 해결 방법은 이 프로그램을 사용 stdbuf
하고 coproc을 통해 명령을 실행하여 출력의 인터리빙을 명시적으로 제어하는 것입니다.
다음에서는 명령이 각 입력 줄 다음에 한 줄을 출력한다고 가정합니다.
#!/bin/bash
coproc stdbuf -i0 -o0 "$@"
IFS=
while read -r in ; do
printf "%s " "$in"
printf "%s\n" "$in" >&${COPROC[1]}
read -r out <&${COPROC[0]}
printf "%s\n" "$out"
done
OP에는 프로그램의 각 라인에 대한 입력만 필요하므로 보다 일반적인 솔루션이 필요한 경우마지막으로즉시 출력하지 않고 한 줄씩 출력하려면 좀 더 정교한 접근 방식이 필요합니다. 표준 입력과 보조 프로세스에서 데이터를 읽으려고 시도하는 이벤트 루프를 만들고 read -t 0
둘 다 동일한 "라인 번호"를 가지고 있으면 출력하고, 그렇지 않으면 라인을 저장합니다. CPU를 100% 사용하지 않으려면 이벤트 루프의 어떤 라운드에서도 준비가 되지 않은 경우 이벤트 루프를 다시 실행하기 전에 약간의 지연을 도입하세요. 프로세스가 버퍼링이 필요한 부분 라인을 출력하는 경우 추가적인 문제가 발생합니다.
이에 대한보다 일반적인 솔루션이 필요한 경우 다음을 사용합니다.예상되는이미 여러 입력 스트림에서 패턴 일치를 처리하기 위한 우수한 지원을 제공하기 때문입니다. 그러나 이것은 bash/zsh 솔루션이 아닙니다.
답변2
쉘 함수. 이는 <<<
zsh 및 bash를 포함하여 이 문자열을 지원하는 모든 셸에서 작동합니다.
xyz() {
while read line
do
printf "%s " "$line"
"$@" <<<"$line"
done
}
$ (echo "foo"; echo "bar") | xyz rev
foo oof
bar rab
답변3
이 특별한 경우에는 다음을 사용합니다 perl
.
printf '%s\n' foo bar | perl -Mopen=locale -lpe '$_ .= " " . reverse$_'
foo oof
bar rab
이를 확장하여 문자소 클러스터를 사용할 수 있습니다.
$ printf '%s\n' $'complique\u301' |
perl -Mopen=locale -lpe '$_ .= " " . join "", reverse/\X/g'
compliqué éuqilpmoc
또는 zsh
:
while IFS= read -r line; do
print -r -- $line ${(j::)${(s::Oa)line}}
done
하지만while read
텍스트를 처리하기 위해 루프를 사용하는 것을 피하겠습니다., 심지어 zsh
(이 특별한 경우에도 내장 명령만 사용되기 때문에 나쁘지 않습니다).
일반적으로 임시 파일을 사용하는 것이 가장 좋은 방법일 것입니다. 를 사용하면 zsh
다음을 통해 이 작업을 수행할 수 있습니다.
(){paste -d ' ' $1 <(rev <$1)} =(print -l foo bar)
( =(...)
임시 파일 생성 및 정리를 담당합니다).
일반적인 경우 tee
파이프와 어떤 형태의 ing으로 교체하면 교착 상태가 발생합니다. 교착 상태 상황에 대한 몇 가지 방법 및 세부 정보는 다음과 유사한 질문을 참조하세요.
답변4
네임드 파이프가 구출하러 옵니다!
xyz() {
pipe="/tmp/$$pipe"
mkfifo "$pipe"
tee "$pipe" | "$@" | paste "$pipe" -
rm "$pipe"
}
예제 명령의 결과:
$ (echo "foo"; echo "bar") | xyz rev
foo oof
bar rab
위의 기능은 설명하는 것과 정확히 일치합니다.