이 코드 조각의 출처는 다음과 같습니다.고급 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
ls
fd 3에 아무 것도 열 필요가 없으므로 grep
(그 fd를 사용하지 않음) 닫는 것이 좋습니다(불필요한 리소스를 해제함). 우리는 fd 3을 사용하여 ls
stdout을 원래 출력으로 복원합니다(실행 전 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/
두 번째 호출에서는 ls
fd 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