
次のスクリプトでは、最初ののためにループは期待どおりに実行されますが、2 番目は実行されません。エラーは表示されませんが、スクリプトがハングしているだけのようです。
HOME=/root/mydir
DIR=$HOME/var
DIRWORK=$HOME/Local
for f in $(find $DIR -type f); do
lsof -n $f | grep [a-z] > /dev/null
if [ $? != 0 ]; then
echo "hi"
fi
done
for i in $(find $DIRWORK -type d -name work); do
echo "2"
done
答え1
スクリプトは危険な方法でコーディングされています。
まず、「/bash」と「/for」のタグが付いているので、Bash シェルを使用していると想定します。
私の答えでは、この素晴らしいBash ガイドは、おそらく Bash を学ぶのに最適な情報源です。
1)決して使用しないでくださいコマンド置換、 のどちらか種類は引用符なしです。ここで大きな問題があります。引用符なしの展開を使用して出力を引数に分割します。
具体的には、これ$(find $DIRWORK -type d -name work)
は$(find $DIR -type f)
単語の分割したがって、find
名前にスペースが含まれるファイル (つまり「ファイル名」) が見つかった場合、Bash の単語分割結果によって、コマンドfor
が反復処理する 2 つの引数 (つまり「ファイル」用と「名前」用) が渡されます。この場合、実際に存在する場合に損害を与える可能性を回避するために、「ファイル: そのようなファイルまたはディレクトリはありません」および「名前: そのようなファイルまたはディレクトリはありません」という結果が返されることを期待します。
2)慣例により、環境変数 (PATH、EDITOR、SHELL など) と内部シェル変数 (BASH_VERSION、RANDOM など) はすべて大文字で表記されます。その他の変数名はすべて小文字で表記されます。変数名は大文字と小文字が区別されるため、この慣例により環境変数と内部変数が誤って上書きされることが回避されます。
$DIRWORK ディレクトリは規則に違反しており、引用符も付いていません。したがって、$DIRWORK が引用符で囲まれていない場合、 letDIRWORK='/path/to/dir1 /path/to/dir2'
はfind
2 つの異なるディレクトリを参照します。引用符の使用は Bash では非常に重要なので、「二重引用符」を使用する必要があります。毎展開、および特殊文字を含む可能性のあるもの(例:"$var"、"$@"、"${array[@]}"、"$(command)")も含みます。Bashは、'一重引用符'で囲まれたすべてをリテラルとして扱います。'と"と`の違いを学びましょう。引用、引数また、このリンクもご覧ください:http://wiki.bash-hackers.org/syntax/words
これはスクリプトのより安全なバージョンなので、代わりにこちらを使用することをお勧めします。
my_home="/root/mydir"
my_dir="$my_home/var"
dir_work="$my_home/Local"
while IFS= read -r -d '' f; do
# I'm guessing that you also want to ignore stderr;
# this is where the 2>&1 came from.
if lsof -n "$f" | grep '[a-z]' > /dev/null 2>&1; then
echo "hey, I'm safer now!"
fi
done < <(find "$dir_work" -type f -print0)
while IFS= read -r -d '' f; do
echo "2"
done < <(find "$dir_work" -type d -name 'work' -print0)
ご覧のとおり、IFS
変数は空に設定されているため、read
行の先頭と末尾のスペースが切り取られることはありません。read
コマンドは、空の文字列 ( -d ''
) を区切り文字として使用し、\0 に達するまで読み取ります。
find
それに応じて変更する必要があるため、-print0
新しい行ではなく \0 でデータを区切るオプションを使用します。驚くべきことに、これはファイル名の一部になることがあります。このようなファイルを \n で 2 つに分割すると、コードが壊れます。
以下について読んでみてくださいプロセス置換私のスクリプトを完全に理解できない場合は。
find ... | while read name; do ...; done
出力の読み取りに使用する必要があると述べている以前の回答find
も、正しくない可能性があります。while
上記のループは、親からコピーされた変数の独自のコピーを使用して、新しいサブシェルで実行されます。このコピーは、好きなように使用できます。ループがwhile
終了すると、サブシェルのコピーは破棄され、親の元の変数は変更されません。
このwhile
ループ内でいくつかの変数を変更し、後で親で使用することを目的としている場合は、データ損失を防ぐ上記のより安全なスクリプトの使用を検討してください。
答え2
このコード
for i in $(find $DIRWORK -type d -name work); do
echo "2"
done
まずこの行を実行します
find $DIRWORK -type d -name work
実行が完了するまで待機しfind
、出力を取得してfor
ループに戻します。
for i in the output of find; do
echo "2"
done
そうして初めて for ループの実行が開始されます。
したがって、ループfind
が完了するまでに長い時間がかかる場合for
、ループは開始されるまで長い時間待機する必要があります。
find
対話型プロンプトでコマンドの時間を計ってみる
$ time find $DIRWORK -type d -name work
そして、それがどのくらい時間がかかるか見てみましょう。
for
また、注意:ファイル名をループするのにループを使用しないでください。次のようwhile
にループを使用します。read
find $DIRWORK -type d -name work | while read name; do
echo "2"
done
読むこれ詳細については。
ボーナス: これはwhile
と並行してループを実行しますfind
。つまり、が 1 行を出力するwhile
とすぐに、ループは 1 回の反復を実行しますfind
。 が実行を終了するのを待つ必要はありませんfind
。