ファイル名の生成に失敗した場合、zshがスクリプトの処理を停止するのはなぜですか?

ファイル名の生成に失敗した場合、zshがスクリプトの処理を停止するのはなぜですか?

私は次に見つかったすべての実行可能プログラムを書くための短いスクリプトを書こうとしています$PATH

for dir in $(tr ':' ' ' <<<"${PATH}"); do
  for pgm in $dir/*; do
    if command -v "${pgm}" >/dev/null 2>&1; then
      echo "${pgm}"
    fi
  done
done | sort >file

Bashでは期待どおりに機能しますが、内部ループでファイル名の生成が失敗した場合、zshはスクリプトの処理を停止します。

for pgm in $dir/*; do
           ^^^^^^
  ...
done

その結果、myには$PATHファイル()を含まないディレクトリが含まれているため、/usr/local/sbinzshではスクリプトは後でディレクトリにある実行可能ファイルに書き込むことができません。

同じ問題を示す別のコードは次のとおりです。

for f in /not_a_dir/*; do
  echo 'in the loop'
done
echo 'after the loop'

Bashでは、このコマンドは次のように出力します。

in the loop
after the loop

コードを使用して終了します0

zsh では同じコマンドが出力されます。

no matches found: /not_a_dir/*

コードを使用して終了します1

シェル間の動作の違いは、以下のnomatchようにoptionsから来ているようですman zshoptions

一致しない(+3)<C> <Z>

ファイル名生成パターンに一致する項目がない場合は、引数リストに変更せずにそのままにする代わりにエラーが出力されます。これは初期~にも適用されます=

man zshexpn(ファイル名生成セクション)に記載されています。

その単語は、パターンに一致するソートされたファイル名のリストに置き換えられます。一致するパターンが見つからない場合、NULL_GLOB オプションが設定されて単語が削除されるか、NOMATCH オプションが設定解除されて単語が変更されない限り、シェルはエラーメッセージを表示します。

設定を解除すると、nomatchzsh は bash のように動作します。

unsetopt nomatch
for f in /not_a_dir/*; do
  echo 'in the loop'
done
echo 'after the loop'

bashとzshの動作の違いとスクリプトがzshでエラーを引き起こす理由を理解していますが、ファイル名の生成に失敗した場合はzshがすぐにスクリプトの処理を停止する理由を理解したいと思います。そのため、失敗したファイル名の生成を失敗したコマンドに置き換えて、同じ問題を(実行を通じて)再現しようとしましたnot_a_cmd

for f in ~/*; do
  not_a_cmd
done
echo 'after the loop'

ただし、スクリプトの出力は両方のシェルでほぼ同じです(エラーメッセージを除くnot_a_cmd)。特に、両方のシェルは以下を印刷します。

after the loop

両方のシェルはコードで終了します0

失敗したファイル名の生成(例for f in /not_a_dir/*:)によってzshがスクリプトの処理を停止しますが、失敗したコマンド(例not_a_cmd:)が停止しないのはなぜですか?

私はそれを使用していますzsh 5.6.2-dev-0 (x86_64-pc-linux-gnu)

答え1

これはバグではなく機能です。

関連情報