複数のディレクトリからファイルをコピーします(リストに名前が表示されている場合)。

複数のディレクトリからファイルをコピーします(リストに名前が表示されている場合)。

巨大なフォルダ ツリーがあり、各フォルダには 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拡張機能)を使用することも選択しました。cpcp

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

関連情報