이는 zsh
및 에서 재현 가능합니다 bash
.
나를 더 혼란스럽게 만드는 것은 echo | ( xargs; : ) > >(cat)
멈추지 않습니다. 이는 zsh
및 에서도 재현 가능합니다 bash
.
xargs
제공되는 GNU를 사용하면 brew install findutils
중단되지 않습니다 echo | gxargs > >(cat)
.
실제로 나는 내 시스템 외에 이런 식으로 작동하는 다른 프로그램을 찾지 못했습니다 xargs
. xargs
파일 설명자에 문제가 있을 수 있다고 생각하여 어둠 속에서 다른 샷 으로 xargs
교체해 보았습니다.bash -c 'kill -9 $$'
bash -c 'exec 0<&- 1<&-'
나는 또한 ##mac
, #macosx
, ##linux
및 #bash
Freenode에 대한 도움을 구했지만 그곳에서는 무슨 일이 일어나고 있는지 아는 사람이 아무도 없는 것 같았습니다.Stack Overflow에도 질문했습니다.하지만 프로그래밍만으로는 충분하지 않았습니다.
> sw_vers | head -n 2
ProductName: Mac OS X
ProductVersion: 10.15.2
> zsh --version
zsh 5.7.1 (x86_64-apple-darwin19.0)
> bash --version | head -n 1
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin19)
> strings $(which xargs) | grep 'xargs.c'
$FreeBSD: src/usr.bin/xargs/xargs.c,v 1.57 2005/02/27 02:01:31 gad Exp $
> gxargs --version | head -n 1
xargs (GNU findutils) 4.7.0
답변1
흥미로운 키워드를 xargs
실행 하고 검색하여 내 시스템의 소스 코드를 찾을 수 있었습니다 . 나에게 붙어서 곧 약간 오래된 버전의 소스 코드를 발견했습니다 .strings $(which xargs)
PROJECT:shell_cmds-207.40.1
shell_cmds-203
Apple의 오픈 소스 사이트.
xargs
해당 패키지 의 버전을 으로 컴파일하고 gcc -g *.c
실행한 echo | ./a.out > >(cat)
후 디버거를 프로세스 lldb
에 연결했습니다 a.out
. 나는 그것이 waitpid
from xargs.c:610
(원천). 발췌:
while ((pid = waitpid(-1, &status, !waitall && curprocs < maxprocs ?
WNOHANG : 0)) > 0) {
는 복잡한 프로그램이기 때문에 xargs
동작을 재현할 수 있는 더 작은 C 프로그램을 만들고 싶었습니다. 여기있어:
// tiny.c
#include <sys/wait.h>
int main() {
int status;
waitpid(-1, &status, 0);
return 0;
}
을 사용하여 컴파일 gcc tiny.c -o tiny
하고 실행하는 echo | ./tiny > >(cat)
것은 xargs
. 실제로 이제는 훨씬 더 단순화할 수 있고 ./tiny > >(cat)
정지할 수도 있고 ( ./tiny; : ) > >(cat)
정지하지 않을 수도 있습니다.
여담: 이 작은 프로그램은 Linux에서 컴파일할 수 있으며 Linux에서 이 동작을 쉽게 재현할 수 있습니다.
전달 -1
하면 waitpid
대기하게 됩니다.모든 하위 프로세스. 그래서 질문이 생깁니다.tiny
에 하위 프로세스가 있는데 왜 ./tiny > >(cat)
그렇지 않습니까 ( ./tiny; : ) > >(cat)
?
bash
나는 의 소스 코드를 자세히 다루지는 않았지만 무슨 일이 일어나고 있는지에 대해 꽤 잘 교육받은 추측을 가지고 있습니다.
먼저 첫 번째 명령을 분석해 보겠습니다 ./tiny > >(cat)
. 먼저 bash
명명된 파이프를 만든 다음 자식 프로세스로 생성을 시작합니다 fork()-exec()
. 그런 다음 자체 이름을 동일한 이름의 파이프로 cat
설정합니다 . stdout
마침내 로 변신하라는 bash
부르심으로 생을 마감한다 . 이제 동일한 PID를 가지며 OS는 여전히 프로세스 를 하위 프로세스로 간주합니다.exec()
tiny
tiny
cat
중요한 것은 에서도 같은 일이 발생 ( ./tiny ) > >(cat)
하지만 exec()
bash(괄호로 서브셸 시작)에 들어간 다음 tiny
. 중요한 사실은 bash
실행할 명령이 하나만 있으면 시작될 때 실행되지 않고 fork()-exec()
즉시 exec()
실행된다는 것입니다.
이제 두 번째 명령을 분석해 보겠습니다 ( ./tiny; : ) > >(cat)
. 우리는 처음부터 같은 것을 얻습니다: 존재가 시작되는 fork()-exec()
것 cat
입니다. 그런 다음 bash
exec()
새 bash
인스턴스로 들어갑니다. 그런 다음 실행해야 할 두 개의 명령이 있음을 확인하고 존재하게 되며 fork()-exec()
, tiny
분기되었기 때문에 이 새 tiny
프로세스에는 하위 프로세스가 없으므로 cat
중단되지 않습니다. 그런 다음 bash
실행합니다 :
( :
는 특수 내장이므로 여기에는 exec가 없지만 내장되지 않은 것을 사용하면 여전히 tiny
포크가 발생하므로 여전히 정지 현상이 발생하지 않습니다).