「*」(アスタリスク)を含む配列要素の自動拡張の問題

「*」(アスタリスク)を含む配列要素の自動拡張の問題

find後で外部ファイルから除外するディレクトリのリストを読み取ることができるスクリプトを作成しようとしています。この部分は私が直接することができますが、面倒なアレイ拡張のために作業が難しくなります。まず、適切なサンプルディレクトリツリーを取得するには、いくつかの「準備」を実行する必要があります。

$ mkdir tmp && cd tmp
$ mkdir excl1_dirx excl2_dirx excl3_dirx
$ touch excl1_dirx/dummy1.txt excl2_dirx/dummy2.txt excl3_dirx/dummy3.txt
$ mkdir excl1_diry excl2_diry excl3_diry
$ touch excl1_diry/dummy4.txt excl2_diry/dummy5.txt excl3_diry/dummy6.txt
$ touch dummy00.txt dummy01.txt

スクリプトが有効な場合とのみdummy00.txt表示されます。dummy01.txt

#!/bin/bash
excl_d=("excl*_dirx" "excl*_diry")

find_str=" . -type f ! ( "

for ((i=0 ; i<$((${#excl_d[*]})) ; i++)); do
   if [[ $i > 0 ]]; then
     find_str+=" -o "
   fi

  find_str+=" -path \"./${excl_d[i]}/*\""
done

find_str+=" )"

# this is just for debugging
echo "[debug] value of str = find $find_str"

find $find_str

まず、done「」の前の行がなぜそんなに(見かけ上)複雑ですか?まあ、bash時にはユーザーが期待していないことをしてユーザーをいじめるのが大好きです。この引用がない場合拡張各配列要素の例は(明らかに)境界を破るものexcl*_dirxです。各配列要素内で一対の二重引用符を使用していますが、これは実際には bash が拡張の無駄を避けるためです。excl1 dirx excl2 dirx excl3 dirx-path

しかし、最良の結果はまだ出ていません。最後の2行目(エスケープされている場合( ))は\( \)シェルではうまく機能しますが、スタンドアロンスクリプトでは機能しません。エラーが発生しなくても、後者の実装結果は間違っています。

一重引用符と二重引用符を使用してさまざまな組み合わせを試しました。

find_str+=" -path \"./${excl_d[i]}/*\""

さて、しかし、2番目から最後の行に表示するときに完全に完璧に見えても動作することはできません。bash内部的にエスケープされた引用符とエスケープされていない引用符を別々に扱うようです\"。ああ、引用符の中の引用符はほぼどこでも動作します。しかし、何らかの理由でこの演算子を使用すると、これはフィルタリングされます+=

私はこの動作の説明だけでなく、スタンドアロンスクリプトでそれを操作する方法に関するソリューションも探しています。これは私が犯した愚かな間違いであるに違いありません。 :-/

ベストアンサー1

ここで問題は、find_str文字列を取得して文字列のリストとして使用することです。jw013さんの意見が正しい、そして読んでくださいコマンドを変数に入れようとしましたが、複雑な場合は常に失敗します!。コマンド全体を変数に入れませんが、複数の単語を文字列変数に入れようとすると問題が発生します。

Bourne / POSIXシェルではこれが必要です。しかし、ksh/bash/zshにはより良い方法があります。すぐに配列を使用することです。

#!/bin/bash
excl_d=("excl*_dirx" "excl*_diry")
find_str=( . -type f \! \( )
for ((i=0 ; i<$((${#excl_d[*]})) ; i++)); do
   if [[ $i > 0 ]]; then
     find_str+=( -o )
   fi
  find_str+=( -path "./${excl_d[i]}/*" )
done
find_str+=( \) )
find "$find_str[@]}"

これらのフィルタを表現するより簡単な方法があります。

#!/bin/bash
exclude_patterns=("excl*_dirx" "excl*_diry")
exclude_args=()
while [[ ${#exclude_patterns} -gt 0 ]]; do
  exclude_args+=( -path "./${exclude_patterns[1]}/*" -prune -o )
  shift exclude_patterns
done
find "${exclude_args[@]}" -type f

おすすめ記事