![find+exec から変数を設定するにはどうすればいいですか?](https://rvso.com/image/38657/find%2Bexec%20%E3%81%8B%E3%82%89%E5%A4%89%E6%95%B0%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B%E3%81%AB%E3%81%AF%E3%81%A9%E3%81%86%E3%81%99%E3%82%8C%E3%81%B0%E3%81%84%E3%81%84%E3%81%A7%E3%81%99%E3%81%8B%3F.png)
ある場所に空のファイルがあるかどうかを調べ、見つかった場合はメールを送信する bash スクリプトを作成しようとしています。最初は「find」と「mail」を組み合わせることを考えましたが、その場所に複数の空のファイルがある場合、複数のメールが送信されてしまうため、これは望ましくありません。そこで、find の前にフラグ変数を 0 に設定し、空のファイルが見つかった場合は find 内で 1 に設定することを考えました。試したのは次のとおりです。
FLAG=0
find $LOCATION -size 0 -type f -exec sh -c 'export FLAG=1' \;
echo $FLAG
しかし問題は、その場所に空のファイルがあっても、フラグ値が 1 に変更されないことです。何が間違っているのでしょうか?
答え1
set -- "${LOCATION}/"*
while [ -s "$1" ] ; do shift ; done
[ -e "$1" ] && FLAG=1
シェル組み込み関数はize 演算子[ test ]
とともに使用できます-s
。出典man test
:
-s FILE
FILE
存在し、サイズが 0 より大きい
ただし、再帰検索では簡単には機能しません。
find
できますが、ブール値を設定するという単純な問題では、このケースには適していません。 は、ls
再帰検索を同じくらい高速に実行でき、リストの最初のフィールドとしてファイル サイズを提供し、1 行に 1 つのエントリのみをリストするように簡単に構成できます (保証付き)。 再帰的なファイル リストにサイズ 0 のファイルが含まれているかどうかに基づいてシェル変数のブール値を設定する場合は、ls
が最善の策ですが、find
物事を複雑にするだけです。 必要なのはファイルのプロパティであり、ファイルの場所ではありません。 これが のls
得意分野であり、grep
その出力を ping するのは簡単です。
これは簡単にできます:
ls -1aqRsp "$LOCATION" 2>&1 | grep -qv "^ *[^0]\|/"
FLAG=$(($?==0))
これは、ファイルの場合のみFLAG
に設定されます -1
隠れた .dot
含まれるファイル -$LOCATION
またはその子ディレクトリの 1 つに存在し、サイズは 0 です。それ以外の場合は$FLAG
です0
。
答え2
find
実行すると、新しい子プロセスがフォークされます-exec
。子プロセスは親の環境を変更できません。
問題となっているファイル名を収集しfind
、2 回目のパスで必要な電子メールを生成することを検討してください。
find . -type f -size 0 -print >> /var/tmp/find.sz0
...
答え3
これは、によって呼び出されたexport FLAG=1
のインスタンス内で実行されます。プロセスの環境が親にコピーされることはありません。環境変数を設定して終了するだけのシェル プロセスは、永続的な成果を何も達成しません。sh
find
がファイルに一致するかどうかをテストしたい場合はfind
、その出力が空であるかどうかをテストするだけで済みます。長いファイル リストが生成され、空でないことが証明されたら破棄される可能性を回避するには、find
最初の機会に出力を切り捨てることができます。GNU find には述語があり-quit
、1 つの文字を表示するように簡単に指示することもできます。次のスニペットでは、FLAG
一致するものがない場合、空になります。
FLAG=$(find "$LOCATION" -size 0 -type f -printf 1 -quit \;)
次のバリアントはFLAG
0 または 1 に設定されます。
FLAG=$(find "$LOCATION" -size 0 -type f -printf 1 -quit \;)
[ -n "$FLAG" ] || FLAG=0
ポータブルなままにしたい場合は、find
印刷一致を行い、最初の行のみを保持するfind
ことができます。SIGPIPE のhead
パイプの側面を閉じた後。
if [ -n "$(find "$LOCATION" -size 0 -type f -print | head -n 1 \;)" ]; then
FLAG=1
else
FLAG=0
fi
これは単なる単純化された例であり、より複雑な変数を設定する必要がある場合、いくつかのアプローチが考えられます。ここでは一般的な方法をいくつか挙げます。
find
Zshには、組み込みのユースケースがたくさんあります。glob 修飾子次のスニペットは、files
に一致するファイルのリストを設定しますfind $LOCATION -size 0 -type f
。
files=(**/*(.DL0))
Ksh と bash も、それぞれ と で有効にすると再帰 glob を持ちますset -o globstar
。shopt -s globstar
ただし、bash 4.2 までは**
ディレクトリへのシンボリック リンクに再帰しますが、これは通常望ましくありません。シェルでさらに処理を行うことができます。
FIGNORE= # include dot files in wildcard matches (the ksh way)
GLOBIGNORE=.:.. # include dot files in wildcard matches (the bash way)
for x in **/*; do
done
find
一致するファイル名に改行が含まれていない場合は、出力を改行で区切られたリストとして解析できます。
find "$LOCATION" -size 0 -type f ! -name '*
*' -print | {
while read -r empty_file; do
…
done
}