파일 설명자 이동의 실제 사용

파일 설명자 이동의 실제 사용

Bash 매뉴얼 페이지에 따르면:

리디렉션 연산자

   [n]<&digit-

파일 설명자를 파일 설명자로 이동 하거나 digit지정되지 않은 경우 n표준 입력(파일 설명자 0)으로 이동합니다. 복사 후 닫습니다.ndigitn

한 파일 설명자를 다른 파일 설명자로 "이동"한다는 것은 무엇을 의미합니까? 이 관행의 일반적인 상황은 무엇입니까?

답변1

3>&4-bash도 지원하는 ksh93의 확장입니다. 3>&4 4>&-이제 3의 약어는 4가 있던 위치를 가리키고 4는 이제 닫혀 있으므로 4가 가리키는 것이 이제 3으로 이동되었습니다.

일반적인 사용법은 복사본을 만들 stdin거나 저장하고 이를 복원하려는 경우입니다. 예를 들면 다음과 같습니다.stdout

변수에 stdout만 남겨두고 명령의 stderr(및 stderr만)을 캡처한다고 가정해 보겠습니다.

명령 대체는 var=$(cmd)파이프라인을 생성합니다. 파이프의 쓰기 끝은 cmdstdout(파일 설명자 1)이 되고 다른 끝은 셸에서 읽어 변수를 채웁니다.

stderr이제 변수에 액세스 하려면 다음을 수행하십시오 var=$(cmd 2>&1). 이제 fd 1(stdout)과 2(stderr)가 모두 파이프로 들어가며(결국 변수로 들어감) 이는 우리가 원하는 것의 절반에 불과합니다.

이렇게 하면 var=$(cmd 2>&1-)(약어로 var=$(cmd 2>&1 >&-) 이제 cmdstderr만 파이프로 들어가고 fd1은 닫힙니다. cmd출력을 쓰려고 하면 오류가 반환됩니다 . EBADF파일을 열면 첫 번째 사용 가능한 fd를 얻고 stdout명령이 이를 방지하지 않는 한 열린 파일이 여기에 할당됩니다! 우리가 원하는 것도 아닙니다.

stdout이 독립적인 상태를 유지하도록 하려면 cmd, 즉 명령 대체 외부에서 가리키는 동일한 리소스를 가리키도록 하려면 어떻게든 해당 리소스를 명령 대체 내부로 가져와야 합니다. 이를 위해 다음의 사본을 만들 수 있습니다.stdout 외부명령 대체는 그것을 내부에 넣습니다.

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

좀 더 간결하게 작성하는 방법은 다음과 같습니다.

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

(이것은 결국 fd 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.

명령은 관례적으로 fd 0부터 2까지가 개방형이고 표준 입력, 출력 및 오류라고 가정하지만 다른 fd에 대해서는 아무 것도 가정하지 않습니다. 그들은 fd 3을 변경하지 않고 유지할 가능성이 높습니다. 다른 파일 설명자가 필요한 경우 open()/dup()/socket()...사용 가능한 첫 번째 파일 설명자를 반환하는 작업을 수행하기만 하면 됩니다. (셸 스크립트와 같이 exec 3>&1) 특별히 사용해야 하는 경우 fd먼저 이를 무언가에 할당합니다(그리고 그 과정에서 fd 3이 보유한 리소스는 해당 프로세스에 의해 해제됩니다).

fd 3을 닫는 것은 사용하지 않기 때문에 좋은 습관이지만 cmd, 호출하기 전에 할당된 채로 놔두는 것은 큰 문제가 아닙니다 cmd. 문제는 프로세스 cmd(및 생성될 수 있는 다른 프로세스)에 사용 가능한 fd가 적다는 것일 수 있습니다. 잠재적으로 더 심각한 문제는 fd가 가리키는 리소스가 결국 cmd백그라운드 프로세스에 의해 생성된 프로세스에 의해 보유될 수 있는지 여부입니다. 이는 리소스가 파이프 또는 기타 프로세스 간 통신 채널(예: 스크립트가 런타임으로 실행되는 경우 script_output=$(your-script))인 경우 문제를 일으킬 수 있습니다. 이는 다른 쪽 끝에서 읽는 프로세스가 파일의 끝을 볼 수 없음을 의미합니다. 끝까지 자원. 백그라운드 프로세스가 종료됩니다.

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

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

그 중 with는 bash다음과 같이 단축될 수 있습니다.

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

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

  1. 비표준이며 단지 구문 설탕입니다. 이러한 흔하지 않은 기능에 익숙하지 않은 사람들에게 스크립트의 이식성을 떨어뜨리고 덜 명확하게 만드는 것과 일부 키 입력을 저장하는 것의 균형을 맞춰야 합니다.
  2. 복사 후 원본 fd를 닫아야 하는 필요성은 대부분 간과되는 경우가 많습니다. 왜냐하면 대부분의 경우 결과가 발생하지 않기 때문에 또는 >&3대신 수행하기 때문입니다 .>&3->&3 3>&-

거의 사용되지 않는다는 것을 증명해 보세요.배쉬에서 가짜. Bash에서 compound-command 3>&4-또는 fd 4를 떠나면 또는 반환 any-builtin 3>&4-후에도 닫힙니다 . ㅏcompound-commandany-builtin수리하다이 문제는 현재(2013-02-19) 해결될 수 있습니다.

답변2

이는 다른 파일 설명자가 가리키는 것과 동일한 위치를 가리키도록 한다는 의미입니다. 표준 오류 설명자( 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에 인쇄한 다음 특별하게 처리합니다.

답변3

Unix에서 파일은 파일 설명자(표준 입력의 경우 0, 표준 출력의 경우 1, 표준 오류의 경우 2와 같은 작은 정수, 다른 파일을 열 때 일반적으로 사용되지 않는 가장 작은 설명자가 할당됨)에 의해 처리됩니다. 따라서 프로그램의 내부 구조를 알고 파일 설명자 5의 출력을 표준 출력으로 보내고 싶다면 설명자 5를 1로 이동하면 됩니다. 이것이 파일의 출처 2> errors이며 구조는 2>&1오류를 출력 스트림에 복사하는 것을 좋아합니다.

따라서 거의 사용하지 않았지만(25년 이상 Unix를 거의 독점적으로 사용하면서 화가 나서 한두 번 사용했던 기억이 막연하게 기억나지만) 필요할 때는 꼭 필요합니다.

관련 정보