find+exec から変数を設定するにはどうすればいいですか?

find+exec から変数を設定するにはどうすればいいですか?

ある場所に空のファイルがあるかどうかを調べ、見つかった場合はメールを送信する 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のインスタンス内で実行されます。プロセスの環境が親にコピーされることはありません。環境変数を設定して終了するだけのシェル プロセスは、永続的な成果を何も達成しません。shfind

がファイルに一致するかどうかをテストしたい場合はfind、その出力が空であるかどうかをテストするだけで済みます。長いファイル リストが生成され、空でないことが証明されたら破棄される可能性を回避するには、find最初の機会に出力を切り捨てることができます。GNU find には述語があり-quit、1 つの文字を表示するように簡単に指示することもできます。次のスニペットでは、FLAG一致するものがない場合、空になります。

FLAG=$(find "$LOCATION" -size 0 -type f -printf 1 -quit \;)

次のバリアントはFLAG0 または 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

これは単なる単純化された例であり、より複雑な変数を設定する必要がある場合、いくつかのアプローチが考えられます。ここでは一般的な方法をいくつか挙げます。

findZshには、組み込みのユースケースがたくさんあります。glob 修飾子次のスニペットは、filesに一致するファイルのリストを設定しますfind $LOCATION -size 0 -type f

files=(**/*(.DL0))

Ksh と bash も、それぞれ と で有効にすると再帰 glob を持ちますset -o globstarshopt -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
}

関連情報