Рекурсивно переименовывать каталоги в bash

Я бы хотел рекурсивно переименовать все каталоги, содержащие строку foo, заменив эту часть строки на Bar. У меня есть что-то подобное до сих пор, но это не работает. Мне также хотелось бы, чтобы foo искался без учета регистра.

find . -type d -exec bash -c 'mv "$1" "${1//foo/Bar}"' -- {} \;

Есть ли элегантные однострочные линии, которые могут быть лучше, чем эта попытка? Я на самом деле попробовал несколько, но думал, что буду откладывать на экспертов. Примечание: я делаю это в системе Mac OS X и не имею таких инструментов, как rename.

Ответ 1

Попробуйте использовать следующий код с расширением параметра

find . -type d -iname '*foo*' -depth -exec bash -c '
    echo mv "$1" "${1//[Ff][Oo][Oo]/BAr}"
' -- {} \;

Но лучшим вариантом будет команда prename (иногда называемая rename или file-rename)

find . -type d -iname '*foo*' -depth -exec rename '[email protected]@[email protected]' {} +

И если вы используете bash4 или zsh (** означает рекурсивный):

shopt -s globstar
rename -n '[email protected]@[email protected]' **/*foo*/

Если это соответствует вашим потребностям, удалите переключатель -n (сухой режим) для переименования для реального.

НЕКОТОРЫЕ DOC

rename изначально был написан отцом Перла, самого Ларри Уэлла.

Ответ 2

Я подозреваю, что проблема заключается в том, чтобы заставить его работать с mkdir -p foo/foo/foo.

В этом отношении я думаю, что решение, основанное на find, скорее всего, не сработает, потому что список путей, вероятно, предопределен.

Следующее не является элегантным и растягивает определение однострочного слоя, но работает для вышеуказанного теста.

$ mkdir -p foo/foo/foo

$ (shopt -s nullglob && _() { for P in "$1"*/; do Q="${P//[Ff][Oo][Oo]/bar}"; mv  -- "$P" "$Q"; _ "$Q"; done } && _ ./)

$ find
.
./bar
./bar/bar
./bar/bar/bar

Ответ 3

find . -type d -iname '*foo*' -exec bash -O nocasematch -c \
    '[[ $1 =~ (foo) ]] && mv "$1" "${1//${BASH_REMATCH[1]}/Bar}"' -- {} \;

Pro: Avoids sed.
Кон: Не будет найдено совпадений, если в разных случаях несколько. Кон: Смешно.

Ответ 4

Я искал похожие ответы, и это работало:

find . -depth -name 'foo' -execdir bash -c 'mv "$0" ${0//foo/Bar}"' {} \;

Ответ 5

Спасибо @Gilles Quenot и Wenli: у меня сработало следующее. Я основал это на обоих ваших решениях.

find . -depth -type d -name 'ReplaceMe*'  -execdir bash -c 'mv "$1" "${1/ReplaceMe/ReplaceWith}"' -- {} \;

-execdir, кажется, является ключевым в Linux Red Hat 7,6