Я пытался получить эту работу некоторое время теперь. Я хочу искать список файлов и затем скопировать их всех в определенный путь.
Если я выполняю команду отдельно, то она работает как так:
find <path> -name 'file1' -exec cp '{}' <path2> \;
Но я не мог выполнить его в для цикла.
#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;
Я попробовал некоторых другой материал, которые, вероятно, не будут работать. Первая строка в кавычке кода просто застревает, и другие ничего не копируют даже при том, что они не застревают.
Я не смог выполнить что-либо с должностным лицом в для цикла после поиска, и в этой точке я не уверен, что сделать.
Я решил непосредственную проблему путем поиска файлов и входа результатов в другой файл и затем выполнения копии в для цикла отдельно, но я все еще хотел бы знать, как сделать это.
В то время как другие ответы корректны, я хочу предложить другой подход, который не требует нескольких вызовов 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 --
Каждый канал соответствует началу следующего шага трех шагов, описанных выше.
case
оператор имеет преимущество разрешения globbing соответствия. Кроме того, Вы могли использовать условные выражения Bash:
if [[ "${f##*/}" == file1 || "${f##*/}" == file2 || "${f##*/}" == file3 ]]; then
printf '%s\0' "$f"
fi
Если список имен файлов для соответствия немного длиннее и не может быть заменен маленьким набором globbing шаблонов, или если список имен файлов для соответствия не известен во время записи, можно обратиться к массиву, который содержит список имен файлов интереса:
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 --
Две проблемы:
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= В случае огромного количества файлов, Вы были бы более обеспеченной законченной итерацией и 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
только для контакта с любыми файлами под любым подкаталогом.
\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
>
ожидает пропавшего ;
. Предполагая, что вы исправили эту проблему, я не могу думать ни о какой другой очевидной причине, по которой ваша команда определенно потерпит неудачу. Вы должны иметь возможность управлять с помощью
for f in file1 file2 file3 ... ; do find <path> -name "$f" -exec cp '{}' <path2> \; ; done
. Однако я предлагаю использовать флаг -t
для указания каталога. Я должен поблагодарить Дэвида Фёрстера за этот комментарий , который, я думаю, показывает наилучшую форму для вызова 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
будет выполняться один раз для каждого элемента в списке файлов, но может быть несколько сопоставляя файлы для каждого имени), затем создайте список аргументов для cp
вместо многократного выполнения cp
;
разделяет команды в оболочке. Форма цикла for
имеет вид
for var in things; do command "$var"; done
Если он не видит ;
до done
, bash будет ожидать ;
или сигнала прерывания.