У меня есть огромное дерево папок, каждая из которых имеет несколько подкаталогов, расположенных примерно на 3 уровнях. Вот пример всего с одним уровнем:
$ 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
в одну папку, например destination_folder
. Любая помощь будет принята с благодарностью! Я предполагаю, что это будет включать find
и cp
, но, похоже, не могу это сделать,
решение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
Сначала он создает целевой каталог в текущем каталоге (и завершается, если это не удается). Затем он считывает входной файл, строка за строкой, и вызывает find
для поиска любого обычного файла с таким именем. Целевой каталог явно избегается с помощью find
использования -prune
всякий раз, когда мы сталкиваемся с ним в нашем поиске.
Всякий раз, когда файл с правильным именем найден, он копируется в целевой каталог. Если несколько файлов имеют одинаковое имя, копия в destination_folder
будет перезаписана.
Если текущий каталог огромен или список имен файлов длинный (но не много тысяч строк), то это будет медленная операция. Поэтому мы можем выбрать сделатьодинокийвызов find
. Следующий код предполагает, что он выполняется с помощью eg, 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 {} +
Здесь я также решил использовать 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 cp
(например, в системе Linux), следует использовать cp
с параметром -b
или --backup
.
В системах, отличных от Linux, GNU cp
часто может быть доступен gcp
после установки GNU coreutils через менеджер пакетов.
Последний скрипт, но /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