비대화형 스크립트에서 zsh 확장이 다르게 작동합니까?

비대화형 스크립트에서 zsh 확장이 다르게 작동합니까?

저는 현재 매우 간단한 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다른 옵션이 설정되었는지 여부에 따라 달라집니다. nomatchunset( )을 사용하면 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단어로 확장합니다: */*.aand */*.b. 그런 다음 각 단어는 전역 패턴으로 확장됩니다. 첫 번째 dir/file.a는 일치하는 항목이 없으므로 두 번째는 자체적으로 확장 되고 두 번째는 자체적으로 확장됩니다. 이것이 의미하는 바는 mvand not을 사용하는 경우 echo2 개의 파일( 음) 및 (해당 파일 없음) mv을 이동해야 한다는 것 입니다. 이는 및 와 같은 대부분의 셸에서 기본적으로 발생합니다.dir/file.a*/*.bshksgbash

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위에서 설명한 잘못된 동작으로 돌아가게 됩니다. 존재하지 않는 파일을 이동하려고 시도합니다.echomv

/ 명령 앞에 스크립트를 삽입 하면 setopt null_glob유효한 대화형 셸에서 얻는 동작을 얻게 됩니다.echomv

답변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

관련 정보