프로세스 교체 출력이 고장났습니다.

프로세스 교체 출력이 고장났습니다.

이것

echo one; echo two > >(cat); echo three; 

명령이 예기치 않은 출력을 제공합니다.

나는 이것을 읽었다:Bash에서 프로세스 교체를 구현하는 방법은 무엇입니까?프로세스 교체에 관한 인터넷의 다른 많은 기사가 있지만 왜 이렇게 작동하는지 이해하지 못합니다.

예상 출력:

one
two
three

실제 출력:

prompt$ echo one; echo two > >(cat); echo three;
one
three
prompt$ two

또한 내 관점에서는 이 두 명령이 동일해야 하지만 그렇지 않습니다.

##### first command - the pipe is used.
prompt$ seq 1 5 | cat
1
2
3
4
5
##### second command - the process substitution and redirection are used.
prompt$ seq 1 5 > >(cat)
prompt$ 1
2
3
4
5

왜 나는 그들이 동일해야 한다고 생각합니까? 둘 다 seq익명 파이프를 통해 출력을 입력에 연결하기 때문에 -catWikipedia, 프로세스 교체.

질문:왜 이런가요? 내 실수는 어디에 있습니까? 포괄적인 답변이 필요합니다(그리고 bash그것이 뒤에서 어떻게 작동하는지 에 대한 설명 ).

답변1

예, bash(이 기능의 출처)와 마찬가지로 ksh프로세스 교체 내부에서 프로세스를 기다리지 않습니다(스크립트에서 다음 명령을 실행하기 전).

일반적으로 다음과 같은 사람 에게는 괜찮습니다 <(...).

cmd1 <(cmd2)

쉘은 교체되는 파이프의 파일이 끝날 때까지 기다리고 cmd1일반적 cmd1으로 읽음으로써 대기 cmd2하며 일반적으로 파일 끝은 cmd2사망 시 발생합니다. 이는 여러 쉘(이 아닌) 이 bash에서 기다리지 않는 이유이기도 합니다 .cmd2cmd2 | cmd1

그러나 cmd1 >(cmd2)일반적으로 의 경우에는 그렇지 않습니다. 일반적으로 그곳에서 대기 cmd2하고 cmd1일반적으로 나중에 종료되기 때문입니다.

이것은 수정되었습니다. zsh기다리세요 cmd2(하지만 as를 작성 cmd1 > >(cmd2)하고 cmd1내장 기능이 아닌 경우 {cmd1} > >(cmd2)대신 as를 사용하세요)녹음된).

ksh기본적으로 기다리지 않지만 내장 기능을 사용하여 기다릴 수 있습니다. wait(또한 pid를 사용할 수 있게 해주지 $!만 그렇게 하면 도움이 되지 않습니다 cmd1 >(cmd2) >(cmd3).)

rc(구문 사용 ) , 를 사용하여 모든 백그라운드 프로세스의 pid를 얻을 수 있다는 점을 제외하면 cmd1 >{cmd2}다른 것과 동일합니다 .ksh$apids

es(또한 유사함) in cmd1 >{cmd2}wait이며, 또한 진행 중인 리디렉션을 기다리고 있습니다.cmd2zshcmd2<{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 또는 명령과 같이 백그라운드에서 시작된 다른 작업에 사용할 수 있습니다.) 일반적으로 파일 설명자를 수행합니다).

위에서 언급한 낭비된 서브쉘 프로세스로 인해 cmd2fd 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열려 있는 파이프에서 데이터를 읽습니다 . 변수 할당을 사용하므로 종료 상태를 에서 사용할 수 있습니다.cmd1cmd2cmd1$?

또는 프로세스 대체를 수동으로 수행한 다음 시스템의 대체를 사용할 수도 있습니다. sh이는 표준 쉘 구문이 됩니다.

{ cmd1 /dev/fd/3 3>&1 >&4 4>&- | cmd2 4>&-; } 4>&1

그러나 앞서 언급했듯이 모든 구현이 완료될 때까지 sh기다리지는 않습니다 (그 반대의 경우보다 낫지만). 이 시점에서는 make 및 make 의 종료 상태를 각각 및에서 사용할 수 있지만 종료 상태가 포함됩니다 ( 마지막 구성 요소 이외의 파이프라인 구성 요소 오류를 보고할 수 있도록 일부 셸의 옵션 도 참조 ).cmd1cmd2$?cmd2bashzshcmd1${PIPESTATUS[0]}$pipestatus[1]pipefail$?

