各行にデータファイルダウンロードコマンドを実行する「コマンド」テキストファイルがあります。コマンドファイルをbashに送信します。しかし、少数のダウンロードは失敗します。欠落している項目を見つけるために使用するアルゴリズムは次のとおりです。
- ダウンロード後にコマンドファイルに戻り、ダウンロードした各ファイルが存在することを確認します。
- ダウンロードがない場合は、コマンドラインを新しいコマンドファイルにコピーします。
- 残りのダウンロードのために新しいコマンドファイルを残しました。
以下は、アルゴリズムを実装するbashスクリプトです。
1 #!/bin/bash
2 while read line
3 do
4 for item in $line
5 do
6 if [[ $item == *out_fname* ]]; then
7 splitline=(${item//=/ })
8 target_file=${splitline[1]}
9 if [ ! -f $target_file ]; then
10 echo $line >> stillneed.txt
11 fi
12 fi
13 done
14 done < "$@"
Q:これはうまく機能しますが、より良いアルゴリズムや実装があります(おそらくbash以外のものを使用することもできます)。私がしたことは、人間がしなければならないことをbashに任せることだけでした。しかし、Unixは常に仕事を処理するより良い方法を持っているようです...
答え1
「out_fname」だけでなく、「out_fname=」を探しているようです。
私はawkとshellを混ぜて使用するか、Pythonを使用します。 awk/シェルから:
awk '{for(i=0;i<NF;i++) {if (index($i, 'out_fname=')) {split($i,A,/=/);print A[i]}}' "$@" |
while read filename; do
if [ ! -f $filename ]; then echo $filename; fi
done > stillneed.txt
Pythonでは:
import fileinput, os
stillneed = open("stillneed.txt", "w")
for line in fileinput.input():
for filename in [l.split('=')[1] for l in line.split() if l.find('out_fname=')!=-1]:
if not os.path.exists(filename):
print >>stillneed, filename
答え2
役に立つかどうかはわかりませんが、成功するまでコマンドを再試行する関数があります。
retry () {
local delay=1 n
if ! [[ $1 = *[^0-9]* ]]; then
# TODO allow delay=0 (prevents Ctrl-C)
if (($1 > 0)); then
delay=${1:1}
fi
shift
fi
# run command
while ! "$@"; do
echo "retrying in ${delay}s"
for ((n=delay; n>0; n--)); do
sleep 1 || return
done
done
}; export -f retry
答え3
初期ダウンロードスクリプトの完了後に欠落しているコンテンツを確認するのではなく、ダウンロードスクリプトにいくつかのチェックを追加することをお勧めします。次の内容はテストせずにただ頭の中に書いています。
cat files_to_download|while read file;
do
SUCCESS="False"
while [[ $SUCCESS == "False" ]];
do
wget $file;
if [[ $? -eq 0 ]];
then
SUCCESS="True"
fi
done
done
答え4
echo
その後、ファイルの各行を解析してファイル名を解決するよりも、ダウンロードが失敗した場合はその行をコピーすることをお勧めします。
[[ -f $1 ]] || { echo "$1 not found" >&2; exit 1; }
while read -r line; do
$line || echo "$line" >> stillneed
done < "$1"
これはより効率的で、後で奇妙なファイル名(スペースを含む)について心配する必要はありません。
既存の方法を改善するには、標準パラメータ拡張を使用できます。
for f; do
while read -r line; do
for item in $line; do
[[ $item = out_fname=* ]] || continue
[[ -f ${item#out_fname=} ]] || echo "$line"
break # assuming one fname per line
done
done < "$f"
done > stillneed
..しかし、何が起こるのか考えてみてください。out_fname='foo bar.ext'
。また、イベントが発生した後に各行を確認しますが、コマンドの実行時にコマンドが有効であることを確認できることに注意してください。
stillneed
ループ全体を一度開くのがより効率的です。ダウンロードコマンドの出力を表示したい可能性が高いため、最初のコードスニペットではこれを行いませんでした。これはテストだけで外部コマンドは実行されないため、ファイルを一度開くことをお勧めします。 (使用すると>
最初にファイルが切り捨てられます。私はfor f
複数の入力ファイルを場所引数として受け入れました。必要に応じて上記と同じように追加するのは簡単です)。
私が強調すべき一つのことは引用ですecho "$line"
。echo $line
一般的に言えば、引用はです。みんなフィールドを分割したい場合を除き、パラメータ拡張(変数を含む)です。