I. 질문: 쉘 표준 오류를 실제로 리디렉션할 수 있습니까?

I. 질문: 쉘 표준 오류를 실제로 리디렉션할 수 있습니까?

몇 주 전에 나는 어떤 것을 보았다.이상한 대답이 문제에 대해"(방법) 백그라운드에서 자동으로 작업을 시작하시겠습니까?".이 해결 방법이 잘못된 것 같습니다(내 답변을 비교해보세요) 쉘이 백그라운드에서 자동으로 작업을 시작하는 것처럼 보이지만.

I. 질문: 쉘 표준 오류를 실제로 리디렉션할 수 있습니까?

제안된 솔루션에 대한 설명이 없으며, 분석에서도 확실한 답변을 제공하지 않습니다.

아래에서 코드 조각을 볼 수 있습니다.

# Run the command given by "$@" in the background
silent_background() {
   if [[ -n $BASH_VERSION ]]; then
      { 2>&3 "$@"& } 3>&2 2>/dev/null
   fi
}

문제의 명령은 입니다 { 2>&3 "$@"& } 3>&2 2>/dev/null.

1) 분석

명령 그룹( )은 하나의 명령( )에 대해 { ... }두 개의 리디렉션( 및 )을 지정합니다 . 3>&2명령은 리디렉션( )이 포함된 비동기 목록( )입니다.2>/dev/null# Run the command given by "$@" in the backgroundcmd&2>&3

POSIX 사양

각 리디렉션은 해당 리디렉션을 명시적으로 재정의하지 않는 복합 명령의 모든 명령에 적용되어야 합니다.

표준 오류 리디렉션은 2>/dev/null비동기 목록과 관련된 리디렉션으로 재정의됩니다 2>&3.

특정 사례

첫 번째 경우에는 표준 오류가 로 리디렉션되고 /dev/null, 두 번째 경우에는 표준 오류가 다음으로 리디렉션됩니다.주문하다아직 터미널에 연결되어 있습니다.

prompt% { grep warning system.log& } 2>/dev/null
prompt% { 2>&3 grep warning system.log& } 3>&2 2>/dev/null
grep: system.log: No such file or directory

아래에서도 비슷한 사례를 볼 수 있습니다. 첫 번째 경우에는 표준 출력주문하다아직 터미널에 연결되어 있습니다. 두 번째 경우에는 표준 출력이 리디렉션됩니다.주문하다수정됨: >echo.txt덮어썼습니다 >print.txt.

prompt% { >&3 echo some data...& } 3>&1 >echo.txt
[1] 3842
some data...
prompt% file echo.txt
echo.txt: empty
prompt% { >&3 echo some data...& } 3>print.txt >echo.txt
[1] 2765
prompt% file echo.txt
echo.txt: empty
prompt% cat print.txt
some data...

ii) 관찰

언급한 대로 명령은 백그라운드에서 자동으로 시작되는 것처럼 보입니다. 보다 정확하게 [1] 3842는 예를 들어 백그라운드 작업에 대한 알림을 표시하지 않습니다.

이전 분석에서는 리디렉션이 취소될 수 있기 때문에 중복되는 것으로 나타났습니다.

{ redir_3 cmd& } redir_1 redir_2.cmd&

iii) 설명

위의 구성은 부작용으로 인해 알림을 숨기는 것 같습니다.

어떻게 이런 일이 일어나는지 설명해 주실 수 있나요?

2. 가설

Ilkkachu의 답변 및 코멘트약간의 진전을 허용하십시오. 각 프로세스에는 자체 파일 설명자가 있습니다.

  • 쉘 메시지는 쉘 표준 오류 시 전송될 수 있습니다.

또 다른 컨텍스트: 서브셸에서 실행되는 비동기 목록입니다. 명령이 g존재하지 않습니다.

prompt% ( g& )
g: command not found

비동기 명령, 괄호로 묶인 명령...은 쉘 환경과 중복되는 서브쉘 환경에서 실행됩니다...

하위 셸은 상위 셸에서 표준 오류 스트림의 값을 상속합니다.

따라서 전자의 경우 표준 오류 스트림이 터미널로 전달되어야 합니다. 그러나 작업 알림은 표시되지 않으며 쉘 표준 오류에는 쉘 오류 메시지가 표시될 수 있습니다.

답변1

