zsh에서
rm foo.bar
인쇄합니다rm: foo.bar: No such file or directory
.rm foo.bar 2>/dev/null
예상대로 아무것도 인쇄되지 않습니다.
그러나 명령에 패턴 일치가 포함되어 있으면 다음을 통해 오류가 억제되지 않습니다 2>/dev/null
.
rm *.bar
인쇄합니다zsh: no matches found: *.bar
.rm *.bar 2>/dev/null
같은 것을 인쇄합니다.
zsh에서 오류 메시지를 억제하는 일반적인 방법이 있습니까? 하나단순한모든 종류의 오류 메시지에 대한 방법입니다.
답변1
실행하려고 하는데 rm *.bar
일치하는 파일이 없으면 *.bar
기본적으로 두 가지 일이 발생할 수 있습니다.
POSIX 동작은 쉘이
rm
리터럴을*.bar
인수로 사용하여 실행되는 것입니다. 이 도구는 문자 그대로 이름이 지정된 파일을 제거하려고 시도하지만*.bar
(를 실행하는 것처럼rm '*.bar'
) 실패하고 다음과 같은 내용을 인쇄합니다rm: *.bar: No such file or directory
. 이것이 POSIX 쉘(sh
)과 호환 쉘이 작동하는 방식입니다.POSIX가 아닌 동작은 쉘이 일치하는 항목이 없음을 감지하고 다음과 같은 것을 인쇄하고
no matches found: *.bar
실행되지 않는 것입니다.rm
조금도. 이것이 Zsh가 기본적으로 작동하는 방식입니다. (비교를 위해 Bash에서는 를 사용하여 이 동작으로 전환할 수 있습니다shopt -s failglob
.)
(이전 경우) 의 오류가 의 rm
stderr에 인쇄됩니다 rm
. 쉘의 오류(후자의 경우)는 쉘의 표준 오류에 인쇄됩니다.
리디렉션은 +rm *.bar 2>/dev/null
의 표준 오류 rm
에만 영향을 미칩니다 . 귀하의 예에서는 실행되지도 않습니다.rm
메인 쉘의 stderr을 리디렉션하려면 exec
. "메인 셸"이란 사용자가 입력하는 대화형 셸을 의미합니다. 또는 스크립트를 실행하는 경우 스크립트를 해석하는 쉘입니다. 예:
exec 2>/dev/null
Zsh에서는 닫을 수도 있습니다.
exec 2>&-
합리적인 접근 방식은 나중에 사용(또는 복원)해야 하는 경우를 대비하여 원본 stderr을 미리 복제하는 것입니다. 예(이 코드를 대화형 Zsh에 붙여넣으려면 다음을 호출해야 합니다.setopt interactive_comments
첫 번째):
exec 7>&2 # "save" stderr
exec 2>/dev/null # redirect
rm *.bar
whatever
exec 2>&7 # restore
exec 7>&- # close descriptor which is no longer needed
다음은 7
임의의 한 자리 숫자입니다. 그렇지 않으면 파일 설명자로 사용되지 않습니다. 처음 두 줄은 하나로 쓸 수 있습니다: exec 7>&2 2>/dev/null
; 마찬가지로 마지막 두 줄도 가능합니다.
stderr 리디렉션 없이 호출된 참고 rm
또는 모든 명령(예: whatever
)은 셸에서 stderr을 상속합니다. 이는 위의 예 rm
(실행된 경우) 를 의미하며 whatever
오류 메시지(있는 경우)를 에 인쇄합니다 /dev/null
. 임의의 숫자나 명령을 호출 하면 exec 2>/dev/null
모두 /dev/null
stderr로 갖게 됩니다. 모든 종류의 오류 메시지를 정말로 표시하지 않으려면 이 방법을 사용하세요.
이 솔루션은 Zsh뿐만 아니라 많은 셸에서 작동합니다. 대화형 셸의 stderr를 리디렉션하려면 덜 정교한 일부 셸에서는 stderr을 사용하여 프롬프트를 인쇄한다는 점을 명심하세요.
프로세스는 자체 표준 오류를 리디렉션할 수 있습니다(셸에서 처럼 exec 2>…
). 또는 stdout이나 to로 오류 메시지를 인쇄할 수 있습니다 /dev/tty
(그렇지 않아야 하지만 기술적으로는 가능합니다). 그러므로exec 2>/dev/null
~ 아니다오류 메시지처럼 보이는 메시지가 표시되지 않도록 보장합니다.
그 후에도 exec 2>/dev/null
요청 시 모든 명령의 stderr를 리디렉션할 수 있습니다. 예를 들어 다음과 같은 경우 whatever
가 있습니다.
whatever 2>&7
그러면 오류 메시지는 의도적으로 파일 설명자로 저장한 원본 stderr로 이동됩니다 7
.
exec
쉘의 stderr(또는 다른 파일 설명자)를 리디렉션하는 유일한 방법은 아닙니다. rm
에서 처리하는 것처럼 쉘(전체 메인 쉘은 아님)을 처리할 수 있습니다 rm … 2>/dev/null
. 서브쉘을 다음과 같이 처리할 수 있습니다:
( rm *.bar ) 2>/dev/null
또는 메인 셸에서 해석되는 일부 코드일 수도 있습니다.
{ rm *.bar; } 2>/dev/null
( ;
여기서는 Zsh에서는 선택 사항이고 다른 일부 셸에서는 필수입니다. 코드가 Zsh 외부에서도 작동하기를 원했습니다.)
이 라인 중 하나가 문제를 해결할 수 있습니다. 쉘에서 나오는 오류 메시지를 억제하는 맥락에서 두 줄은 ++ 와 동일합니다 .
리디렉션은 (
/ 에서 시작하고 / {
에서 끝나며 범위가 제한됩니다. 여전히 괄호 안에 많은 명령을 넣을 수 있으므로 "제한된" 범위는 실제로 전체 스크립트일 수 있습니다. 또는 스크립트의 일부일 수도 있습니다( 이 답변의 앞부분에 소개된 내용 포함). / 이후 리디렉션이 작동하지 않는다는 사실 은 이 접근 방식을 .)
}
whatever
)
}
exec
+rm
엄밀히 말하면, 가 되기 전과 후에 " "에 영향을 미칩니다 rm
. 참아주세요. 리디렉션은 rm … 2>/dev/null
다음에 의해 수행되는 리디렉션으로 시작됩니다.rm
실행 파일 로 자신을 대체하려는 분기된 셸. 이 쉘은 자신을 rm
. 리디렉션을 수행한 후 보고되는 모든 오류는 로 이동됩니다 /dev/null
. 예를 들어 rm
찾을 수 없는 경우 이미 수행된 리디렉션은 command not found
오류를 억제합니다.
++ 기술적으로 ( … )
서브쉘을 생성 { … }
하지 않습니다. 서브셸은 변수와 현재 작업 디렉터리를 상속하는 별도의 셸 프로세스처럼 동작하지만 상위 셸에서 아무것도(변수나 현재 작업 디렉터리 등) 변경할 수 없습니다. 이는 완전히 별도의 프로세스로 실현될 수도 있고 실현되지 않을 수도 있습니다. 중요한 것은 동작입니다. OTOH는 { … }
특정 목적으로만 명령을 그룹화합니다(우리의 경우 목적은 리디렉션입니다). 이는 사용할 때 하위 쉘이 전혀 없다는 의미는 아닙니다 { … }
. 예를 들어 파이프라인에서 마지막 부분을 제외한 모든 부분은 어쨌든 서브쉘입니다(일부 쉘에서는 마지막 부분을 포함한 모든 부분). 이러한 기술적 세부 사항은 우리 문제의 맥락에서만 관련이 없지만 일반적으로 차이를 만들 수 있습니다.