파일 디스크립터 이동을위한 실제 사용


16

bash 맨 페이지에 따르면 :

리디렉션 연산자

   [n]<&digit-

파일 디스크립터 digit를 파일 디스크립터 로 이동 n하거나, n지정되지 않은 경우 표준 입력 (파일 디스크립터 0)으로 이동 합니다. digit에 중복 된 후 닫힙니다 n.

파일 디스크립터를 다른 디스크립터로 "이동"한다는 것은 무엇을 의미합니까? 그러한 관행의 전형적인 상황은 무엇입니까?

답변:


14

3>&4-는 bash에서 지원하는 ksh93 확장이며 3>&4 4>&-, 짧습니다 . 즉 3은 이제 4가 사용되었던 위치를 가리키고 4는 이제 닫혀 있으므로 4가 가리키는 것은 이제 3으로 이동했습니다.

일반적인 사용법은 다음과 같이 복제 stdin하거나 stdout사본을 저장하고 복원하려는 경우에 사용됩니다.

변수에 stdout을 그대로두고 명령의 stderr (및 stderr 만)을 캡처한다고 가정하십시오.

명령 대체 var=$(cmd), 파이프를 작성합니다. 파이프의 쓰기 끝은 cmd표준 stdout (파일 설명자 1)이되고 다른 끝은 변수를 채우기 위해 셸에서 읽습니다.

이제 stderr변수로 가려면 다음을 수행하십시오 var=$(cmd 2>&1).. 이제 fd 1 (stdout)과 2 (stderr)는 파이프 (그리고 결국 변수)로갑니다. 우리가 원하는 것의 절반에 불과합니다.