yash프로세스에도 비슷한 문제가 있습니다 .리디렉션특징. 거기에 cmd1 >(cmd2)기록될 것입니다 cmd1 /dev/fd/3 3>(cmd2). 그러나 기다리지 않고 는 wait를 cmd2사용할 수 없으며 해당 pid는 변수에서 사용할 수 없습니다. 와 동일한 해결 방법을 사용하게 됩니다 .wait$!bash

답변2

두 번째 명령을 다른 명령으로 파이프하여 cat입력 파이프가 닫힐 때까지 기다릴 수 있습니다. 전임자:

prompt$ echo one; echo two > >(cat) | cat; echo three;
one
two
three
prompt$

짧고 간단합니다.

=========

간단해 보일 수도 있지만 그 이면에는 많은 일이 벌어지고 있습니다. 작동 방식에 관심이 없으면 나머지 답변을 무시할 수 있습니다.

이를 갖게 되면 echo two > >(cat); echo three대화 >(cat)형 셸에서 분기되어 독립적으로 실행됩니다 echo two. 따라서 echo two완료한 다음 echo three실행하십시오. 그러나 >(cat)완료되기 전에 수행하십시오. 예상치 못한 시간(몇 밀리초 후)에서 데이터를 가져올 때 bash터미널로 돌아가기 위해 줄바꿈을 눌러야 하는 프롬프트 같은 것을 제공합니다(예: 다른 사용자가 메시지를 보내는 경우).>(cat)mesg

그러나 주어진 경우 echo two > >(cat) | cat; echo three두 개의 하위 쉘이 생성됩니다(기호 문서에 따라 |).

A라는 하위 쉘은 에 대한 echo two > >(cat)것이고 B라는 하위 쉘은 에 대한 것입니다 cat. A는 자동으로 B에 연결됩니다(A의 표준 출력은 B의 표준 입력입니다). echo two그런 다음 >(cat)실행이 시작됩니다. >(cat)stdout은 B의 stdin과 동일한 A의 stdout으로 설정됩니다. 완료되면 echo twoA가 종료되고 표준 출력이 닫힙니다. 그러나 >(cat)B의 stdin에 대한 참조는 남아 있습니다. 두 번째 catstdin은 B의 stdin을 보유하며 catEOF가 보일 때까지 종료되지 않습니다. EOF는 아무도 더 이상 쓰기 모드로 파일을 열지 않는 경우에만 제공되므로 >(cat)stdout은 두 번째 파일을 차단합니다 cat. B는 아직도 그 순간을 기다리고 있다 cat. echo two종료할 때 >(cat)결국 EOF를 얻게 되므로 >(cat)버퍼를 플러시하고 종료합니다. 더 이상 B's/second cat의 표준 입력을 보유하는 사람이 없으므로 두 번째 것은 catEOF를 읽습니다(B는 표준 입력을 전혀 읽지 않으므로 상관하지 않습니다). 이 EOF는 두 번째 항목이 cat버퍼를 플러시하고 stdout을 닫은 다음 종료하도록 합니다. 그런 다음 B는 cat이미 종료되었고 B는 기다리고 있었기 때문에 종료됩니다 cat.

>(cat)bash는 !의 하위 쉘 도 생성한다는 점에 유의해야 합니다 . 이 때문에 당신은 보게 될 것입니다

echo two > >(sleep 5) | cat; echo three

echo threeB의 표준 입력이 저장되지 않더라도 sleep 5여전히 실행되기 전에 5초를 기다린다. 이는 C가 생성된 숨겨진 하위 쉘을 >(sleep 5)기다리고 있고 sleepC가 B의 표준 입력을 보유하고 있기 때문입니다. 당신은 방법을 볼 수 있습니다

echo two > >(exec sleep 5) | cat; echo three

sleep그러나 B의 표준 입력은 저장되지 않고 B의 표준 입력을 저장하는 고스트 서브셸 C가 없기 때문에 대기 시간은 없습니다 (exec는 C를 분기하여 기다리게 만드는 대신 sleep이 C를 대체하도록 강제합니다 sleep). 이 경고에도 불구하고,

echo two > >(exec cat) | cat; echo three

이전에 설명한 대로 함수는 계속해서 순서대로 올바르게 실행됩니다.

관련 정보