코드에서“{exec> / dev / null; }> / dev / null”후드에서 무슨 일이 일어나고 있습니까?


15

exec 리디렉션이 포함 된 명령 목록을 리디렉션하면 다음과 같이 exec> / dev / null이 여전히 적용되지 않는 것 같습니다.

{ exec >/dev/null; } >/dev/null; echo "Hi"

"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

이상하다; 를 재현 할 수 없습니다 close(10). strace를 실행 한 전체 스크립트 컨텐츠를 게시 할 수 있습니까?
DepressedDaniel

: @DepressedDaniel 여기에 전체 스크립트와 strace를입니다 스크립트 의 strace
조이 Pabalinas

;이후 에는 스트레이가 생겨서 결국 복합 목록에 적용되지 않는 }의미가 변경 > /dev/null됩니다 {}.
DepressedDaniel

@DepressedDaniel 아, 당신은 완전히 정확합니다! 이제 결과는 내가 기대하는 것입니다. 답변 주셔서 감사합니다!
Joey Pabalinas

답변:


17

따라 가자

{ 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 하고 명령이 종료 될 때이를 취소하는 것을 기억합니다 .

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

    씨. 마지막으로, 쉘은 표준 출력을 원래 위치로 다시 전환합니다. (이것은 메커니즘과 동일합니다. 경로 ls -lR /usr/share/fonts >~/FontList.txt재 지정은 속한 명령 기간 동안 만 수행됩니다.)

  3. 첫 번째 명령이 완료되면 쉘이 실행 echo "Hi"됩니다. 표준 출력은 첫 번째 명령 이전의 위치입니다.


2a가 2b 이전에 실행되는 이유는 무엇입니까? (오른쪽에서 왼쪽으로)
Joey Pabalinas

5
적용되는 명령 전에 리디렉션 실행 해야합니다 . 그들은 어떻게 다르게 일할 수 있습니까?
AlexP

아하, 그런 식으로 생각하지 않았어! 처음 두 가지는 큰 답변입니다. 하나를 결정하기 전에 조금만 주었지만 두 가지 설명에 감사드립니다!
Joey Pabalinas

불행히도 하나의 답변 만 선택할 수 있으므로 기술이 적기 때문에 기술에 정통하지 않은 사용자도 도울 수 있다고 생각합니다. 그러나 @DepressedDaniel은 여기 에 더 자세한 설명을 제공 하는 똑같이 훌륭한 대답을 했습니다 .
Joey Pabalinas

14

서브 쉘 또는 서브 프로세스를 사용하지 않기 위해 복합리스트의 출력 {}이 파이프 될 때 >쉘은 복합리스트를 실행하기 전에 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 행에서 descriptor 1( STDOUT)가 최소한 인덱스를 가진 다른 디스크립터 에 어떻게 복사 되는지 볼 수 있습니다.10 (그것이 무엇입니까 F_DUPFD; 해당 디스크립터에 복제 한 후 주어진 수에서 시작하여 가장 낮은 사용 가능한 디스크립터를 리턴합니다). 또한 137 행에서 open("/dev/null")(descriptor 3) 의 결과가 descriptor 1( STDOUT) 에 어떻게 복사 되는지를보십시오 . 마지막으로, 온라인 147에서 STDOUT저장된 기존 디스크립터 10는 디스크립터 1( STDOUT)로 다시 복사됩니다 . 순 효과는 변경 사항을 STDOUT온라인 144(내부에 해당 )으로 단열하는 것 exec >/dev/null입니다.


137 행에서 FD 3이 FD 1을 덮어 쓰므로, 141 행이 10을 / dev / null을 가리 키지 않는 이유는 무엇입니까?
Joey Pabalinas

@JoeyPabalinas Line 141은 FD 1 (즉, stdout)을 10 이후에 사용 가능한 다음 디스크립터 로 복제 합니다. 이는 시스템 호출의 반환 값에서 볼 수 있듯이 11로 나타납니다. 10은 bash에 하드 코딩되어 있으므로 bash의 설명자 저장은을 통해 스크립트에서 조작 할 수있는 한 자리 설명자를 방해하지 않습니다 exec.
DepressedDaniel

그렇다면 fcntl (1, F_DUPFD, 10)은 FD 1이 현재 가리키는 위치에 관계없이 항상 STDOUT을 참조합니까?
Joey Pabalinas

@JoeyPabalinas 귀하의 질문이 무엇인지 잘 모르겠습니다. FD 1 IS STDOUT. 그들은 같은 것입니다.
DepressedDaniel

원래 게시물에 전체 strace 출력을 추가했습니다.
Joey Pabalinas

8

차이 { exec >/dev/null; } >/dev/null; echo "Hi"{ exec >/dev/null; }; echo "Hi"이중 리디렉션 않는다는 것이다 dup2(10, 1);폐쇄 원래의 카피 (10)가 fd 전에 stdout다음 명령을 실행하기 전에, ( echo).

외부 리디렉션이 실제로 내부 리디렉션을 오버레이하기 때문에 그런 식으로 발생합니다. stdout그것이 완료되면 원래 fd를 다시 복사하는 이유 입니다.


차이점을 쉽게 설명하는 +1 AlexP의 답변에는이 설명이 없습니다.
Kamil Maciorowski
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.