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
答え2
あなたのため:
#!/bin/bash
excl_d=("excl*_dirx" "excl*_diry")
find_str=". "
for ((i=0 ; i<$((${#excl_d[*]})) ; i++)); do
if [[ $i > 0 ]]; then
find_str+=" -o "
fi
find_str+=" -path \"./\${excl_d[i]}\" -prune "
done
find_str+=" -o -type f -print "
# this is just for debugging
echo "[debug] value of str = find $find_str"
eval "find ${find_str}"
ここでの問題は、bashがスクリプトの1行を実行すると、1つの引数find ${find_str}
でのみfindを実行することです。つまり、findの主関数のargc引数は2に等しくなります。eval
代わりに、文字列を形成し、bashでトークン化を渡してください。