가장 중요한 점을 놓치고 있습니다. 쉘 리디렉션은 왼쪽에서 오른쪽으로 적용됩니다.

존재하다:

{ 2>&3 "$@"& } 3>&2 2>/dev/null

전체 명령 그룹은 다음과 같이 실행됩니다.

  • 파일 설명자 3 => 표준 오류, 즉단말기이 시간에.
  • 파일 설명자 2(표준 오류) => /dev/null

따라서 그룹 내의 명령이 실행될 때:

  • 표준 오류 => 파일 설명자 3, 터미널을 가리키고 있습니다.

따라서 "$@"&표준 오류로 인쇄되는 항목이 있으면 출력이 터미널에 인쇄됩니다.


특정 사례의 경우:

{ grep warning system.log& } 2>/dev/null

{ grep warning system.log& }표준 오류에 대한 포인터로 실행됩니다 /dev/null. grep리디렉션을 재정의하지 않으므로 표준 오류는 와 동일하고 로 {...}리디렉션되며 /dev/null터미널에 출력이 제공되지 않습니다.

존재하다:

{ 2>&3 grep warning system.log& } 3>&2 2>/dev/null

grep표준 오류는 위에서 언급한 것처럼 터미널을 가리키는 파일 설명자 3으로 리디렉션되므로 출력을 터미널로 보낼 수 있습니다.

답변2

대화형 Bash에서는 foo &백그라운드에서 실행되며 작업 ID와 프로세스 ID를 셸의 표준 오류에 인쇄합니다.foo

foo 2>/dev/null &foostderr이 리디렉션된 상태로 백그라운드에서 실행되지만 /dev/null여전히 작업 ID와 프로세스 ID를 인쇄합니다.껍데기표준 오류( 리디렉션되지 않음 stderr foo)

{ foo & } 2>/dev/null먼저 stderr를 으로 리디렉션한 다음 해당 리디렉션을 적용하여 /dev/null내부를 처리합니다. {}그런 다음 foo백그라운드에서 시작하고 작업 ID와 프로세스 ID를 인쇄합니다.지금 리디렉션쉘의 표준 오류(예 /dev/null: ).

Bash 매뉴얼은 그룹 외부의 리디렉션이 전체 그룹에 적용됨을 암시합니다.:

명령어를 그룹화하면 전체 명령어 목록에 대해 리디렉션을 적용할 수 있습니다.

앱의 리디렉션 그룹도 작업 ID 출력을 리디렉션하는지 확인할 수 있습니다.

${sleep9&}2>온도
[여기서는 출력이 없습니다]
$ 고양이 체온
[1]8490

그러나 명령이 최종적으로 종료되면 알림이 터미널에 나타납니다. (또는 오히려 쉘의 현재 stderr에 나타납니다.)

$ kill %1
[1]+  Terminated              sleep 99

에서 { ... } 3>&2 2>/dev/nullfd 3은 stderr이 현재 가리키는 위치로 리디렉션된 다음 stderr /dev/null이 원래 터미널을 가리켰다고 가정하면 결과는 입니다 3 -> terminal, 2 -> /dev/null. 이러한 리디렉션은 그룹에 대해 작동합니다.

{ foo 2>&3 & }그룹 내부에 있으면 그룹 foo의 fd 3을 가리키는 stderr을 사용하여 백그라운드에서 시작하고 작업 ID와 프로세스 ID가 그룹의 stderr에 인쇄됩니다.

둘을 합치면 foostderr는 그룹의 fd 3이 가리키는 위치, 즉 원래 stderr을 가리키고 작업 ID는 그룹의 fd 2에 인쇄됩니다 /dev/null.

따라서 실제로 { 2>&3 foo & } 3>&2 2>/dev/null셸에서 인쇄된 작업 ID와 프로세스 ID는 로 리디렉션되고 /dev/nullstderr foo은 이 리디렉션을 중심으로 라우팅됩니다.

foo's stdout      -> group's fd 1 -> original stdout (never redirected)
foo's stderr      -> group's fd 3 -> original stderr
job id from group -> group's fd 2 -> /dev/null

답변3

{명령이 nulled로 실행되고 stderr복원된 fd를 사용하여 포함된 백그라운드 명령을 시작하지만 이는 명령을 시작하고 명령이 시작된 피드백 메시지를 내보내는 명령이며 null로 실행된다는 점 을 놓쳤습니다 .

관련 정보