우리가 var=$(cmd 2>&1-)(짧게 var=$(cmd 2>&1 >&-)하면 이제 cmdstderr 만 파이프로 이동하지만 fd 1은 닫힙니다. 경우 cmd시도가 함께 반환 모든 출력, 작성 EBADF오류가 파일을 열 경우, 그것은 최초의 무료 FD를 얻을 것이다 열려있는 파일에 할당됩니다 stdout그에 대해 명령 가드하지 않는 한! 우리가 원하는 것도 아닙니다.

stdout을 cmd단독으로 남겨 두 려면 (즉 , 명령 대체 외부에서 가리키는 동일한 자원을 가리 키려면) 해당 자원을 명령 대체 내부로 가져와야합니다. 이를 위해 명령 대체 stdout 외부 의 사본 을 수행하여 내부로 가져갈 수 있습니다.

{
  var=$(cmd)
} 3>&1

더 확실한 작성 방법입니다.

exec 3>&1
var=$(cmd)
exec 3>&-

(또한 결국 fd 3을 닫는 대신 복원하는 이점이 있습니다).

그런 다음 {(또는 exec 3>&1) 및을 (를) 기준으로 }fd 1 및 3 모두 처음에 가리키는 동일한 리소스 fd 1을 가리 킵니다. fd 3은 명령 대체 내의 해당 자원을 가리 킵니다 (명령 대체는 fd 1, stdout 만 경로 재지 정함). 따라서 위의 cmdfds 1, 2, 3을 얻었습니다.

  1. var에 파이프
  2. 손대지 않은
  3. 1이 명령 대체 외부를 가리키는 것과 동일

우리가 그것을 변경하면 :

{
  var=$(cmd 2>&1 >&3)
} 3>&1-

그러면 다음과 같이됩니다.

  1. 1이 명령 대체 외부를 가리키는 것과 동일
  2. var에 파이프
  3. 1이 명령 대체 외부를 가리키는 것과 동일

이제 우리는 원하는 것을 얻었습니다. stderr는 파이프로 가고 stdout은 그대로 남아 있습니다. 그러나 우리는 fd 3을cmd 있습니다.

일반적으로 명령은 fds 0 ~ 2가 열려 있고 표준 입력, 출력 및 오류 인 것으로 가정하지만 다른 fd는 가정하지 않습니다. 그들은 아마도 그 fd 3을 그대로 남겨 둘 것입니다. 다른 파일 설명자가 필요한 경우 open()/dup()/socket()...사용 가능한 첫 번째 파일 설명자를 반환하는 작업을 수행합니다 . 그렇다면 (쉘 스크립트처럼 exec 3>&1) 그것들을 사용해야합니다.fd 구체적 , 우선 그것을 무언가에 할당 할 것입니다.

fd 3 cmd을 사용하지 않기 때문에 fd 3을 닫는 것이 좋습니다 . 그러나 전화하기 전에 할당 된 상태로두면 별 문제가되지 않습니다 cmd. 문제 cmd(및 잠재적으로 발생하는 다른 프로세스)에 사용 가능한 fd가 하나 더 적을 수 있습니다. 잠재적으로 더 심각한 문제는 fd가 가리키는 리소스가 cmd백그라운드에서 생성 된 프로세스에 의해 유지 될 수있는 경우 입니다. 해당 리소스가 파이프 또는 다른 프로세스 간 통신 채널 인 경우 (예 : 스크립트가로 실행되는 경우 script_output=$(your-script)), 다른 쪽 끝에서 읽은 프로세스는 파일 끝까지 파일 끝을 볼 수 없기 때문에 문제가 될 수 있습니다. 백그라운드 프로세스가 종료됩니다.

따라서 여기에 작성하는 것이 좋습니다.

{
  var=$(cmd 2>&1 >&3 3>&-)
} 3>&1

다음과 bash같이 단축 할 수 있습니다.

{
  var=$(cmd 2>&1 >&3-)
} 3>&1

거의 사용되지 않는 이유를 요약하면 다음과 같습니다.

  1. 그것은 비표준적이고 단지 구문 설탕입니다. 몇 가지 키 입력을 절약하면서 스크립트를 이식성이 떨어지고 일반적이지 않은 기능에 익숙하지 않은 사람들에게 덜 명확하게 만들 수 있습니다.
  2. 복제 후 원래 fd를 닫을 필요성은 종종 간과되기 때문에 대부분의 경우 결과를 겪지 않기 때문에 또는 >&3대신에 수행 합니다 .>&3->&3 3>&-

거의 사용되지 않는다는 증거 는 bash에서 가짜 임을 알 수 있습니다. bash에서 compound-command 3>&4-또는 any-builtin 3>&4-fd 4 후에도 compound-command또는 any-builtin반환 된 후에도 닫힙니다 . 이 문제를 해결하기 위한 패치 가 제공됩니다 (2013-02-19).


고마워, 이제 나는 fd를 움직이는 것이 무엇인지 안다. 두 번째 스 니펫 (및 일반적인 fds)에 관한 4 가지 기본 질문이 있습니다. 1) cmd1에서 2 (stderr)를 3의 사본으로 만듭니다. 명령 이이 3 fd를 내부적으로 사용하면 어떻게됩니까? 2) 왜 3> & 1 및 4> & 1이 작동합니까? 3과 4를 복제하면 두 cmd에만 적용됩니다. 현재 쉘도 영향을 받습니까? 3) 왜 cmd1에서 4를 닫고 cmd2에서 3을 닫습니까? 이러한 명령은 언급 된 fd를 사용하지 않습니까? 4) 마지막 스 니펫에서 fd가 존재하지 않는 것으로 복제되면 (cmd1 및 cmd2에서) 각각 3과 4를 의미합니까?
Quentin

@Quentin, 나는 더 간단한 예를 사용했으며 이제는 대답보다 적은 질문을 제기하기를 바랍니다. fd 이동 구문과 직접 ​​관련이없는 질문이 여전히있는 경우 별도의 질문을하는 것이 좋습니다.
Stéphane Chazelas

{ var=$(cmd 2>&1 >&3) ; } 3>&1-1을 닫는 데 오타가 아닙니까?
Quentin

