非対話型スクリプトでは、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 .

拡張は2つの段階で行われます。まず、*/*.{a,b}2 単語に拡張します。*/*.aand */*.b。その後、各単語はグローバルパターンに展開されます。最初のものdir/file.aは一致するものがないため、2番目のものは独自に拡張され、2番目のものは独自に拡張されます。これが意味するのは、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(そしてシステムを読みます/etc/zshrcが、管理者がいたずらでない限りそれは犯人ではありません)。一部を設定することもできます。オプション特にzshがコマンドを拡張する方法が修正されましたextended_glob(zshが1990年代初頭に以前のバージョンとの互換性を選択しなかった場合は、このオプションが存在しなかったときにこれがデフォルトでなければなりませんでした)。これらのオプションは、スクリプトの実行時に設定されません。

あなたの場合、コマンドはmv */*.{a,b} .最初に単語リストに解析されますmv*/*.a、、 (*/*.b.括弧拡張をオフにしない限り)。その後、ワイルドカード文字を含む各単語はグローバルパターンと見なされます。デフォルトでは、zshはglobパターンがどのファイルとも一致しない場合、コマンドは実行されず、zshからエラー信号を送信します。明らかに、対応.zshrcするnull_globオプションをオンにすると、一致しないパターンが空の単語リストに展開されます(つまり削除されます)。スクリプトを実行すると、2番目のパターンが*/*.bどのファイルとも一致しないため、表示されるエラーが発生します。コマンドを対話的に実行すると、モードは削除されます。

null_globスクリプトを使用して明示的にこのオプションを設定できますsetopt null_glob。以下を使用して特定のモードをオンにすることもできます。N グローバル予選:

mv */*.{a,b}(N) .

ここでは中括弧拡張を使用しないでください。代わりに、以下を使用してください。これまたはオペレーター(通常のshとは異なり)zshで使用できます。これは、2つのパターンではなく、2つのパターンのいずれかに一致するファイルを意味するためです。

mv */*.(a|b) .

これにより、パターンが1つだけで、その単一のパターンに一致するファイルがない場合にのみエラーが発生します。

このコマンドは、一致するファイルがまったくない場合にエラーを発生させます。(N)誤ったコマンドが実行される可能性があるため、ミュートすることはできません。mv .代わりに、最初に一致するものがあるかどうかをテストし、mv一致するものがある場合にのみ実行してください。

files=(*.(a|b)(N))
if ((#files)); then mv -- $files[@] .; fi

関連情報