巨大なフォルダ ツリーがあり、各フォルダには 3 レベルほどの複数のサブディレクトリがあります。以下は 1 レベルだけの例です。
$ tree
.
|-- AB.txt
|-- CD.txt
|-- destination_folder
|-- spreadsheet.txt
`-- subdirectory
`-- EF.txt
2 directories, 4 files
私が興味を持っているファイル名のリストがありますspreadsheet.txt
。
$ cat spreadsheet.txt
AB.txt
CD.txt
EF.txt
spreadsheet.txt
にあるすべてのファイルを1 つのフォルダ (例 ) にコピーしたいのですが、destination_folder
どなたか助けていただけるとありがたいです。 と が関係するfind
と思いますcp
が、うまくいきません。
答え1
ファイル名ごとに 1 回実行しfind
、ファイル名に埋め込まれた改行が含まれていないことを前提とします。
#!/bin/sh
mkdir -p destination_folder || exit 1
while IFS= read -r name; do
find . -path ./destination_folder -prune -o \
-type f -name "$name" -exec cp {} destination_folder \;
done <spreadsheet.txt
これはまず、現在のディレクトリに宛先ディレクトリを作成します (失敗した場合は終了します)。次に、入力ファイルを 1 行ずつ読み取り、その名前を持つ通常のファイルを検索します。検索中に宛先ディレクトリに遭遇した場合は、を使用するfind
ことで、宛先ディレクトリを明示的に回避します。find
-prune
正しい名前のファイルが見つかると、そのファイルは宛先ディレクトリにコピーされます。同じ名前のファイルが複数ある場合は、コピーされたファイルはdestination_folder
上書きされます。
現在のディレクトリが巨大であったり、ファイル名のリストが長い場合(ただし数千行ではない)、この操作は遅くなります。そのため、シングルを呼び出しますfind
。次のコードは、bash
配列を使用するため、たとえば で実行されることを前提としています。
#!/bin/bash
mkdir -p destination_folder || exit 1
names=()
while IFS= read -r name; do
names+=( -o -name "$name" )
done <spreadsheet.txt
find . -path ./destination_folder -prune -o \
\( "${names[@]:1}" \) -type f -exec cp -t destination_folder {} +
ここでは、見つかったファイルごとに 1 回ではなく、できるだけ少ない回数で呼び出せるように、cp -t
(GNU拡張機能)を使用することも選択しました。cp
cp
names
上記のコードは、最終的に で使用するための正しい形式になる配列 を構築しますfind
。質問の例によると、上記のコードの最後に実際に実行されるコマンドは次のようになります。
find . -path ./destination_folder -prune -o '(' -name AB.txt -o -name CD.txt -o -name EF.txt ')' -type f -exec cp -t destination_folder '{}' +
宛先ディレクトリでのファイル名の衝突の問題を回避するには、GNU を使用している場合(Linux システムなど)、またはオプションとともにcp
を使用します。cp
-b
--backup
Linux 以外のシステムでは、パッケージ マネージャー経由で GNU coreutils をインストールすると、 GNU がcp
利用できるようになることがよくあります。gcp
最後のスクリプトですが、/bin/sh
(配列なし) の場合:
#!/bin/sh
mkdir -p destination_folder || exit 1
set --
while IFS= read -r name; do
set -- -o -name "$name" "$@"
done <spreadsheet.txt
shift
find . -path ./destination_folder -prune -o \
\( "$@" \) -type f -exec cp -t destination_folder {} +
答え2
単純な for ループ:
for filename in $(cat spreadsheet.txt)
do
find . -name "$filename" -exec cp {} /destination/folder \;
done