유닉스 ( bash
, ksh
, zsh
)
. DF의 대답은 포함 씨앗 을 기반으로 답변 tee
및 출력 프로세스 대체를
( >(...)
) 그 수도 있고 수도없는 일, 당신의 요구 사항에 따라 :
프로세스 대체는 (대부분) ( 예를 들어 Ubuntu에서 작동하는 ) POSIX 기능 전용 셸이 지원 하지 않는 비표준 기능입니다 . 셸 스크립트 대상 은이 스크립트에 의존 해서는 안됩니다 .dash
/bin/sh
/bin/sh
echo 123 | tee >(tr 1 a) >(tr 1 b) >/dev/null
이 접근 방식 의 함정 은 다음과 같습니다.
내부 >(...)
에서 시작된 명령 은 원래 셸과 분리되어 있으며 언제 완료되는지 쉽게 확인할 수 없습니다. 은 tee
모든 것을 작성 후 완료되지만, 치환 과정은 여전히 커널 및 파일 I / O의 다양한 버퍼의 데이터를 소모한다, 플러스 어떤 시간 데이터의 내부 처리에 의해 수행된다. 외부 쉘이 하위 프로세스에서 생성 된 모든 것에 계속 의존하면 경쟁 조건이 발생할 수 있습니다.
zsh
유일한 셸 않는 프로세스가 끝까지 출력 프로세스 대체 실행을위한 기본 대기에 의해 , 를 제외하고 이 경우 표준 에러 (하나에 리디렉션됩니다 2> >(...)
).
ksh
(적어도 버전 현재 93u+
) wait
출력 프로세스 대체 생성 프로세스가 완료 될 때까지 기다릴 인수없는 사용을 허용합니다 . 그러나
대기중인 백그라운드 작업 을 기다리는 결과를 초래할 수있는 대화식 세션에서도 유의하십시오 .
bash v4.4+
를 사용하여 가장 최근에 실행 된 출력 프로세스 대체를 기다릴 수 wait $!
있지만 인수없는 기능 wait
은 작동 하지 않으므로 여러 출력 프로세스 대체가 있는 명령에 적합 하지 않습니다 .
그러나 bash
및 ksh
수 강제 기다려야 에 명령을 파이프하여 | cat
이것이 실행 명령한다,하지만 노트 서브 쉘을 . 주의 사항 :
ksh
(현재 ksh 93u+
) stderr 을 출력 프로세스 대체로 보내는 것을 지원하지 않습니다 ( 2> >(...)
); 그러한 시도는 조용히 무시 됩니다.
기본적 으로 (훨씬 더 일반적인) stdout 출력 프로세스 대체 와 zsh
(권장 할 만하게) 동기식 이지만 기술 조차도 stderr 출력 프로세스 대체 ( ) 와 동기식으로 만들 수 없습니다 .| cat
2> >(...)
그러나 동기 실행 을 보장하더라도 예기치 않게 인터리브 출력 문제가 남아 있습니다.
bash
또는 ksh
에서 실행될 때 다음 명령 은 문제가있는 동작을 보여줍니다 ( 두 증상을 모두 보려면 여러 번 실행해야 할 수 있음 ). AFTER
일반적으로 는 출력 대체에서 출력 되기 전에 인쇄 되며 후자의 출력은 예측할 수없이 인터리브 될 수 있습니다.
printf 'line %s\n' {1..30} | tee >(cat -n) >(cat -n) >/dev/null; echo AFTER
간단히 말해서 :
특정 명령 별 출력 시퀀스 보장 :
- 그것도 지원
bash
하지도 ksh
않습니다 zsh
.
동기 실행 :
- stderr 소스 출력 프로세스 대체를 제외하고는 가능합니다.
- 에서
zsh
, 그들은있어 변함없이 비동기.
- 에서
ksh
, 그들은 전혀 작동하지 않습니다 .
이러한 제한을 감수 할 수 있다면 출력 프로세스 대체를 사용하는 것이 실행 가능한 옵션입니다 (예 : 모두가 별도의 출력 파일에 기록하는 경우).
참고 tzot의 훨씬 더의 복잡하지만, 잠재적으로 POSIX 호환 솔루션은 또한 예측할 수없는 출력 동작을 보여 ; 그러나를 사용 wait
하면 모든 백그라운드 프로세스가 완료 될 때까지 후속 명령이 실행을 시작하지 않도록 할 수 있습니다. 보다 강력한 동기식 직렬화 된 출력 구현
은 하단 을 참조하십시오 .
예측 가능한 출력 동작을 가진 유일한 간단한 bash
솔루션 은 다음과 같습니다. 그러나 쉘 루프는 본질적으로 느리기 때문에 큰 입력 세트 에서는 엄청나게 느 립니다.
또한 이것은 대상 명령의 출력 행을 대체 합니다 .
while IFS= read -r line; do
tr 1 a <<<"$line"
tr 1 b <<<"$line"
done < <(echo '123')
Unix (GNU 병렬 사용)
GNU를parallel
설치 하면 병렬 실행 을 추가로 허용하는 직렬화 된 (명령 별) 출력 으로 강력한 솔루션 을 사용할 수 있습니다 .
$ echo '123' | parallel --pipe --tee {} ::: 'tr 1 a' 'tr 1 b'
a23
b23
parallel
기본적으로 다른 명령의 출력이 인터리브되지 않도록합니다 (이 동작은 수정할 수 있습니다-참조 man parallel
).
참고 : 일부 Linux 배포판 에는 위의 명령과 함께 작동하지 않는 다른 parallel
유틸리티 가 함께 제공됩니다. parallel --version
어떤 것을 가지고 있는지 결정하는 데 사용하십시오 .
윈도우
Jay Bazuzi의 유용한 답변 은 PowerShell 에서 수행하는 방법을 보여줍니다 . 즉, 그의 대답은 bash
위 의 루핑 대답 과 유사 하며 큰 입력 세트에서는 엄청나게 느리고 대상 명령의 출력 라인 도 번갈아 나타납니다 .
bash
기반이지만 동기 실행 및 출력 직렬화 기능이있는 이식 가능한 Unix 솔루션
다음은 추가로 제공하는 tzot의 답변에 제시된 접근 방식의 간단하지만 합리적으로 강력한 구현입니다 .
그것이 있기 때문에 엄격하게 POSIX를 준수하면서, bash
스크립트, 그것을해야한다 있는 모든 유닉스 플랫폼에 이식bash
.
참고 : 이 Gist 에서 MIT 라이선스에 따라 출시 된보다 완전한 구현을 찾을 수 있습니다 .
아래 코드를 script로 저장하고 fanout
실행 가능하게 만들고 int를 넣으면 PATH
질문의 명령이 다음과 같이 작동합니다.
$ echo 123 | fanout 'tr 1 a' 'tr 1 b'
a23
b23
fanout
스크립트 소스 코드 :
#!/usr/bin/env bash
aCmds=( "$@" )
tmpDir="${TMPDIR:-/tmp}/$kTHIS_NAME-$$-$(date +%s)-$RANDOM"
mkdir "$tmpDir" || exit
trap 'rm -rf "$tmpDir"' EXIT
maxNdx=$(( $# - 1 ))
fmtString="%0${#maxNdx}d"
aFifos=() aOutFiles=()
for (( i = 0; i <= maxNdx; ++i )); do
printf -v suffix "$fmtString" $i
aFifos[i]="$tmpDir/fifo-$suffix"
aOutFiles[i]="$tmpDir/out-$suffix"
done
mkfifo "${aFifos[@]}" || exit
for (( i = 0; i <= maxNdx; ++i )); do
fifo=${aFifos[i]}
outFile=${aOutFiles[i]}
cmd=${aCmds[i]}
printf '# %s\n' "$cmd" > "$outFile"
eval "$cmd" < "$fifo" >> "$outFile" &
done
tee "${aFifos[@]}" >/dev/null || exit
wait
cat "${aOutFiles[@]}"