Zshはfindコマンドで配列を使用する

Zshはfindコマンドで配列を使用する

rsync配列で指定されたいくつかのファイルを上書きし、ディレクトリ内の他のファイルを削除したいと思います。

私が考えられる唯一のアプローチは、、、を使用して他のファイルを削除しfindrsyncコピーするファイルをできるだけ少なくすることです。

次の例では、と/tmp/tmp/を除く内の他のすべてのファイルを削除します。btrfs_x64.efiiso9660_x64.efi

$ refind_efi_dir='/tmp/tmp/'
$ drivers=('btrfs_x64.efi' 'iso9660_x64.efi')
$ find ${refind_efi_dir}drivers_x64/ "${drivers[@]/#/! -name }" -type f -exec rm -f {} +

展開を次のコマンドに展開します。

$ find /tmp/tmp/drivers_x64/ ! -name btrfs_x64.efi ! -name iso9660_x64.efi -type f -exec rm -f {} +

しかし、代わりに次のコマンドを実行しているようです。

$ find /tmp/tmp/drivers_x64/ "! -name btrfs_x64.efi" "! -name iso9660_x64.efi" -type f -exec rm -f {} +

前者を取得する方法はありますか? 理想的には、配列エントリの一部にスペースが含まれている場合にも機能します。

答え1

findはい、ここでは配列の各要素に対して3 つの引数を生成する必要があります。またfind、 はパターンを取るため、正確なファイル名に一致させるには、ワイルドカード演算子 ( 、、 )-nameをエスケープする必要があります。find*?[\

set -o extendedglob # for (#m)
exclusions=()
for name ($drivers) exclusions+=(! -name ${name//(#m)[?*[\\]/\\$MATCH})

find ${refind_efi_dir}drivers_x64/ $exclusions -type f -exec rm -f {} +

"${array[@]/pattern/replacement}"各要素に対して置換が実行された後、配列内の要素と同じ数だけ展開されます。

ここで、-nameファイル名前パターンには が含まれていてはならない/ため、各要素を に置き換えてから で!/-name/element分割することができます/

set -o extendedglob # for (#m)
find ${refind_efi_dir}drivers_x64/ \
  ${(@s[/])${drivers//(#m)[?*[\\]/\\$MATCH}/#/!\/-name\/} \
  -type f -exec rm -f {} +

または、外部コマンドに引数として渡すことができないため、$'\0'代わりにを使用します。/

set -o extendedglob # for (#m)
find ${refind_efi_dir}drivers_x64/ \
  ${(@0)${drivers//(#m)[?*[\\]/\\$MATCH}/#/!$'\0'-name$'\0'} \
  -type f -exec rm -f {} +

しかし、それは読みやすさにはあまり役立ちません...

ここでは、zshすべてに対して のグロブを使用することもできます。

(cd -P -- $refind_efi_dir && rm -f -- **/^(${(~j[|])drivers})(D.))

パラメータj[|]拡張フラグは、配列の要素$driversを で結合し|~それを|glob 演算子として扱うようにします。このパターンは、^(オプションが必要ですextendedglob) で否定されます。をD使用すると隠しファイルを含めることができ、.のような通常のファイルに制限することができます-type f

答え2

"${drivers[@]/#/! -name }"! -name各配列要素と同じシェル ワードを配置します。 は結果"${=drivers[@]/#/! -name }"を分割するため、!と は-name別々のシェル ワードになりますが、配列要素も空白で分割されます。

一つの解決策はeP glob 修飾子:

find … /(e\''reply=($drivers)'\'P\''!'\'P\''-name'\') …

/(…)glob パターンを展開します/。これは常に 1 つの対象 (ルート ディレクトリ) に一致し、それに glob 修飾子を適用します。glob 修飾子は、e各一致を配列変数の新しい値に置き換えますreply。この配列変数は、後続の glob 修飾子を適用する単語のリストに設定します。次に、P2 回使用される glob 修飾子は、各一致の前に指定されたテキストを個別の単語として挿入します。

この特定のケースでは、ステファン・シャゼラスが示したように、find を使用せずに、zsh globs ですべてを実行できます。

関連情報