@Quentin, 그것은 내가 그것을 포함하는 의미 생각하지 않는 점에서 오타 그러나 그것은 괄호가 사용하는 내부 아무것도하기 때문에 차이가 없습니다,의 1을 전략 중 (이 3 fd와 그것을 복제의 요점 : 1 원래 때문에 그렇지 않으면 내부에서 액세스 할 수 없습니다 ). $(...)
Stéphane Chazelas

1
입력시 @Quentin {...}가리킨 사용 1가 fd 것과 3 점을 FD로 함과 동시에 1 입력에이어서, 폐쇄가 fd $(...)1은 그 피드 파이프로 설정된다가 fd $var후 대한 cmd것과 2뿐만 아니라, 다음 (1) 어떻게 3 점 1이 닫힌 상태로 남아 있다는 사실은 bash의 버그이므로보고하겠습니다. 해당 기능의 출처 인 ksh93에는 해당 버그가 없습니다.
Stéphane Chazelas

4

다른 파일 디스크립터와 동일한 위치를 가리 키도록하는 것을 의미합니다. 당신은 (떨어져 표준 오류 기술자의 명백한 별도의 처리에서 매우 드물게이 작업을 수행하지 필요 stderr, fd 2,/dev/stderr -> /proc/self/fd/2 ). 복잡한 경우에 유용 할 수 있습니다.

고급 Bash 스크립팅 안내서에는 더 긴 로그 레벨 예제 와이 스 니펫이 있습니다.

# Redirecting only stderr to a pipe.
exec 3>&1                              # Save current "value" of stdout.
ls -l 2>&1 >&3 3>&- | grep bad 3>&-    # Close fd 3 for 'grep' (but not 'ls').
#              ^^^^   ^^^^
exec 3>&-                              # Now close it for the remainder of the script.

Source Mage 's Sorcery에서 예를 들어 동일한 코드 블록에서 다른 출력을 식별하는 데 사용합니다.

  (
    # everything is set, so run the actual build infrastructure
    run_build
  ) 3> >(tee -a $C_LOG >> /dev/stdout) \
    2> >(tee -a $C_LOG 1>&2 > $VOYEUR_STDERR) \
     > >(tee -a $C_LOG > $VOYEUR_STDOUT)

로깅 이유 때문에 추가 프로세스 대체가 추가되었지만 (VOYEUR는 데이터를 화면에 표시할지 아니면 로그 만 표시할지 결정) 일부 메시지는 항상 표시 해야합니다 . 이를 위해 파일 디스크립터 3에 인쇄 한 다음 특수하게 처리합니다.


0

Unix에서 파일은 파일 디스크립터에 의해 처리됩니다 (예 : 작은 정수, 예를 들어 표준 입력은 0, 표준 출력은 1, 표준 오류는 2입니다. 다른 파일을 열면 일반적으로 사용되지 않는 가장 작은 디스크립터가 지정됩니다). 이 프로그램의 inards을 알고, 당신은 표준 출력에 파일 기술자 (5)로 이동 출력을 보낼 그렇다면, 당신은이 있다고 1. 기술자 5를 이동 줄 2> errors에서 유래 및 구조물이 좋아 2>&1에 오류를 복제 출력 스트림

그래서 거의 사용하지 않았지만 (25 년 이상 유일하게 독점적으로 유닉스를 사용했을 때 분노에 한두 번 사용한 것을 막연히 기억합니다), 절대적으로 필요할 때.


그러나 파일 디스크립터 1을 다음과 같은 방식으로 복제하지 않는 이유는 무엇입니까? 5> & 1? 커널이 곧바로 FD를 닫으려고하므로 FD를 어떻게 사용하는지 이해가되지 않습니다.
Quentin

그것은 5를 1로 복제하지 않고 1이 가고있는 곳으로 5를 보냅니다. 그리고 당신이 묻기 전에; 그렇습니다. 다양한 전구체 쉘에서 선택되는 여러 가지 표기법이 있습니다.
vonbrand

여전히 이해가되지 않습니다. 경우 5>&11 어디로 5를 전송 한 후 정확히 않습니다 1>&5-(5)를 폐쇄 외에합니까?
Quentin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.