3

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

Вот что я пытаюсь:

find /converter/storage/unmatched/ -size -4b -name '*.mp3' -execdir mv -b `dirname {}` /converter/storage/smallfiles/ \;
mv: cannot move `.' to `/converter/storage/smallfiles/.'

Сначала я пытаюсь переместить его, чтобы доказать себе, что это можно сделать, не удаляя ненужные файлы. Как мне переместить или удалить содержащий каталог?

1 ответ1

3

Ваша команда не будет работать должным образом по нескольким причинам:

  1. Баш оценивает `имя - ​`dirname {}`​ перед командой находкой запускается на выполнение.

    Это можно решить, вызвав bash для оценки вывода команды dirname

  2. -execdir изменяет каталог найденного файла перед выполнением команды.

    Поэтому, даже с правильным синтаксисом, dirname {} всегда будет . ,

  3. Так как find просматривает все файлы во всех каталогах (а не в самих каталогах), перемещение каталога, который в данный момент обрабатывается, может вызвать непредвиденное поведение.

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

    $ find data -type f -printf "  %p" ; echo
      data/1/b  data/1/a  data/2/b  data/2/a
    $
    $ find data -type f -exec bash -c ' \
    > echo -n "  "mv `dirname {}` backup/
    > ' \; ; echo
      mv data/1 backup/  mv data/1 backup/  mv data/2 backup/  mv data/2 backup/
    $
    $ find data -type f -exec bash -c '
    > echo -n "  "mv `dirname {}` backup/; mv `dirname "{}"` backup/
    > ' \; ; echo
      mv data/1 backup/  mv data/1 backup/
    mv: cannot stat `data/1': No such file or directory
    

    После перемещения каталога data/1 , find продолжает обрабатывать его содержимое. Это вызывает сообщение об ошибке, так как find выполняет mv для каждого файла в data/1 , но в противном случае работает хорошо. Когда он заканчивает обработку data/1 , поиск как-то "теряется".

  4. Хотя find правильно обрабатывает все возможные символы со специальным значением, если {} встречается как простой аргумент для поиска, команды в примере 3 не будут выполняться должным образом, если, например, в пути будет пробел. Чтобы избежать этого, оба аргумента dirname должны быть заключены в кавычки.

    Использование mv "`dirname "{}"`" (или mv "$("{}")" , что более читабельно) будет хорошо работать с пробелами, но в целом может привести к катастрофическим последствиям 1.

  5. mv -b не может создавать резервные копии каталогов, только файлы.

Как вы узнали сами, проблема 3 возникает только в том случае, если вы пытаетесь переместить каталог, а не если вы, например, удалите его. Следовательно, простым и безопасным решением проблемы 3 (которая устраняет проблему 5 в процессе) является перемещение соответствующих каталогов в zip-файл. 2

Проблема 4 может быть решена путем передачи {} в качестве аргумента bash и доступа к нему как $0 .

Команда

find /converter/storage/unmatched/ -type f -size -4b -name '*.mp3' \ -exec bash -c ' \
  zip -0mqr smallfiles.zip "$(dirname "$0")"\
' {} \;

будет работать должным образом, хотя он будет генерировать сообщения об ошибках при попытке переместить каталог более одного раза.

Каким- то более чистым подходом (то есть без сообщений об ошибках) было бы использовать find -depth -type d для просмотра самих каталогов. 3

Для каждого каталога мы можем использовать

find "$0" -maxdepth 1 -type f -size -4b -name '*.mp3' -printf 1 -quit

вывести 1 в том и только в том случае, если он должен быть удален 4, и проверить ([), чтобы проверить, не найдено ли напечатанное 1.

Команда

find /converter/storage/unmatched/ -depth -type d -exec bash -c ' \
  [ $(find "$0" -maxdepth 1 -type f -size -4b -name '*.mp3' -printf 1 -quit) ] \
  && echo zip -0mqr smallfiles.zip "$0"\
  ' {} \;

будет иметь желаемый эффект.


1 Если имя каталога внутри несоответствующего состоит, например, из одного символа новой строки, "$(dirname "{}")" будет иметь значение, unmatched а $(dirname {}) - . (т.е. папка, из которой вы выполняете команду)!

2 Даже без сжатия файлов это займет значительно больше времени, чем (пере) их перемещение, если файлы большие. Поскольку ваша папка назначения называется smallfiles, я подозреваю, что это не будет проблемой.

3 Ключ -depth` заставляет поиск обрабатывать содержимое каждого каталога перед обработкой самого каталога. Это позволяет избежать сообщений об ошибках, которые мы получили при первом подходе.

4 -quit останавливает поиск после нахождения первого совпадения. Это не строго необходимо, но это может немного ускорить процесс.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .