Хотя это правда, что к eval
всегда нужно подходить с осторожностью, конструкция eval echo
не всегда бессмысленна, и можно безопасно использовать. Недавно мне это понадобилось, чтобы оценить несколько расширений скобок в том порядке, в котором они мне были нужны.
bash
делает несколько раскрытий фигурных скобок слева направо, поэтому
xargs -I_ cat _/{11..15}/{8..5}.jpg
расширяется до
xargs -I_ cat _/11/8.jpg _/11/7.jpg _/11/6.jpg _/11/5.jpg _/12/8.jpg _/12/7.jpg _/12/6.jpg _/12/5.jpg _/13/8.jpg _/13/7.jpg _/13/6.jpg _/13/5.jpg _/14/8.jpg _/14/7.jpg _/14/6.jpg _/14/5.jpg _/15/8.jpg _/15/7.jpg _/15/6.jpg _/15/5.jpg
, но мне нужно было сначала выполнить расширение второй скобки, что дало
xargs -I_ cat _/11/8.jpg _/12/8.jpg _/13/8.jpg _/14/8.jpg _/15/8.jpg _/11/7.jpg _/12/7.jpg _/13/7.jpg _/14/7.jpg _/15/7.jpg _/11/6.jpg _/12/6.jpg _/13/6.jpg _/14/6.jpg _/15/6.jpg _/11/5.jpg _/12/5.jpg _/13/5.jpg _/14/5.jpg _/15/5.jpg
Лучшее, что я мог придумать,
xargs -I_ cat $(eval echo _/'{11..15}'/{8..5}.jpg)
Это работает, потому что одинарные кавычки защищают первый набор фигурных скобок от расширения во время синтаксического анализа командной строки eval
, оставляя их для расширения подоболочкой, вызванной eval
.
Возможно, существует какая-то хитрая схема, включающая раскрытие вложенных фигурных скобок, позволяющая сделать это за один шаг, но если и есть, то я слишком стар и глуп, чтобы это понять. Помимо bash
существуют оболочки, которые позволяют более аккуратно реализовать такие вещи. Но в любом случае такое использование eval
безопасно, поскольку все его аргументы представляют собой фиксированные строки, не содержащие расширений параметров.
Шаблон подстановочных знаков *.avi
расширяется оболочкой, которая запускает find
перед запуском find
, поэтому его действие зависит от того, есть ли файлы *.avi
в текущем каталоге или нет. См. find not recursive when file at top для получения дополнительных пояснений. Чтобы расширить *.avi
в подкаталогах,вам нужно будет сделать три вещи по-разному :указать шаблон, чтобы исходная оболочка не расширяла его; организовать запуск дополнительной оболочки в каждом подкаталоге для выполнения подстановочного расширения; и искать каталоги только с помощью команды find
, а не любого типа файла.
Кроме того, ваш код вызывает rename
для каждого файла на любом уровне в текущем каталоге, включая сами подкаталоги, через {} +
. Итак, rename
работает с каталогами, а не только с обычными файлами.
Кроме того, в вашем коде Perl есть синтаксическая ошибка.
autoload -Uz zmv # best in ~/.zshrc
zmv -n '(**/)(*.avi)(#qD^/)' '$1${2//[^a-zA-Z0-9._-]/_}'
^/
— выбрать любой тип файла, кроме каталога . Замените на .
только для обычных файлов . -n
для работы всухую -. Удаляйте, когда счастливы.
find
иrename
С вариантами rename
на основе perl -и реализацией find
, поддерживающей-execdir
:
LC_ALL=C find. -depth -name '*[!a-zA-Z0-9._-]*.avi' ! -type d -execdir \
rename 's/[^a-zA-Z0-9._-]/_/g' {} +
При таком подходе есть несколько предостережений:
rename
экземпляр на каталог, содержащий файлы для переименования (один rename
на файл с некоторыми find
реализациями/версиями, где -execdir... {} +
фактически совпадает с -execdir... {} \;
.(zmv
запускает один mv
для каждого файла, но вы можете сделать mv
встроенным с помощью zmodload zsh/files
, чтобы ускорить его ). -execdir
find
запускает команду в каталоге , который содержит эти файлы, и передает команде путь относительно этого каталога. Некоторые find
реализации (GNU )добавляют префикс ./
к файлам, некоторые — нет. Некоторые варианты rename
принимают опции после выражения Perl, что означает, что если у вас есть файл, имя которого начинается с -
, это может вызвать проблемы.LC_ALL=C
для -name
для работы, даже если имена файлов содержат последовательности байтов, которые в противном случае не сформировали бы допустимые символы в локали. rename
наследует это и в любом случае в большинстве вариантов работает только с ASCII. Однако это означает, что он заменит многобайтовые символы -на столько _
, сколько байтов содержит символ. Например, он переименует UTF -8 stéphane
в st__phane
вместо st_phane
. zsh
подходит, потому что он преобразует как многобайтовые символы -, так и все байты, которые не могут быть декодированы в символы, в один _
символ каждый. zsh
's zmv
, он не будет выполнять проверки работоспособности (например, 2 файла не будут иметь одинаковое имя, например a+b.avi
иa@b.avi
)до начала переименования. Однако rename
не должен перезаписывать существующие файлы. Если предположить, что
rename 's/[^A-Za-z0-9.-]/_/g' -- *.avi
будет правильно переименовывать все файлы с суффиксом имени файла .avi
в текущем каталоге, вы сможете применить это ко всем каталогам ниже текущего (, включая текущий )с
find. -type d -exec sh -c '
for dirpath do
( cd "$dirpath" && rename "s/[^A-Za-z0-9.-]/_/g" -- *.avi )
done' sh {} +
Для пакетов найденных каталогов будет выполнен короткий скрипт в строке -, который переходит в найденный каталог и выполняет команду rename
.