몇 주 전에 나는 어떤 것을 보았다.이상한 대답이 문제에 대해"(방법) 백그라운드에서 자동으로 작업을 시작하시겠습니까?".이 해결 방법이 잘못된 것 같습니다(내 답변을 비교해보세요) 쉘이 백그라운드에서 자동으로 작업을 시작하는 것처럼 보이지만.
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 background
cmd&
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 &
foo
stderr이 리디렉션된 상태로 백그라운드에서 실행되지만 /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/null
fd 3은 stderr이 현재 가리키는 위치로 리디렉션된 다음 stderr /dev/null
이 원래 터미널을 가리켰다고 가정하면 결과는 입니다 3 -> terminal, 2 -> /dev/null
. 이러한 리디렉션은 그룹에 대해 작동합니다.
{ foo 2>&3 & }
그룹 내부에 있으면 그룹 foo
의 fd 3을 가리키는 stderr을 사용하여 백그라운드에서 시작하고 작업 ID와 프로세스 ID가 그룹의 stderr에 인쇄됩니다.
둘을 합치면 foo
stderr는 그룹의 fd 3이 가리키는 위치, 즉 원래 stderr을 가리키고 작업 ID는 그룹의 fd 2에 인쇄됩니다 /dev/null
.
따라서 실제로 { 2>&3 foo & } 3>&2 2>/dev/null
셸에서 인쇄된 작업 ID와 프로세스 ID는 로 리디렉션되고 /dev/null
stderr 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로 실행된다는 점 을 놓쳤습니다 .