私はサブディレクトリを介して繰り返す必要がある(1行)スクリプトを書いています。ハイパーリンクを含む.txtファイルを見つけます。 wgetを使用してコンテンツをインポートし、テキストファイルと同じディレクトリにダウンロードします。
見つかったすべてのテキストファイルには有効なハイパーリンクのみが含まれていると想定されます。
これをテストするには:
サブディレクトリを作成します。内容を含む./s1
テキストファイルを作成します。 ./s1/s1.txt
./s1/s1.txt
www.google.com
次の行は次のとおりです。
find . -type f -name "*.txt" -exec bash -cx "wget -i \"{}\" -P $(dirname \"{}\") " \;
問題は$(dirname \"{}\")
正しく拡張されないことです。実行されるbashコマンドは次のとおりです。
+ wget -i ./s1/s1.txt -P .
したがって、$(dirname \"{}\")
返される.
効果は新しいものです。目次 ./s1/s1.txt
建設される。したがって、ダウンロードしたファイルは次のように保存されます。./s1/s1.txt/index.html
私が交換した場合、$(dirname \"{}\")
出力$(echo \"{}\")
は次のようになります。
+ wget -i ./s1/s1.txt -P ./s1/s1.txt
したがって、パラメータ渡し自体は正確です。したがって、結果がdirname
呼び出しbashシェルに正しく返されないと仮定します。またはdirname
まったく評価しないでください。
ちょうどbashコマンドを実行したとき
bash -cx "wget -i ./s1/s1.txt -P $(dirname ./s1/s1.txt)"
(したがって、find
コマンドの外部) コマンドは期待どおりに実行されます。
+ wget -i ./s1/s1.txt -P ./s1
このラインを操作する正しい方法は何ですか?
答え1
ここでは、次のことができます。
find . -name '*.txt' -type f -execdir wget -i {} -P . ';'
見つかったファイルのディレクトリでコマンドを実行するのではなく、非標準ですが、非常に一般的な-execdir
述語を使用してください(そして、GNUを含むいくつかの実装が前に来ることができるフルパスではなくファイル名に拡張されます)。find
-exec
{}
./
find
find
GNUを使用すると、find
一部xargs
を並列に実行できます。
xargs -r0 -n4 -P10 -a <(
find . -name '*.txt' -type f -printf '-i\0%p\0-P\0%h\0'
) wget
find
引数リストを作成し、wget
それをNULで区切って出力します(0は、ファイルパスの外部コマンドライン引数には現れない唯一のバイト値です)。xargs
一度に最大パラレルでインスタンスを実行します。4
wget
10
P
存在するzsh
:
for file (**/*.txt(N.)) wget -i $file -P $file:h
(次に追加D
グローバル予選find
メソッドのように隠されたファイルも処理したい場合)。
あなたの
find . -type f -name "*.txt" -exec bash -cx "wget -i \"{}\" -P $(dirname \"{}\") " \;
は二重引用符内にあるため、コマンドを入力したシェルはコマンドを渡す前に$(...)
出力に展開します。dirname \"{}\"
find
dirname \"{}\"
、sh / bashのdirname '"{}"'
出力(現在の作業ディレクトリパス)と同じです。dirname anything-that-does-not-contain-a-slash-and-does-not-start-with-dash
.
したがって、 find は次の引数で呼び出されます。
find
.
-type
f
-name
*.txt
-exec
bash
-cx
wget -i "{}" -P .
;
find
次のパラメータで実行されますbash
。
bash
-cx
wget -i "./path/to/the/file.txt" -P .
見つかった各ファイルに対して、bashは次のことを実行しますwget
。
wget
-i
./path/to/the/file.txt
-P
.
しかし、もしファイルパス\
含めると、潜在的に災害になる可能性がある、、、または"
文字`
は含まれません"
(たとえば、名前がファイルの場合$(rm -rf ~).txt
)。
二重引用符の代わりに一重引用符を使用する場合:
find . -type f -name "*.txt" -exec bash -cx 'wget -i "{}" -P "$(dirname "{}")"' \;
修正された可能性がありますが、上記の理由により、まだ非常に間違った状態です。{}
しなければならないいいえコードで計算されたパラメータに組み込まれます。バラより@gilsの答えこれを正しく行う方法。
1 -execdir
AFAIK OpenBSDでは、1996年のFreeBSD、1997年のFreeBSD、2002年のNetBSD、find
2005年のGNU、2010年のsfind、2014年に少なくともtoyboxに追加されました。
答え2
コメントで述べたように、find
このbash部分でプレースホルダを使用しないでください。{}
これは信頼できず、可能です。セキュリティ問題(シェル注入)。
この方法を使用する方が良いです。
find . -type f -name '*.txt' -exec sh -c '
for file; do
wget -i "$file" -P "$(dirname "$file")"
done
' sh {} +
または標準を使用してくださいパラメータ拡張(より効率的なものに加えて、ディレクトリ名が改行文字で終わってもまだ機能するという利点があります):
find . -type f -name '*.txt' -exec sh -c '
for file; do
wget -i "$file" -P "${file%/*}"
done
' sh {} +
$ tree
.
└── s1
├── index.html
└── s1.txt
1 directory, 2 files