Как интегрировать команду mv после команды find?

61

Я ищу файлы, имена которых содержатся AAAв их пути, используя следующую команду:

find path_A -name "*AAA*"

Учитывая вывод команды выше, я хочу переместить эти файлы, скажем, в другой путь path_B. Вместо того, чтобы перемещать эти файлы один за другим, могу ли я оптимизировать команду, перемещая эти файлы сразу после команды find?

huahsin68
источник

Ответы:

87

С GNU mv :

find path_A -name '*AAA*' -exec mv -t path_B {} +

Это будет использовать -execопцию поиска, которая заменяет {}каждый результат поиска по очереди и запускает команду, которую вы ему даете. Как объяснено в man find:

   -exec command ;
          Execute  command;  true  if 0 status is returned.  All following
          arguments to find are taken to be arguments to the command until
          an  argument  consisting of `;' is encountered.  

В этом случае мы используем +версию -execтак, чтобы мы выполняли как можно меньше mvопераций:

   -exec command {} +
          This  variant  of the -exec action runs the specified command on
          the selected files, but the command line is built  by  appending
          each  selected file name at the end; the total number of invoca‐
          tions of the command will  be  much  less  than  the  number  of
          matched  files.   The command line is built in much the same way
          that xargs builds its command lines.  Only one instance of  `{}'
          is  allowed  within the command.  The command is executed in the
          starting directory.
cuonglm
источник
Не забывайте "\;" в конце exec!
Чарльз Рот
@ Чарльз Рот: это даёт +работу, вы можете прочитать мою цитату выше или man findвместо этого
cuonglm
Спасибо за это! Я пытался использовать, -exec mv {} path_b +и он терпел неудачу с ошибками разрешений. TBH, я до сих пор не понимаю, почему, но -exec mv -t path_b {} +работает удовольствие!
Джереми Дэвис,
6
@JeremyDavis При использовании -exec ... {} +, то {}должен быть последним, прежде чем +. Именно поэтому он использует mv -t destdir {} +и нет mv {} destdir +. -exec mv {} destdir ';'Вместо этого использовали одну простую , но она выполнялась бы mvодин раз для каждого файла.
Кусалананда
23

Вы можете сделать что-то вроде ниже.

find path_A -name "*AAA*" -print0 | xargs -0 -I {} mv {} path_B

Где,

  1. -0Если есть пробелы или символы (включая символы новой строки), многие команды не будут работать. Эта опция заботится об именах файлов с пустым пространством.
  2. -IЗаменить вхождения replace-str в исходных аргументах именами, считанными из стандартного ввода. Кроме того, пробелы без кавычек не заканчивают элементы ввода; вместо этого разделителем является символ перевода строки.

тестирование

Я создал две директории как sourcedirи destdir. Теперь, я создал кучу файлов внутри , sourcedirкак file1.bak, file2.bakиfile3 with spaces.bak

Теперь я выполнил команду как,

find . -name "*.bak" -print0 | xargs -0 -I {} mv {} /destdir/

Теперь, внутри destdir, когда я делаю ls, я мог видеть , что файлы перемещены из sourcedirв destdir.

Рекомендации

http://www.cyberciti.biz/faq/linux-unix-bsd-xargs-construct-argument-lists-utility/

Рамеш
источник
22

В интересах пользователей OS X, сталкивающихся с этим вопросом, синтаксис в OS X немного отличается. Предполагая, что вы не хотите выполнять рекурсивный поиск в подкаталогах path_A:

find path_A -maxdepth 1 -name "*AAA*" -exec mv {} path_B \;

Если вы хотите искать все файлы рекурсивно в path_A:

find path_A -name "*AAA*" -exec mv {} path_B \;
mannykary
источник
Это не специфический синтаксис OS X, он такой же, как и любой другой, findкоторый я использовал. Хорошие моменты: -maxdepth(особенно если path_B является подкаталогом - избегает mvпопыток переместить файлы уже там!) И использование \; (поэтому {} не должен быть последним параметром, и mvможно использовать нормальный синтаксис)
drevicko
6

Это -execлучший способ сделать это. Если по какой-либо причине это не вариант, вы также можете прочитать результаты в цикле:

find path_A -name "*AAA*" -print0 | 
    while IFS= read -r -d $'\0' file; do mv "$file" path_B; done

Это безопасный способ, он может работать с именами файлов, которые содержат пробелы, символы новой строки или другие странные символы. Более простой способ, который не работает, если имена файлов не состоят только из простых буквенно-цифровых символов ,

mv $(find path_A -name "*AAA*") path_B

Но используйте цикл while.

Тердон
источник
Более простой способ все еще может потерпеть неудачу, если your file names consist only of simple alphanumeric characters, например, при достиженииARG_MAX