Как я использую исполнительную команду в для цикла после команды находки?

Я пытался получить эту работу некоторое время теперь. Я хочу искать список файлов и затем скопировать их всех в определенный путь.

Если я выполняю команду отдельно, то она работает как так:

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;

Я попробовал некоторых другой материал, которые, вероятно, не будут работать. Первая строка в кавычке кода просто застревает, и другие ничего не копируют даже при том, что они не застревают.

Я не смог выполнить что-либо с должностным лицом в для цикла после поиска, и в этой точке я не уверен, что сделать.

Я решил непосредственную проблему путем поиска файлов и входа результатов в другой файл и затем выполнения копии в для цикла отдельно, но я все еще хотел бы знать, как сделать это.

4
задан 25 August 2017 в 03:05

3 ответа

В то время как другие ответы корректны, я хочу предложить другой подход, который не требует нескольких вызовов find сканировать ту же структуру каталогов неоднократно. Основная идея состоит в том, чтобы использовать find кому:

  1. генерируйте список файлов, которые соответствуют общим критериям,
  2. примените пользовательский фильтр к тому списку, и
  3. выполните некоторое действие, например. cp, на записях фильтрованного списка.

Реализация 1

(требует, чтобы 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

Реализация 2

Если список имен файлов для соответствия немного длиннее и не может быть заменен маленьким набором 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 --

Реализация 3

Как изменение мы можем использовать ассоциативный массив, который, надо надеяться, имеет более быстрые времена поиска, чем простые массивы "списка":

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 --
4
ответ дан 23 November 2019 в 11:36

Две проблемы:

  1. for f in some_file не выполняет итерации по содержанию some_file, просто выполняет итерации или берет литеральную строку some_file. Для преобладания это забывает приблизительно for цикличное выполнение для итерации по содержанию файла, использует правильно реализованный while конструкция.

  2. Переменные не будут расширены когда вставленные одинарные кавычки, '$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 только для контакта с любыми файлами под любым подкаталогом.

7
ответ дан 23 November 2019 в 11:36

Вы пояснили, что список файлов - это не текстовый файл, а серия имен файлов, которые вы хотите найти с помощью 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 используйте указанный каталог в качестве пункта назначения
  • [ 1115] не принимать никаких дополнительных опций
  • +, если есть несколько совпадений (в вашем случае это маловероятно, поскольку for будет выполняться один раз для каждого элемента в списке файлов, но может быть несколько сопоставляя файлы для каждого имени), затем создайте список аргументов для cp вместо многократного выполнения cp
  • ; разделяет команды в оболочке. Форма цикла for имеет вид

    for var in things; do command "$var"; done
    

    Если он не видит ; до done, bash будет ожидать ; или сигнала прерывания.

  • ] [Тысяча сто сорок два]
5
ответ дан 23 November 2019 в 11:36

Другие вопросы по тегам:

Похожие вопросы: