"{ exec >/dev/null; } >/dev/null" 코드에서 정확히 무슨 일이 일어나고 있나요?

"{ exec >/dev/null; } >/dev/null" 코드에서 정확히 무슨 일이 일어나고 있나요?

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"

단계별로.

  1. 두 가지 명령이 있습니다:

    ㅏ. { exec >/dev/null; } >/dev/null, 이어서

    비.echo "Hi"

    쉘은 먼저 명령(a)을 실행한 다음 명령(b)을 실행합니다.

  2. 집행 { exec >/dev/null; } >/dev/null수익은 다음과 같습니다.

    ㅏ. 먼저 쉘이 리디렉션을 수행합니다.>/dev/null 명령이 끝나면 실행 취소하는 것을 잊지 마세요..

    b. 그런 다음 쉘이 실행됩니다 { exec >/dev/null; }.

    씨. 마지막으로 쉘은 표준 출력을 원래 위치로 다시 전환합니다. (이는 ls -lR /usr/share/fonts >~/FontList.txt자신이 속한 명령 기간 동안에만 발생하는 리디렉션과 동일한 메커니즘입니다.)

  3. 첫 번째 명령이 완료되면 쉘이 실행됩니다 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행을 참조하세요 . 마지막으로 줄에서는 설명자에 저장된 이전 내용이 설명자 ( )에 다시 복사됩니다. 최종 효과는 와이어 의 변경 사항 (내부 항목에 해당 )을 격리하는 것입니다.STDOUT10F_DUPFDopen("/dev/null")31STDOUT147STDOUT101STDOUTSTDOUT144exec >/dev/null

답변3

{ exec >/dev/null; } >/dev/null; echo "Hi"와 차이점은 fd 10(원본 파일의 복사본)을 닫기 전과 다음 명령( )을 실행하기 전에 { exec >/dev/null; }; echo "Hi"이중 리디렉션이 수행 된다는 것입니다.dup2(10, 1);stdoutecho

이는 외부 리디렉션이 실제로 내부 리디렉션보다 우선하기 때문에 발생합니다. 이것이 stdout완료되면 원본 fd로 다시 복사되는 이유입니다 .

관련 정보