Пакетное переименование папок с помощью одной команды bash

Пакетное переименование папок с помощью одной команды bash

У меня папки настроены следующим образом:

/path/to/directory/SLUG_1/SLUG_1 - SLUG_2 - SLUG_3

SLUG_2 — это год, после которого может стоять буква, например «1994» или «2003a».

Я хотел бы переименовать эти файлы в:

/path/to/directory/SLUG_1/SLUG_2 - SLUG_3

Я уже довольно близко подхожу к этой команде:

find $root -mindepth 2 -maxdeth 2 -type d | sed "s#\(\(/path/to/directory/[^/]*/\).* - \([0-9]\{4\}[a-bA-B]\? - .*\)\)#mv "\1" "\2\3"#

Это печатает:

mv "/path/to/directory/SLUG_1/SLUG_1 - SLUG_2 - SLUG_3" "/path/to/directory/SLUG_1/SLUG_2 - SLUG_3"

Это именно та команда, которую я хочу выполнить. Но я не могу ее выполнить.

Я попробовал присвоить вывод переменной и выполнить его, вызвав переменную. Это не сработало. Я попробовал несколько вариантов этой идеи, и у меня возникли ошибки.

Такое чувство, что мне не хватает какого-то инструмента. Какой-то итерационный инструмент, который облегчит эту работу. Есть предложения?

решение1

Предположим, что все подкаталоги /path/to/directoryследуют этому соглашению об именовании:

cd /path/to/directory
prename -n 's~/\d{4}[a-z]? - ~/~i' */*

prenameэтоПереименование Perl(подойдет любой из вариантов).

решение2

Это печатает:

mv ...

Это именно та команда, которую я хочу выполнить. Но я не могу ее выполнить.

Я попробовал присвоить вывод переменной и выполнить его, вызвав переменную. Это не сработало. Я попробовал несколько вариантов этой идеи, и у меня возникли ошибки.

Такое ощущение, что мне здесь не хватает какого-то инструмента.

Простой путь, который вы ищетеэто добавить | bashк вашей команде. Вот как вы можете перейти от "команды, которая печатает команду" к "фактически выполняющей команду, которая была напечатана".

Однако, даже если вы включили двойные кавычки в команду для печати,этоплохая идеявключить в сценарий.

Вы, возможно, разобрались с аспектом пробелов, но что делать, если имена файлов включают символы двойных кавычек напрямую? Или символы новой строки? Или имена переменных (которые будут раскрыты внутри двойных кавычек)?

TheтолькоСимволы, недопустимые в именах файлов, — это косая черта ( /) и нулевой байт.

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

В командной строке вы можете увидеть команду, которую собираетесь запустить, подтвердить ее правильность и затем запустить ее. В сценарии нет контроля и подтверждения. Скрипт сделает то, что вы ему сказали, неважно, насколько разрушительным это может оказаться.

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

решение3

Другой ответ, использование GNU findдля обработки переименования. Этот метод надежен независимо от того, какие символы могут быть в имени файла.

Если я правильно понял ваш вариант использования, вы хотите переименовать каталоги, которые начинаются с полного имени их родительского каталога. Другими словами, если ваш каталог назван так:

/some/path/abcdefghi/abcdefghi - something - else/

Вы хотите переименовать его следующим образом:

/some/path/abcdefghi/something - else/

Поскольку вы указали GNU в качестве тега в этом вопросе, вы можете использовать расширения GNU для findкоманды и обработать это следующим образом:

find . -mindepth 2 -maxdepth 2 -type d -regextype posix-egrep -regex '.*/([^/]+)/\1[^/]+' -exec sh -c 'new="$(sed -r "s:/([^/]+)/\\1 ?-? ?([^/]+)\$:/\\1/\\2:" <<<$1)"; mv "$1" "$new"' find-sh {} \;

Результаты теста:

[vagrant@localhost test]$ mkdir -p SLUG_1/SLUG_{1\ -\ SLUG_{2..4}\ -\ SLUG_5,7\ -\ something}
[vagrant@localhost test]$ find . -type d
.
./SLUG_1
./SLUG_1/SLUG_1 - SLUG_2 - SLUG_5
./SLUG_1/SLUG_1 - SLUG_4 - SLUG_5
./SLUG_1/SLUG_1 - SLUG_3 - SLUG_5
./SLUG_1/SLUG_7 - something
[vagrant@localhost test]$ find . -mindepth 2 -maxdepth 2 -type d -regextype posix-egrep -regex '.*/([^/]+)/\1[^/]+' -exec sh -c 'new="$(sed -r "s:/([^/]+)/\\1 ?-? ?([^/]+)\$:/\\1/\\2:" <<<$1)"; mv "$1" "$new"' find-sh {} \;
[vagrant@localhost test]$ find . -type d
.
./SLUG_1
./SLUG_1/SLUG_3 - SLUG_5
./SLUG_1/SLUG_4 - SLUG_5
./SLUG_1/SLUG_2 - SLUG_5
./SLUG_1/SLUG_7 - something
[vagrant@localhost test]$ 

Связанный контент