exec 리디렉션이 포함된 명령 목록을 리디렉션하면 exec >/dev/null이 더 이상 작동하지 않는 것 같습니다. 예를 들면 다음과 같습니다.
{ exec >/dev/null; } >/dev/null; echo "Hi"
"안녕"을 인쇄하세요.
내 생각에는 {}
명령 목록이 파이프라인의 일부가 아닌 한 하위 쉘로 간주되지 않으므로 exec >/dev/null
현재 쉘 환경 내에서 계속 적용되어야 한다고 생각합니다.
이제 다음과 같이 변경하면:
{ exec >/dev/null; } 2>/dev/null; echo "Hi"
예상되는 출력이 없습니다. 파일 설명자 1은 향후 명령에 대해 여전히 /dev/null을 가리킵니다. 다시 실행하면 다음이 표시됩니다.
{ exec >/dev/null; } >/dev/null; echo "Hi"
이것은 어떤 출력도 제공하지 않습니다.
스크립트를 작성하고 추적해 보았지만 여기서 정확히 무슨 일이 일어나고 있는지 아직 잘 모르겠습니다.
이 스크립트의 각 지점에서 STDOUT 파일 설명자는 어떻게 되나요?
편집: 내 strace 출력을 추가했습니다.
read(255, "#!/usr/bin/env bash\n{ exec 1>/de"..., 65) = 65
open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
close(10) = 0
open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(1, F_GETFD) = 0
fcntl(1, F_DUPFD, 10) = 10
fcntl(1, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
dup2(10, 1) = 1
fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
close(10) = 0
fstat(1, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0
ioctl(1, TCGETS, 0x7ffee027ef90) = -1 ENOTTY (Inappropriate ioctl for device)
write(1, "hi\n", 3) = 3
답변1
우리 따라가자
{ exec >/dev/null; } >/dev/null; echo "Hi"
단계별로.
두 가지 명령이 있습니다:
ㅏ.
{ exec >/dev/null; } >/dev/null
, 이어서비.
echo "Hi"
쉘은 먼저 명령(a)을 실행한 다음 명령(b)을 실행합니다.
집행
{ exec >/dev/null; } >/dev/null
수익은 다음과 같습니다.ㅏ. 먼저 쉘이 리디렉션을 수행합니다.
>/dev/null
명령이 끝나면 실행 취소하는 것을 잊지 마세요..b. 그런 다음 쉘이 실행됩니다
{ exec >/dev/null; }
.씨. 마지막으로 쉘은 표준 출력을 원래 위치로 다시 전환합니다. (이는
ls -lR /usr/share/fonts >~/FontList.txt
자신이 속한 명령 기간 동안에만 발생하는 리디렉션과 동일한 메커니즘입니다.)첫 번째 명령이 완료되면 쉘이 실행됩니다
echo "Hi"
. 표준 출력은 첫 번째 명령 앞에 위치합니다.
답변2
서브쉘 또는 서브프로세스 사용을 피하기 위해 복합 목록의 출력이 {}
파이프될 때 >
쉘은 복합 목록을 실행하기 전에 STDOUT 설명자를 저장하고 실행한 후에 이를 복원합니다. 따라서 exec >
복합 목록은 이전 설명자가 STDOUT으로 되돌아가는 지점 이후에는 영향을 미치지 않습니다.
관련 부분을 살펴 보겠습니다 strace bash -c '{ exec >/dev/null; } >/dev/null; echo hi' 2>&1 | cat -n
.
132 open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
133 fcntl(1, F_GETFD) = 0
134 fcntl(1, F_DUPFD, 10) = 10
135 fcntl(1, F_GETFD) = 0
136 fcntl(10, F_SETFD, FD_CLOEXEC) = 0
137 dup2(3, 1) = 1
138 close(3) = 0
139 open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
140 fcntl(1, F_GETFD) = 0
141 fcntl(1, F_DUPFD, 10) = 11
142 fcntl(1, F_GETFD) = 0
143 fcntl(11, F_SETFD, FD_CLOEXEC) = 0
144 dup2(3, 1) = 1
145 close(3) = 0
146 close(11) = 0
147 dup2(10, 1) = 1
148 fcntl(10, F_GETFD) = 0x1 (flags FD_CLOEXEC)
149 close(10) = 0
134행에서 최소한 인덱스가 있는 다른 설명자에 설명자 1
( )를 복사하는 방법을 볼 수 있습니다 (이것이 수행되는 작업입니다 . 해당 설명자에 복사한 후 지정된 숫자 설명자에서 시작하여 사용 가능한 가장 낮은 설명자를 반환합니다). (설명자)의 결과가 설명자 ( ) 에 복사되는 방법은 137행을 참조하세요 . 마지막으로 줄에서는 설명자에 저장된 이전 내용이 설명자 ( )에 다시 복사됩니다. 최종 효과는 와이어 의 변경 사항 (내부 항목에 해당 )을 격리하는 것입니다.STDOUT
10
F_DUPFD
open("/dev/null")
3
1
STDOUT
147
STDOUT
10
1
STDOUT
STDOUT
144
exec >/dev/null
답변3
{ exec >/dev/null; } >/dev/null; echo "Hi"
와 차이점은 fd 10(원본 파일의 복사본)을 닫기 전과 다음 명령( )을 실행하기 전에 { exec >/dev/null; }; echo "Hi"
이중 리디렉션이 수행 된다는 것입니다.dup2(10, 1);
stdout
echo
이는 외부 리디렉션이 실제로 내부 리디렉션보다 우선하기 때문에 발생합니다. 이것이 stdout
완료되면 원본 fd로 다시 복사되는 이유입니다 .