フォルダにファイルが存在し、ファイルが安定していることを確認するための次のスクリプトがあります(これは私が受け取る大容量のビデオファイルについてです)。
#!/bin/bash
cdate1=$(date +%Y%m%d-%T)
folder1="/path-to-folder"
cd $folder1
while file=$(ls "$folder1")
[ -z "$file" ]
do sleep 10
done
echo "There is a file in the folder at $cdate1"
size1=$(stat -c '%s' "$file")
echo "The size1 is $size1"
sleep 30
size2=$(stat -c '%s' "$file")
echo "The size2 is $size2"
if [ $size1 = $size2 ]
then
ls -l
echo "Start converting"
else
echo "Restart the script"
fi
同じフォルダ内の複数のファイルを確認し、すべてのファイルが安定したら変換スクリプトを起動するようにスクリプトを変更するにはどうすればよいですか?
答え1
スクリプトには次のようないくつかの問題があります。
folder1="path-to-folder"
cd $folder1
while file=$(ls "$folder1") ...
本当ですcd path-to-folder; ls path-to-folder
。path-to-folder
絶対パス(例のように「/」で始まる)の場合は機能できますが、相対パスを使用している限りは機能しません。また、スペースが含まれていても機能しませんpath-to-folder
。たとえば、どこでも引用符を使用する必要があるためです。cd "$folder1"
その後、aを実行すると常に次に設定され、無限while file=$(ls ...)
ループが実行されます。file
何(ディレクトリの内容)。
正しい構文は、for file in $(ls ...)
ファイル名にスペースが含まれていると動作を停止することです(名前がファイルの場合はfoo
ループが実行されるため)。なぜ必要なのかを確認してくださいbar
foo bar
lsの出力を解析しないでください。。を使用する代わりに、ls
単にfor file in *
。
最後に、ファイルサイズが変更されなくなってもファイルが変更される可能性があります。
ファイルを繰り返す良い方法は、find
ファイルが変更されたことを確認するための良い方法ですmtime
。
次の関数は、特定のディレクトリにあるすべてのファイルの最後の変更時刻(mtime)値を提供します。
folder1="/path-to-folder"
find "${folder1}" -exec stat -c "%Y" \{\} \; \
| sort -n | tail -1
したがって、スクリプトは次のようになります。
#!/bin/bash
dir="$1"
# check whether $dir exists
test -d "${dir}" || exit 1
last=0
current=1
while [ "$last" != "$current" ]; do
last=$current
current=$(find "${dir}" -exec stat -c "%Y" \{\} \; \
| sort -n | tail -1)
sleep 10
done
echo "directory is now stable..."
修正する
より良いアプローチは、特定のファイルが送信されたことを受信者に事前に通知することです。非常に簡単な解決策は、ペイロードが送信された後に空のダミーファイルをコピーすることです。たとえば、foo.avi
「別のファイルをコピー」というファイルの場合は、そのファイルが準備されていることを確認するfoo.avi.copyfinished
だけです。foo.avi.copyfinished
foo.avi
while true; do
for file_ready in *.copyfinished; do
file=${file_ready%.copyfinished}
if [ -e "${file}.converted" ]; then
echo "skipping already converted file ${file}" 1>&2
else
touch "${file}.converted"
do_convert "${file}"
fi
done
sleep 1
done
この解決策には明らかに発信者の協力が必要です。
答え2
プロセスが転送が完了したかどうかを知っている転送側の処理を制御する方が簡単です。これは転送を中断しません。
ファイルを一時的な名前またはディレクトリに送信します。転送が完了したら、ファイルを正しい場所に移動してください。
cp srcvideo.avi /folder1/srcvideo.tmp && mv /folder1/srcvideo.tmp /folder1/srcvideo.avi
これにより、スクリプトでディメンションに対してすべてのテストを実行する必要がなくなります。正しいファイルが出るまで待つことができます。
cd /folder1 || exit 1
for file in *.avi; do
echo "$file found"
do_some_processing "$file"
done
必要に応じて、ファイル名を変更せずに別々のトランスポートディレクトリを使用して同じ目的を達成できます。
答え3
ファイルの成長が停止するのを待つのは、ダウンロードが完了したかどうかを検出する良い方法です。ネットワーク障害が原因でダウンロードが長すぎると一時停止すると、スクリプトが起動します。
ダウンロードが完了した後にいくつかのタスクを実行する最善の方法は、ダウンロードが完了して成功したときにスクリプトを実行するようにダウンローダに指示するか、ダウンローダが終了するのを待ってからダウンロードが成功した場合にスクリプトを実行するように指示することです。まともなダウンローダであれば、これらのオプションの少なくとも1つを許可します。
一部の悪いダウンロード方法を使用している場合は、手動でファイルを表示するのではなく、通知ツールを使用してファイルのダウンロードが完了したときに反応します。 Linuxでは、通知ツールは次のとおりです。inotify。ダウンローダの動作方法に応じて、ファイルを閉じる(ダウンローダが最終ファイルに直接書き込む場合)または名前を変更する(ダウンローダが最初に一時ファイルに書き込んでからその場所で名前を変更する場合)。
シェルツールが利用可能inotifywait
ファイルイベントが発生すると反応します。以下は、名前が変更された各ファイルを変換する例です。
cd /path/to/directory
inotifywait -m -e moved_to --format=%f . |
while IFS= read -r filename; do
conversion-program "$filename"
done
答え4
すべてのファイルが存在するかどうかを知る前にスクリプトを開始するには、まずファイル数が一定であることを確認してください。以下を使用して同様の操作を実行できます。listfile = $(ls "$folder")
[ $( echo $listfile | wc -w) != $(ls "$folder" | wc -w) ]
whileループでは、状況が同じになるまでに$listfile
リセットされます$(ls "$folder")
。タスクが完了したら、1つのファイルで実行したタスクを実行するdo
ループを簡単に実行できます。操作が完了したら変換を開始できます。$listfile