`... > >(sed 's/^/stdout: /')`가 인쇄되지 않는데 `... | sed 's/^/stdout: /'`가 빈 표준 입력에 인쇄되는 이유는 무엇입니까?

`... > >(sed 's/^/stdout: /')`가 인쇄되지 않는데 `... | sed 's/^/stdout: /'`가 빈 표준 입력에 인쇄되는 이유는 무엇입니까?

나는 기능적으로 동일하다고 생각되는 두 구조 사이의 차이점을 일으키는 원인을 이해하려고 노력하고 있습니다.

$ ( echo foo >&2 ) 2> >(sed 's/^/stderr: /') | sed 's/^/stdout: /'   
stdout: stderr: foo
$ ( echo foo >&2 ) 2> >(sed 's/^/stderr: /') > >(sed 's/^/stdout: /')
stderr: foo

편집: user1133275를 올바르게 이해했다면 그는 표준 출력으로 출력하지 않으면 >(sed 's/^/stdout: /')하위 쉘이 ( echo foo >&2 )실행되지 않을 것이라고 제안했습니다. 그러나 이는 다음을 의미합니다.

$ ( echo foo >&2 ) 2> >(sed 's/^/stderr: /') > >(echo baz)   
baz
stderr: foo

표시되어서는 안 됩니다 baz.

stdout:편집 2: 아마도 흥미롭게도 sed는 파이핑할 때에도 빈 입력에 대해 출력 하지 않습니다 .

$ sed 's/^/stdout: /' < /dev/null
$ printf "" | sed 's/^/stdout: /'
$

답변1

첫 번째 명령,

( echo foo >&2 ) 2> >(sed 's/^/stderr: /') | sed 's/^/stdout: /'

단순화된 형식(생성된 데이터를 저장하기 위해 임시 파일 사용 echo):

{ echo foo 2>file >&2; sed 's/^/stderr: /' file; } | sed 's/^/stdout: /'

즉, 첫 번째는 sed표준 오류의 결과 내용을 읽고 echo이를 표준 출력에 쓰고, 두 번째는 sed이를 읽고 수정합니다.

두 번째 명령은

( echo foo >&2 ) 2> >(sed 's/^/stderr: /') > >(sed 's/^/stdout: /')

단순화된 형태로,

echo foo 2>file >&2; sed 's/^/stderr: /' file; sed 's/^/stdout: /' </dev/null

여기서 sedstderr를 얻는 것은 출력을 생성하는 반면, stderr(아무 것도 없음)을 얻는 다른 것은 sed출력을 생성하지 않습니다(입력을 얻지 못하고 데이터가 삽입되거나 추가되지 않기 때문입니다).

다른 표현으로는:

첫 번째 명령:

( echo foo >&2 ) 2>file
sed 's/^/stderr: /' file | sed 's/^/stdout: /'

두 번째 명령:

( echo foo >&2 ) 2>file >otherfile
sed 's/^/stderr: /' file
sed 's/^/stdout: /' otherfile

즉, sed두 명령 중 두 번째 명령은 아무것도 읽지 않습니다. 특히, 첫 번째 명령과 마찬가지로 첫 번째 명령의 출력을 읽지 않습니다 sed.


매우 단순화된 표기법을 사용하면 첫 번째 명령은 다음과 같습니다.

utility-writing-to-stderr 2> >(something1) | something2

이는 something1표준 출력에 기록되고 something2.

두 번째 명령은 동일한 표기법을 사용합니다.

utility-writing-to-stderr 2> >(something1) >(something2)

즉 , 서로 연결되어 something1있지도 않고 어떤 방식으로든 생성되는 내용을 읽을 수 없습니다. 게다가 표준 출력 스트림에서는 아무 것도 생성되지 않으므로 표준 입력에서 아무것도 읽을 수 없습니다.something2something2something1utility-writing-to-stderrsomething2

답변2

>쉘의 리디렉션 순서로 인해 작동 ( echo foo >&2 )하고 |작동합니다 . >(sed 's/^/stderr: /')예를 들어

$ ( echo foo >&2 ) 2> >(sed 's/^/stderr: /' > >(sed 's/^/stdout: /') )
stdout: stderr: foo

또는

$ ( echo foo >&2 ) 2> >(sed 's/^/stderr: /') | cat > >(sed 's/^/stdout: /')
stdout: stderr: foo

