
У меня папки настроены следующим образом:
/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]$