while ループ -done<done<file の代わりにコマンド

while ループ -done<done<file の代わりにコマンド

サブフォルダ内のすべてのファイルを現在のフォルダに移動するには、このスクリプトを使用します。

while read f
do
    mv "$f" .
done < file_list

file_listこれはうまく機能しますが、生成する必要があります

find . -name *.avi > file_list

私が望むのは、コマンドをwhileループに直接追加することです。

while read f
do
    mv "$f" .
done < find . -name *.avi

しかし、バッシュは私に言う -bash: syntax error near unexpected token `.'

findコマンドをwhileループにパイプする簡単な解決策は何ですか?

答え1

これにはループは必要なく、find次のように実行できます。

find . -name '*.avi' -exec mv {} . \;

答え2

プロセス置換を使用することもできます。

while IFS= read -r f
do
    mv -- "$f" .
done < <(find . -name '*.avi' )

シェルでは、bashパイプライン方式と比較して利点があります。いいえサブシェルでループを実行すると、ループ内で行った変数の割り当ては後で失われません。bash(または)では、zsh次のことをお勧めします。

while IFS= read <&3 -rd '' f 
do
    mv -- "$f" .
done 3< <(find . -name '*.avi' -print0)

任意のファイル名を処理するには、NUL区切り文字を使用し、ユーザーにプロンプ​​トが表示され、stdinから答えを読むことができるので、0の代わりにfd 3を使用してください。mvこれは0を使用した場合に出力になります。find

または、findコマンド自体を使用します。

find . -name '*.avi' -exec mv -t . {} +

使用する+ことは、多くのパラメータを一度に渡すので、各ファイルmvに対してmv呼び出しを実行する必要がないという意味です。-tGNU拡張です。他のmv実装では、次のように変更できます。

find . -name '*.avi' -exec sh -c 'exec mv "$@" .' sh {} +

答え3

正しい方法はパイプです

find . -name '*.avi' |
while read f
do
    mv "$f" .
done 

第1のコマンド「」の結果は、find . -name *.avi第2のコマンドに提供される。

これをパイプといいます(記号に由来|)。パイプを次の一時ファイルとして考えることができます。

find . -name '*.avi' > file1.tmp
while read f
do
    mv "$f" .
done < file1.tmp
rm file1.tmp

指摘したように、ファイル名にスペースまたは改行文字が含まれると、エラーが発生する可能性があります。

唯一の目的がファイルを移動することである場合は、@Terdonのコマンドを使用してください。

また、引用する必要があります。*.aviそれ以外の場合は、2回目の実行で中断されます。

find: paths must precede expression: `foo.avi' 
find: possible unquoted pattern after predicate `-name'?

答え4

既存の2つの答えの間の中間位置は、「インライン」を見つけて使用することです。~のためリングの形。

for f in `find . -name '*.avi' `
do
    mv "$f" .
done 

バックティックを使用すると、findコマンドが所定の位置で実行され、その出力が置き換えられます。

欠点ファイル名に空白と予期しない奇妙な文字が含まれていると、問題が発生する可能性があります。 @Terdonの答えは素晴らしいです。

関連情報