명확한 예를 들어 질문을 순서대로 작성하세요.

$ ( ( echo foo >&2 ) > >(sed 's/^/stdout: /') ) 2> >(sed 's/^/stderr: /')
stderr: foo

또는

$ ( ( echo foo >&2 )  | sed 's/^/stdout: /' ) 2> >(sed 's/^/stderr: /')
stderr: foo

답변3

이 두 명령은 동일하지 않습니다.

명령(1):

( one ) 2> >(two) | three 

(std) 출력을 two다음으로 보냅니다 three(stderr에 대한 자세한 내용은 아래 참조).

명령(2):

( one ) 2> >(two) > >(three)

(std) 출력을 (stderr ) one로 보냅니다 .threetwo


가슴 아픈 세부 사항은 다음과 같습니다.

명령 1:( one ) 2> >(two) | three

  • 명령 threestart( sed 's/^/stdout: /')는 표준 입력의 입력을 기다립니다.
  • 2( stderr) stdin는 ~ 로 리디렉션됩니다 two.
  • one내부 리디렉션( echo foo >&2)이 발생했습니다.
  • 명령이 실행됩니다 foo( stderr표준 출력으로 전송되지 않음).
  • 단어는 foo( of )에서 ( of )로 리디렉션됩니다.stderronestdintwo
  • 두 명령을 실행하십시오 ( sed 's/^/stderr: /')
  • twoModified() 출력이 stderr: foo이제 stdout 으로 전송됩니다 two.
  • 현재 표준 출력은 two파이프를 통해 도착합니다 three.
  • three입력 명령이 처음부터 출력을 받기를 기다립니다.
  • 출력이 수정되고 리더가 추가됩니다 stdout:.
  • 마지막 문자열은 stdout: stderr: footty로 이동합니다.

명령 2: (1) 2 >> (2) >> (3)

  • 마지막 리디렉션( >)이 먼저 빌드됩니다.
  • 명령이 three입력 대기를 시작합니다.stdin 오직( >()).
  • 이 명령 three오직표준 출력 one.
  • 명령이 two입력 대기를 시작합니다.
  • 이 명령은 two(오직) 표준 에러 one.
  • 내부 리디렉션은 stdout을 one여기에 연결합니다 stderr.
  • 명령은 (of ) 로 one전송되어 실행됩니다 .foostderrone
  • foo가다two
  • two수신된 문자열을 변경하여 stderr: foo다음으로 보냅니다.단말기.
  • threestdout 에서 빈 입력을 받습니다 one.
  • three인쇄아무것도 없다(처리할 행이 없기 때문입니다.)

이 명령에 대해 알아보세요:

printf '' | sed 's/^/initial: /'

출력이 없습니다(그리고 작동하게 할 방법도 없습니다).


변경 순서: 명령 3: (1) >>(3) 2>>(2)

two연습으로 three세부사항 출력 대신 에 이동 하십시오.tty

답변4

나는 @Kusalananda의 매우 명확한 답변에 동의하지만 직장에서 프로세스 대체의 작은 예로서 그의 답변에 이것을 추가할 위험이 있습니다.

두 번째 제안은 작은 인쇄상의 변경(및 잘못된 논리의 비교적 큰 변경)으로 구현될 수 있습니다.

  $ ( echo foo >&2 ) 2> >(sed 's/^/stderr: /') > >(sed 's/^/stdout: /')
  stderr: foo    # for the well explained reason above

하지만:

    v                                          vv
  $ ( (echo foo >&2) 2> >(sed 's/^/stderr: /') )> >(sed 's/^/stdout: /')  
  stdout: stderr: foo

( )>표시된 대로 추가하면(또는 >( )둘 다 잘 작동함) 실제로 프로세스 대체를 통해 출력을 입력으로 리디렉션할 수 있는 파일을 생성할 수 있기 때문입니다 >(sed 's/^/stdout: /').

다른 프로세스 교체 내에 프로세스 교체를 포함하는 방법을 설명하기 위해 이것을 추가하는 것을 고려했습니다. 가독성을 위해 나는아니요더 높은 수준의 임베딩으로 푸시합니다. 하지만 어떤 이유로든 피해야 하는 경우관로그리고 관련껍데기, 이는 좋은 해결책입니다.

관련 정보