Ваша команда не будет работать должным образом по нескольким причинам:
Баш оценивает `имя - `dirname {}`
перед командой находкой запускается на выполнение.
Это можно решить, вызвав bash для оценки вывода команды dirname
-execdir
изменяет каталог найденного файла перед выполнением команды.
Поэтому, даже с правильным синтаксисом, dirname {}
всегда будет .
,
Так как 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
, поиск как-то "теряется".
Хотя find правильно обрабатывает все возможные символы со специальным значением, если {}
встречается как простой аргумент для поиска, команды в примере 3 не будут выполняться должным образом, если, например, в пути будет пробел. Чтобы избежать этого, оба аргумента dirname
должны быть заключены в кавычки.
Использование mv "`dirname "{}"`"
(или mv "$("{}")"
, что более читабельно) будет хорошо работать с пробелами, но в целом может привести к катастрофическим последствиям 1.
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
останавливает поиск после нахождения первого совпадения. Это не строго необходимо, но это может немного ускорить процесс.