私はこれをしばらくの間機能させようとしています。ファイルのリストを検索し、それらをすべて特定のパスにコピーしたい。
コマンドを個別に実行すると、次のように動作します。
find <path> -name 'file1' -exec cp '{}' <path2> \;
しかし、forループ内で実行できませんでした。
#this works
for f in file1 file2 file3...; do find <path> -name "$f" \; done;
#none of these work (this code tries to find and copy the files named file and list from the path)
for f in file1 file2 file3...; do find <path> -name "$f" -exec cp '{}' <path2> \; done;
for f in file1 file2 file3...; do find <path> -name "$f" -exec cp {} <path2> \; done;
動作しそうにないものをいくつか試しました。コードの引用の最初の行は動けなくなり、他の行は動けなくても何もコピーしません。
検索後にforループ内でexecを使用して何も実行できなかったため、この時点で何をすべきかわかりません。
ファイルを検索して結果を別のファイルに記録し、forループ内でコピーを個別に実行することにより、当面の問題を解決しましたが、これを行う方法を知りたいです。
他の答えは正しいですが、同じディレクトリ構造を繰り返しスキャンするためにfind
を複数回呼び出す必要のない異なるアプローチを提供したいと思います。基本的な考え方は、find
を使用して
cp
、フィルタリングされたリストのエントリ。(ヌルバイトで区切られたレコードを読み取るにはBashが必要です)
find /some/path -type f -print0 |
while read -rd '' f; do
case "${f##*/}" in
file1|file2|file3)
printf '%s\0' "$f";;
esac
done |
xargs -r0 -- cp -vt /some/other/path --
各パイプは、上記の3つのステップの次のステップの開始に対応しています。
case
ステートメントには、 globbing の一致を許可するという利点があります。あるいは、Bashの 条件式 を使用できます。
if [[ "${f##*/}" == file1 || "${f##*/}" == file2 || "${f##*/}" == file3 ]]; then
printf '%s\0' "$f"
fi
一致するファイル名のリストが少し長く、 globbing パターンの小さなセットで置き換えることができない場合、または一致するファイル名のリストが執筆時点で不明な場合、目的のファイル名のリストを保持する array に頼ることができます。
FILE_NAMES=( "file1" "file2" "file3" ... )
find /some/path -type f -print0 |
while read -rd '' f; do
for needle in "${FILE_NAMES[@]}"; do
if [ "${f##*/}" = "$needle" ]; then
printf '%s\0' "$f"
fi
done
done |
xargs -r0 -- cp -vt /some/other/path --
バリエーションとして、 連想配列 を使用できます。これは、単純な「リスト」配列よりもルックアップ時間が速いことを願っています:
declare -A FILE_NAMES=( ["file1"]= ["file2"]= ["file3"]= ... ) # Note the superscripts with []=
find /some/path -type f -print0 |
while read -rd '' f; do
if [ -v FILE_NAMES["${f##*/}"] ]; then
printf '%s\0' "$f"
fi
done |
xargs -r0 -- cp -vt /some/other/path --
2つの問題:
for f in some_file
はsome_file
のコンテンツを反復処理せず、リテラル文字列some_file
を反復処理するか、取得します。これを乗り越えるために、ファイルコンテンツの繰り返し処理のためのfor
ループを忘れるには、適切に実装されたwhile
コンストラクトを使用してください。
単一引用符、この場合は'$f'
内に置かれた場合、変数は展開されません。元のアイデアを機能させるには、二重引用符を使用します。
これらをまとめると、ファイル名がfile_list
ファイル内で改行で区切られていると仮定します。
while IFS= read -r f; do
find /path1 -name "$f" -exec cp '{}' /path2 \;
done <file_list
または、ファイルがそのサブディレクトリの下ではなく/path1
ディレクトリにあることがわかっている場合は、配列を使用してファイル名を取得し、(GNU)cp
を直接使用して、再び改行を仮定することができますfile_list
内のファイル名の区切り:
(
IFS=$'\n'
files=( $(<file_list) )
cp -t /path2 "${files[@]}"
)
膨大な数のファイルがある場合は、配列にダンプするよりも、個別にcp
を繰り返し処理する方が適切です。
while IFS= read -r f; do cp -- "$f" /path2; done <file_list
次のようなファイルリストがある場合file1 file2 file3 ...
を直接for
構成体に追加し、二重引用符を使用するだけでできます。
for f in file1 file2 file3; do
find /path1 -name "$f" -exec cp '{}' /path2 \;
done
現在、すべてのファイルがサブディレクトリにない場合は、ここでもcp
を直接使用できます。
cp -t /path2 file1 file2 file3
または、cp
を使用して、任意のサブディレクトリの下にあるファイルを処理する場合にのみ、静的な絶対パスまたは相対パスを指定できます。
ファイルリストはテキストファイルではなく、find
で検索する一連のファイル名であることを明確にしました。あなたはこれがあなたのために働くと述べました:
for f in file1 file2 file3 ... ; do find <path> -name "$f" \; done;
おそらく、そのコマンドから予想されるファイルのリストを取得することを意味します。
次に、これは機能しないと言います:
for f in file1 file2 file3 ... ; do find <path> -name "$f" -exec cp '{}' <path2> \; done
おそらく、前にリストされたファイルが<path2>
にコピーされないことを意味します。どのエラーが発生しているかはわかりませんが、書かれているとおり、このコマンドが次のようにハングすることを期待しています。
$for f in file1 file2 file3 ... ; do find <path> -name "$f" -exec cp '{}' <path2> \; done
>
不足している;
を待っています。あなたがその問題を修正したと仮定すると、あなたのコマンドが間違いなく失敗する他の明白な理由は考えられません。あなたはshouldで管理できます
for f in file1 file2 file3 ... ; do find <path> -name "$f" -exec cp '{}' <path2> \; ; done
ただし、-t
フラグを使用してディレクトリを指定することをお勧めします。 David Foerster for このコメント に感謝する必要があります。これは、cp
を使用したmv
またはfind
呼び出しに最適な形式を示していると思います。重複ファイルの上書きも拒否します。
for f in file1 file2 file3; do find /path/to/search -name "$f" -exec cp -vt /path/to/destination -- {} + ; done
-v
冗長にする-何が行われているかを教えてください-t
指定されたディレクトリを宛先として使用--
これ以上のオプションを受け入れない+
複数の一致がある場合(あなたの場合、これはfor
がファイルリストの各アイテムに対して1回実行されるため、起こりそうにありませんが、名前ごとに複数の一致するファイルがある場合があります) cp
を複数回実行する代わりにcp
のリスト;
は、シェル内のコマンドを区切ります。 for
ループの形式は
for var in things; do command "$var"; done
done
の前に;
が表示されない場合、bashは;
または割り込み信号を待ちます。