저는 현재 매우 간단한 zsh 스크립트를 작성 중입니다. 내가 보통 하는 일은 다음과 같습니다.
mv */*.{a,b} .
zsh 스크립트에서 실행하면 크기가 다르게 조정되고 대화형 모드에서 작업할 때 실패합니다.
% mkdir dir
% touch dir/file.a
% ls file.a
ls: cannot access file.a: No such file or directory
% mv */*.{a,b} .
% ls file.a
file.a
따라서 이것은 작동하지만 스크립트로는 다음과 같습니다.
% mkdir dir
% touch dir/file.a
% ls file.a
ls: cannot access file.a: No such file or directory
% cat script.sh
#!/usr/bin/zsh
mv */*.{a,b} .
% ./script.sh
./script.sh:2: no matches found: */*.b
그렇다면 차이점은 무엇입니까? 내가 뭘 잘못했나요?
답변1
두 가지 모두에 대한 기본 옵션 설정이 잘못되었습니다 zsh
. 대신 as 명령을 사용하면 echo
무슨 일이 일어나고 있는지 쉽게 확인할 수 있습니다 mv
.
null_glob
대화형으로 옵션을 설정한 것처럼 보입니다 . zsh
설명서 에 따르면 이 옵션은 기본적으로 설정되어 있지 않습니다. 이 옵션을 설정 해제하면 어떻게 됩니까? nomatch
다른 옵션이 설정되었는지 여부에 따라 달라집니다. nomatch
unset( )을 사용하면 nonomatch
다음과 같은 결과를 얻을 수 있습니다.
% mkdir dir
% touch dir/file.a
% ls file.a
ls: cannot access file.a: No such file or directory
% echo */*.{a,b} .
dir/file.a */*.b .
확장은 두 단계로 이루어집니다. 먼저 */*.{a,b}
2단어로 확장합니다: */*.a
and */*.b
. 그런 다음 각 단어는 전역 패턴으로 확장됩니다. 첫 번째 dir/file.a
는 일치하는 항목이 없으므로 두 번째는 자체적으로 확장 되고 두 번째는 자체적으로 확장됩니다. 이것이 의미하는 바는 mv
and not을 사용하는 경우 echo
2 개의 파일( 음) 및 (해당 파일 없음) mv
을 이동해야 한다는 것 입니다. 이는 및 와 같은 대부분의 셸에서 기본적으로 발생합니다.dir/file.a
*/*.b
sh
ksg
bash
zsh 기본 옵션 설정은 null_glob
설정되지 않고 nomatch
설정됩니다. 스크립트는 기본 옵션 설정으로 실행됩니다( 실제로 ~/.zshenv
변경해서는 안 되는 또는 에서 변경 /etc/zshenv
하지 않는 한). 이는 스크립트에서 다음을 얻음을 의미합니다.
% mkdir dir
% touch dir/file.a
% ls file.a
ls: cannot access file.a: No such file or directory
% cat script.sh
#!/usr/bin/zsh
echo */*.{a,b} .
% ./script.sh
./script.sh:2: no matches found: */*.b
일치하는 항목이 없으므로 */*.b
오류가 발생합니다 nomatch
.
/command 앞에 스크립트를 삽입 하면 setopt nonomatch
위에서 설명한 잘못된 동작으로 돌아가게 됩니다. 존재하지 않는 파일을 이동하려고 시도합니다.echo
mv
/ 명령 앞에 스크립트를 삽입 하면 setopt null_glob
유효한 대화형 셸에서 얻는 동작을 얻게 됩니다.echo
mv
답변2
zsh를 대화식으로 실행하면 ~/.zshrc
(및 system 을 읽습니다 /etc/zshrc
. 그러나 관리자가 장난스럽지 않다면 그게 범인이 아닙니다). 일부를 설정할 수도 있습니다.옵션특히 zsh가 명령을 확장하는 방식이 수정되었습니다 extended_glob
(zsh가 1990년대 초반에 이전 버전과의 호환성을 선택하지 않았다면 이 옵션이 존재하지 않았을 때 이것이 기본값이었어야 했습니다). 이러한 옵션은 스크립트를 실행할 때 설정되지 않습니다.
귀하의 경우 명령은 mv */*.{a,b} .
먼저 단어 목록으로 구문 분석됩니다 mv
. */*.a
, */*.b
, .
(중괄호 확장을 끄지 않는 한). 그런 다음 와일드카드 문자를 포함하는 각 단어는 전역 패턴으로 간주됩니다. 기본적으로 zsh에서는 glob 패턴이 어떤 파일과도 일치하지 않으면 명령이 실행되지 않고 zsh에서 오류 신호를 보냅니다. 분명히 당신은 .zshrc
해당 null_glob
옵션을 켜서 일치하지 않는 패턴이 빈 단어 목록으로 확장됩니다(즉, 제거됩니다). 스크립트를 실행하면 두 번째 패턴이 */*.b
어떤 파일과도 일치하지 않아 표시되는 오류가 발생합니다. 명령을 대화형으로 실행하면 모드가 제거됩니다.
null_glob
스크립트를 사용하여 명시적으로 이 옵션을 설정할 수 있습니다 setopt null_glob
. 다음을 사용하여 특정 모드에 대해 켤 수도 있습니다.N
글로벌 예선:
mv */*.{a,b}(N) .
여기서는 중괄호 확장을 사용하면 안 됩니다. 대신에 다음을 사용하세요.이것또는운영자(일반 sh와는 달리) zsh에서 사용할 수 있습니다. 두 패턴이 아니라 두 패턴 중 하나와 일치하는 파일을 의미하기 때문입니다.
mv */*.(a|b) .
이렇게 하면 패턴이 하나만 있고 해당 단일 패턴과 일치하는 파일이 없는 경우에만 오류가 발생합니다.
이 명령은 일치하는 파일이 전혀 없으면 오류를 발생시킵니다. (N)
잘못된 명령이 실행될 수 있으므로 음소거할 수는 없습니다 . mv .
대신, 먼저 일치하는 항목이 있는지 테스트하고 mv
일치하는 항목이 있는 경우에만 실행하세요.
files=(*.(a|b)(N))
if ((#files)); then mv -- $files[@] .; fi