stderr만 파이프로 리디렉션

stderr만 파이프로 리디렉션

이 코드 조각의 출처는 다음과 같습니다.고급 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.

# Thanks, S.C.

주석에서는 코드가 "grep"에 대해 fd 3만 끄는 것으로 설명합니다. 그러나 fd 3을 두 번 닫습니다. 왜 우리는 이것을 해야 합니까? 이렇게 'grep'에 대해 fd 3을 한 번만 닫는 것이 잘못된 것인가요?

ls -l 2>&1 >&3 | grep bad 3>&-    

답변1

lsfd 3에 아무 것도 열 필요가 없으므로 grep(그 fd를 사용하지 않음) 닫는 것이 좋습니다(불필요한 리소스를 해제함). 우리는 fd 3을 사용하여 lsstdout을 원래 출력으로 복원합니다(실행 전 ls).

파이프라인에서는 모든 명령이 자체 프로세스에서 동시에 실행된다는 점을 기억하세요. 리디렉션은 각각에 개별적으로 적용되므로 하나의 fd 3을 닫으면 다른 하나의 fd 3을 닫을 수 없습니다.

이 경우 닫지 않으면 파일 설명자 수 제한에 더 빨리 도달할 수 있다는 점을 제외하면 닫거나 닫지 않아도 실제로 큰 차이가 없습니다.

다른 경우(예: 명령 자체가 다른 프로세스를 시작하는 경우) 이로 인해 리소스가 묶이고 문제가 발생할 수 있습니다. 예를 들어, stdout이 파이프인 경우 백그라운드 프로세스는 결국 fd를 상속하게 되어 프로세스가 반환될 때까지 파이프 판독기에서 EOF를 볼 수 없게 됩니다.

더 나은 작성 방법은 다음과 같습니다.

{ ls -l 2>&1 >&3 3>&- | grep bad 3>&-; } 3>&1

이런 방식으로 fd 3은 해당 명령 그룹 기간 동안 일시적으로만 리디렉션됩니다(그리고 나중에 복원되거나 닫힙니다).

차이점을 확인하세요:

$ { ls -l /proc/self/fd 2>&1 >&3 3>&- | grep bad 3>&-; } 3>&1
total 0
lrwx------ 1 stephane stephane 64 Apr  2 09:29 0 -> /dev/pts/4
lrwx------ 1 stephane stephane 64 Apr  2 09:29 1 -> /dev/pts/4
l-wx------ 1 stephane stephane 64 Apr  2 09:29 2 -> pipe:[575886]
lr-x------ 1 stephane stephane 64 Apr  2 09:29 3 -> /proc/20918/fd/
$ { ls -l /proc/self/fd 2>&1 >&3  | grep bad 3>&-; } 3>&1
total 0
lrwx------ 1 stephane stephane 64 Apr  2 09:29 0 -> /dev/pts/4
lrwx------ 1 stephane stephane 64 Apr  2 09:29 1 -> /dev/pts/4
l-wx------ 1 stephane stephane 64 Apr  2 09:29 2 -> pipe:[575900]
lrwx------ 1 stephane stephane 64 Apr  2 09:29 3 -> /dev/pts/4
lr-x------ 1 stephane stephane 64 Apr  2 09:29 4 -> /proc/20926/fd/

두 번째 호출에서는 lsfd 3도 아무 이유 없이 터미널에 열립니다(파이프를 실행할 때 stdout에서 열립니다).

ksh93에서는 를 사용하면 exec해당 fd를 닫을 필요가 없습니다. 0, 1, 2 이외의 fd는 명령 실행 시 자동으로 닫히기 때문입니다.

$ ksh93 -c 'exec 3>&1; ls -l /dev/fd/'
total 0
lrwx------ 1 stephane stephane 64 Apr  2 09:34 0 -> /dev/pts/16
lrwx------ 1 stephane stephane 64 Apr  2 09:34 1 -> /dev/pts/16
lrwx------ 1 stephane stephane 64 Apr  2 09:34 2 -> /dev/pts/16
lr-x------ 1 stephane stephane 64 Apr  2 09:34 3 -> /proc/21105/fd
$ ksh93 -c 'exec 3>&1; ls -l /dev/fd/ 3>&3'
total 0
lrwx------ 1 stephane stephane 64 Apr  2 09:34 0 -> /dev/pts/16
lrwx------ 1 stephane stephane 64 Apr  2 09:34 1 -> /dev/pts/16
lrwx------ 1 stephane stephane 64 Apr  2 09:34 2 -> /dev/pts/16
lrwx------ 1 stephane stephane 64 Apr  2 09:34 3 -> /dev/pts/16
lr-x------ 1 stephane stephane 64 Apr  2 09:34 4 -> /proc/21108/fd

왜 그런 말이 나오는지 모르겠어요(그러나 "ls"는 아님)위의 내용은 버그인 것 같습니다(아마도 내 ;-).

답변2

fd3스크립트에서 두 개의 하위 쉘을 분기했기 때문에 두 번 닫으십시오 . 각각은 상위 파일 설명자를 상속하고 복사합니다. 각 하위 셸 내의 종료는 fd3다른 하위 셸(상위 셸 포함)에 영향을 주지 않습니다.

따라서 주석 줄은 매우 불분명하고 오해의 소지가 쉽습니다.

stder파이프로 리디렉션하려는 경우 다음을 사용할 수 있습니다 process substitution.

ls -l 2> >(grep bad)

또는 교환 stderr하고 stdout:

ls -l 3>&1 1>&2 2>&3 | grep bad

답변3

"고급 Bash 스크립팅 가이드"의 코드 조각을 이해하기보다는 질문 요약 때문에 여기에 온 다른 사람들의 경우 다른 답변을 추론하기 어렵다는 것을 알았습니다. 다음 방법은 내 목적에 적합하며 그 의도를 읽고 이해하는 것이 더 쉽습니다.

$ ls -l 2>&1 1>&- | grep bad

나에게 이것은 stdout그것을 닫아 무시하고 다음 명령으로 파이프할 수 있도록 리디렉션 stderr하고 싶다는 것을 의미합니다.stdout

관련 정보