errpipe
필터를 통해 실행되는 간단한 API가 포함된 유틸리티 스크립트를 작성하려고 합니다 . stderr
처음에는 bash의 프로세스 교체 기능을 사용하여 구현해 보았습니다.
#!/bin/bash
com="$1"
errpipe="$2"
$com 2> >(1>&2 $errpipe)
com
문제는 이것이 존재하지 않을 때 출력이 이상하게 보인다는 것입니다.
내가 들어가면
sh-3.2$ ./errpipe foo cat
알겠어요
sh-3.2$ ./errpipe foo cat
sh-3.2$ ./errpipe: line 6: foo: command not found
@
는 @
커서를 나타냅니다. 즉, 쉘 프롬프트가 너무 일찍 인쇄됩니다. 나는 이것이 메인 쉘 스크립트가 프로세스 교체 프로세스가 완료될 때까지 기다리지 않기 때문이라고 생각합니다. wait
스크립트 끝에를 추가해도 문제가 해결되지 않는 것 같습니다 .
나는 을 사용하는 솔루션 bash
이나 ksh
어쩌면 zsh
일부 미친 기능을 사용하는 awk
솔루션 에 열려 있습니다. 프로세스와 파일 설명자를 조작하기 위해 더 풍부한 API를 제공하는 C 또는 Perl과 같은 것을 사용하여 이들을 함께 연결하는 방법을 알고 있다고 생각하지만 다른 옵션이 없는 한 사용을 피하고 싶습니다.
"거의 작동하는" 솔루션 중 하나는 포크될 때 쉘이 변경되지 않는다는 사실을 활용하고 $$
오류 파이프가 완료되면 상위에 신호를 보내는 것입니다.
#!/bin/bash
com="$1"
errpipe="$2"
$com 2> >(1>&2 $errpipe; kill -SIGUSR1 $$)
while true; do
sleep 60
done
이렇게 하면 원래 문제가 해결되지만 a) 추악합니다. b) User defined signal 1: 30
SIGUSR1에 대한 신호 처리기가 있더라도 종료하기 전에 인쇄되며 c) SIGUSR을 부모에게 보내는 프로세스가 어떻게든 죽으면 영원히 순환됩니다. .
답변1
예, 함수가 어디에서 왔는지와 마찬가지로 프로세스 교체 내부의 프로세스는 기다리지 않습니다 bash
.ksh
일반적으로 다음과 같은 사람 에게는 괜찮습니다 <(...)
.
cmd1 <(cmd2)
쉘은 교체되는 파이프의 파일이 끝날 때까지 기다리고 cmd1
일반적 cmd1
으로 읽음으로써 대기 cmd2
하며 일반적으로 파일 끝은 cmd2
사망 시 발생합니다. 이는 여러 쉘(이 아닌) 이 bash
에서 기다리지 않는 이유이기도 합니다 .cmd2
cmd2 | cmd1
그러나 cmd1 >(cmd2)
일반적으로 의 경우에는 그렇지 않습니다. 일반적으로 그곳에서 대기 cmd2
하고 cmd1
일반적으로 나중에 종료되기 때문입니다.
zsh
거기에서 기다릴 것입니다 cmd2
(그러나 으로 쓰면 cmd1 > >(cmd2)
그렇지 않고 대신 {cmd1} > >(cmd2)
as를 사용하십시오)녹음된).
ksh
기본적으로 기다리지 않지만 내장 기능을 사용하여 기다릴 수 있습니다. wait
(또한 pid를 사용할 수 있게 해주지 $!
만 그렇게 하면 도움이 되지 않습니다 cmd1 >(cmd2) >(cmd3)
.)
rc
(구문 사용 ) , 를 사용하여 모든 백그라운드 프로세스의 pid를 얻을 수 있다는 점을 제외하면 cmd1 >{cmd2}
다른 것과 동일합니다 .ksh
$apids
es
(또한 유사함) in cmd1 >{cmd2}
wait이며, 또한 진행 중인 리디렉션을 기다리고 있습니다.cmd2
zsh
cmd2
<{cmd2}
bash
는 pid cmd2
(또는 더 정확하게는 서브셸의 pid, 왜냐하면 그것이 cmd2
마지막 명령이더라도 해당 서브셸의 하위 프로세스에서 실행되기 때문)를 에서 사용할 수 있도록 $!
하지만 사용자가 이를 기다리도록 허용하지는 않습니다.
꼭 필요한 경우 bash
다음 두 명령을 기다리는 명령을 사용하여 문제를 해결할 수 있습니다.
{ { cmd1 >(cmd2); } 3>&1 >&4 4>&- | cat; } 4>&1
이렇게 하면 fd3과 fd3이 cmd1
모두 cmd2
파이프에 열려 있게 됩니다. 다른 쪽 끝에서 파일이 끝날 때까지 기다리므로 일반적으로 cat
둘 다 발생하고 종료될 때만 cmd1
종료 됩니다 cmd2
. 쉘은 명령을 기다립니다 cat
. 모든 백그라운드 프로세스 종료를 캡처하는 네트워크로 생각할 수 있습니다. ( &
데몬으로 모두 종료하지 않는 한 백그라운드 자체의 , coprocs 또는 명령과 같이 백그라운드에서 시작된 다른 작업에 사용할 수 있습니다.) 일반적으로 파일 설명자를 수행합니다).
위에서 언급한 낭비된 서브쉘 프로세스로 인해 cmd2
fd 3이 닫혀도 여전히 작동합니다(명령은 일반적으로 이 작업을 수행하지 않지만 일부 명령은 유사 sudo
하거나 ssh
수행합니다). 향후 버전은 bash
결국 다른 쉘처럼 최적화될 수 있습니다. 그런 다음 다음과 같은 것이 필요합니다.
{ { cmd1 >(sudo cmd2; exit); } 3>&1 >&4 4>&- | cat; } 4>&1
명령 을 기다리는 fd 3이 있는 추가 쉘 프로세스가 아직 열려 있는지 확인하십시오 sudo
.
cat
아무것도 읽히지 않는다는 점에 유의하십시오 (프로세스가 fd 3에 쓰지 않기 때문입니다). 바로 동기화를 위한 것입니다. 시스템 호출 만 수행 read()
하고 궁극적으로는 아무것도 반환하지 않습니다.
cat
파이프라인 동기화를 위한 명령 대체를 사용하면 실제로 이 실행을 피할 수 있습니다 .
{ unused=$( { cmd1 >(cmd2); } 3>&1 >&4 4>&-); } 4>&1
이번에는 쉘이 fd 3의 반대쪽 끝에 cat
열려 있는 파이프에서 데이터를 읽습니다 . 변수 할당을 사용하므로 종료 상태를 에서 사용할 수 있습니다.cmd1
cmd2
cmd1
$?
또는 프로세스 대체를 수동으로 수행한 다음 시스템의 대체를 사용할 수도 있습니다. sh
이는 표준 쉘 구문이 됩니다.
{ cmd1 /dev/fd/3 3>&1 >&4 4>&- | cmd2 4>&-; } 4>&1
그러나 앞서 언급했듯이 모든 구현이 완료될 때까지 sh
기다리지는 않습니다 (그 반대의 경우보다 낫지만). 이 시점에서는 make 및 make 의 종료 상태를 각각 및에서 사용할 수 있지만 종료 상태가 포함됩니다 ( 마지막 구성 요소 이외의 파이프라인 구성 요소 오류를 보고할 수 있도록 일부 셸의 옵션 도 참조 ).cmd1
cmd2
$?
cmd2
bash
zsh
cmd1
${PIPESTATUS[0]}
$pipestatus[1]
pipefail
$?
yash
프로세스에도 비슷한 문제가 있습니다 .리디렉션특징. 거기에 cmd1 >(cmd2)
기록될 것입니다 cmd1 /dev/fd/3 3>(cmd2)
. 그러나 기다리지 않고 는 wait를 cmd2
사용할 수 없으며 해당 pid는 변수에서 사용할 수 없습니다. 와 동일한 해결 방법을 사용하게 됩니다 .wait
$!
bash