Мне гораздо проще использовать find , а затем grep -v в канале для фильтрации файлов вместо разработки сложных шаблонов регулярных выражений. Однако, когда я передаю что-то в zmv следующим образом:

find | grep -v TFLM | zmv "(*)" "TFLM \$1"

Он просто игнорирует ввод и продолжает применять преобразование ко всем файлам. Есть ли способ сказать ему использовать ввод канала? Думаю, я мог бы удалить отфильтрованные файлы, а затем использовать zmv, но на самом деле это не решение.

4
xeruf 4 Авг 2018 в 19:38

3 ответа

zmv не читает со стандартного ввода.

Я бы, вероятно, использовал find с mv здесь или zmv с универсальным именем файла zsh, но не с обоими, и вообще без использования grep. Использование grep следует применять к тексту, разделенному на строки, а не к именам файлов (которые потенциально могут включать встроенные символы новой строки).

С именами файлов (будет действовать только в текущем каталоге и только в нескрытых файлах):

zmv '^TFLM *' 'TFLM $f'

Рекурсивно, если не переименовывать каталоги, включая скрытые файлы и файлы в скрытых каталогах, таких как find:

zmv '(**/)(^TFLM *)(#qD^/)' '${1}TFLM $2'

С find (но без обработки конфликтов zmv, поэтому для безопасности добавлен параметр -i):

find . ! -type d ! -name 'TFLM *' -exec sh -c '
    for pathname do
       mv -i "$pathname" "${pathname%/*}/TFLM ${pathname##*/}"
    done' sh {} +

В bash (только для текущего каталога, исключая скрытые файлы):

shopt -s extglob
for name in !(TFLM *); do
    mv -i -- "$name" "TFLM $name"
done

Или, в bash с утилитой Perl rename:

shopt -s extglob
rename 's|/|/TFLM |' ./!(TFLM *)

(без ./ некоторые варианты не будут работать, если имена файлов начинаются с -. Не все варианты поддерживают -- для обозначения конца параметров).

6
Stéphane Chazelas 4 Авг 2018 в 23:15
Я ни разу в жизни не видел имя файла, содержащее новую строку, но тем не менее у вас есть несколько хороших предложений ^^
 – 
xeruf
6 Авг 2018 в 21:58
Редко, но время от времени вы вполне можете с ними сталкиваться. См., например. unix.stackexchange.com/questions/23163/newlines-in-filenames и unix.stackexchange.com/questions/307664/… и другие
 – 
Kusalananda
6 Авг 2018 в 22:08

То, что вы делаете, не соответствует тому, как должны работать find, grep и zmv. Прежде всего, вы используете find для поиска файлов, а затем grep для шаблона; это не имеет никакого смысла. Команда find имеет встроенное сопоставление с образцом, например, в GNU find, начиная с базового -name до -iname, -path, -regex и многих других. многое другое. Вы даже можете изменить синтаксис регулярного выражения, если хотите, с помощью -regextype. Дело не только в том, что вы делаете что-то не быстро или задействовано слишком много команд, что еще хуже, ваша команда подвержена ошибкам, например, если в файле есть место внутри.

Гораздо лучше чистый find с параметром -exec, за которым следует внешняя команда, такая как mv. При некоторой осторожности это решение может быть очень переносимым между различными системами.

Но, поскольку вы используете zsh, то он просит использовать все его великолепие, так что просто добавьте параметр -vn к zmv и поэкспериментируйте с различными шаблонами, скорее всего, вы захотите

zmv -vn '(^(*TFLM*))' 'TFLM $1'

-v означает многословие, а -n предотвращает выполнение, просто печатает то, что должно быть сделано (это отлично подходит для тестирования).

5
jimmij 4 Авг 2018 в 20:45
Или просто zmv -n '^*TFLM' 'TFLM $f'. Или рекурсивно и без переименования каталогов: zmv -b '(**/)(^*TFLM*)(#q^/)' '${1}TFLM $2'
 – 
Stéphane Chazelas
4 Авг 2018 в 22:58
1
Шаблон '^*TFLM' не будет работать, нужна хотя бы одна пара скобок, а вторая *, поэтому '(^*TFLM*)' — это минимум, по крайней мере, на моем zsh-5.5.1. Также -b является неузнаваемой опцией; вы, вероятно, используете очень новый zsh или какие-то нестандартные настройки.
 – 
jimmij
4 Авг 2018 в 23:18
zmv -n '^*TFLM*' 'TFLM $f' (извините, пропустил второй * выше) отлично работает для меня с 5.4.2 или 5.5.1. Я не понимаю, зачем вам нужны скобки. Извините, -b тоже опечатка (b стоит рядом с n на моей клавиатуре). Обратите внимание, что -n подразумевает -v.
 – 
Stéphane Chazelas
4 Авг 2018 в 23:23
Хорошо, с $f (но не $1) и вторым * все в порядке без скобок.
 – 
jimmij
4 Авг 2018 в 23:28

Я мысленно работаю так же, как и вы, предпочитая размещать команды grep в конце. Трудно сопротивляться желанию, но в таких ситуациях вы должны или мысленно помнить, что когда вы хотите начать действовать в соответствии со списком, исходящим от find | ..., вы должны начать тянуть xargs. Когда вы дойдете до этого момента, пора переключиться на find <regex> -exec ....

В вашем сценарии шаблон, который вы могли бы рассмотреть здесь, выглядит примерно так:

$ find . ! -name "*TFLM*" -exec zmv "{}" "TFLM {}" \;

Но это не сработает, судя по комментариям @StephaneChazelas:

Поскольку zmv — это функция zsh, ее нельзя выполнить напрямую с помощью find. Даже если бы кто-то сделал автономную оболочку сценария вокруг zmv, назвав ее так, как -exec zmv "{}"... не имело бы особого смысла (полностью противоречит цели zmv) и представило бы уязвимость внедрения команд

Таким образом, у вас остаются более традиционные варианты использования одного из методов, показанных в этом разделе вопросов и ответов U&L под названием: переименование файлов.

Или используя zmv напрямую, чтобы выполнить само переименование. Поскольку вы используете zsh и zmv, скорее всего, вам вообще не понадобится помощь find.

$ zmv "(^*TFLM*)" "TFLM \$1"

ПРИМЕЧАНИЕ. Примите мой совет относительно zmv и zsh с долей скептицизма. На самом деле я не использую Zsh, обычно я весь день сижу в Bash.

Ссылки

2
slm 4 Авг 2018 в 23:18
- обновлен Q с вашим комментарием + изменен.
 – 
slm
4 Авг 2018 в 23:18
1
«приведет к уязвимости, связанной с внедрением команд» — не знаю, но когда я переименовываю файлы, никто не придумывает их имена, меня просто не устраивает их текущий шаблон. Поэтому я не знаю, как это может быть соответствующей уязвимостью. Не стесняйтесь поправлять меня, если я что-то упускаю из виду.
 – 
xeruf
6 Авг 2018 в 22:03