diffを使用したコマンドで変数が拡張されない

diffを使用したコマンドで変数が拡張されない

私のシェルはzshです。 Bashでこのコマンドを試しましたが、うまくいきました。

ignore_files=(
    local
    \*.local.\*
    .bundle
    Gemfile.lock
    )
ignore_command=""
for f in "${ignore_files[@]}"; do
    ignore_command="$ignore_command -x $f"
done
echo $ignore_command 
# print: -x local -x *.local.* -x .bundle -x Gemfile.lock 

sudo diff -ruNd  $ignore_command <path a> <path-b> 
# failed: diff: extra operand '<path-b>'


echo sudo diff -ruNd  $ignore_command <path a> <path-b> 
# sudo diff -ruNd  -x local -x *.local.* -x .bundle -x Gemfile.lock <path a> <path-b>

eval diff -ruNd  $ignore_command <path a> <path-b> # success

変数がコマンドで拡張されていないようです。 evalなしでbashのようにする理由と方法を知りたいです。

ベストアンサー1

コマンドはパラメータのリストであり、配列を使用してこれらのパラメータを保存しようとします。

ignore_files=(
    local
    '*.local.*'
    .bundle
    Gemfile.lock
)
ignore_command=()
for f in "${ignore_files[@]}"; do
    ignore_command+=(-x "$f")
done
print -r -- "${(q+)ignore_command[@]}"
sudo diff -ruNd  "${ignore_command[@]}" <path a> <path-b> 

Bourneのようなシェルの動作のために混乱する可能性があります。ここで引用符で囲まれていない引数拡張は分割+グローブ演算子なので、文字列/スカラー変数をリスト(またはパターンに一致するファイル)に変換します。拡張後

この誤った動作は、zshのような他のほとんどの最新のシェルによって修正されましたrcesfish

を使用してBourneシェルの動作をシミュレートできますが、set -o shwordsplit -o globsubstこれは回帰を引き起こします。特にあなたの場合、グローバル文字が含まれているためglobsubst問題が発生します。*.local.*

zsh変数を分割したり、拡張時にワイルドカードを実行したりするには、明示的に要求する必要があります。

  • $=var、文字$varで区切ります$IFS
  • ${(s:,:)var}特定の文字列に基づいて分割(ここ,
  • $~var:拡張時にワイルドカードの指定(またはコンテキストに応じてパターンマッチング)を行います。 (たとえば、Contains$varの場合、リストコンテキストはBourneシェルのように現在のディレクトリのファイルリストに展開されます。)*$~var$var
  • $=~var$var:Bourneシェルと同様に、分割とワイルドカードを同時に実行します。

この特別なケースでは、次のこともできます。

sudo diff -ruNd  --exclude=$^ignore_files <path a> <path-b>

^アレイがinfishまたは...rcのように拡張されている場合は、実際にはアクティブ化などの対応する拡張オプションを--exclude=element1 --exclude=element2有効にします)。rcexpandparam~globsubst= shwordsplit

おすすめ記事