Поиск + замена строк в именах файлов
Используя bash, как я могу искать все вхождения подстроки 'foo' во всех именах файлов (включая папки), которые рекурсивно содержались в каталоге и заменяли их на "bar"?
Например, если текущая структура выглядит следующим образом:
-foo_test
- fooo.txt
- xfoo
- yfoo.h
- 1foo.c
Он должен выглядеть следующим образом после запуска bash script:
-bar_test
- baro.txt
- xbar
- ybar.h
- 1bar.c
Ответы
Ответ 1
Оба варианта, показанные здесь, корректно работают на тестовой структуре OPs:
find . -depth -name '*foo*' -execdir bash -c 'mv -i "$1" "${1//foo/bar}"' bash {} \;
или, если у вас очень большое количество файлов и вы хотите, чтобы он работал быстрее:
find . -depth -name '*foo*' -execdir bash -c 'for f; do mv -i "$f" "${f//foo/bar}"; done' bash {} +
EDIT. Как отмечалось в комментариях, мой более ранний ответ с использованием команды find
, которая не использовала параметр execdir
и используя rename
, имеет проблемы с переименованием файлов в каталогах, содержащих foo от их имени. Как было предложено, я изменил команды find на использование -execdir
, и я удалил вариацию с помощью команды rename
, так как это нестандартная команда.
Ответ 2
Это было сложно из-за имен directory с несколькими экземплярами "foo". Когда вы меняете ./foo_test/xfoo
на ./bar_test/xbar
, все, что было в ./foo_test
, становится недоступным. Поэтому я сначала изменил имена файлов, а затем изменил последнее вхождение "foo" в именах каталогов. Я добавил эхо-заявления, чтобы отслеживать, что происходит во время разработки. Вы можете, конечно, удалить их.
#!/bin/sh
#first change the file names
#append '.' to process files in current directory
for D in $(find -d . -name "*foo*" -type d ) '.'
do
pushd $D >> /dev/null
echo 'directory: ' "$D"
for file in $(find . -name "*foo*" -type f -maxdepth 1)
do
echo ' change' "$file" 'to' `echo "$file" | sed s/foo/bar/g`
mv "$file" `echo "$file" | sed s/foo/bar/g`
done
popd >> /dev/null
done
echo ''
#Now change the directory names
for D in $(find -d . -name "*foo*" -type d )
do
echo 'change' "$D" 'to' `echo "$D" | sed 's/\(.*\)foo/\1bar/'`
#change only the last occurance of foo
mv "$D" `echo "$D" | sed 's/\(.*\)foo/\1bar/'`
done
Я не сомневаюсь, что есть более короткие и более элегантные способы сделать это (возможно, просто удалив половину строк в этом script), но я уверен, что это работает.
ИЗМЕНИТЬ
Тождественные петли были красным флагом. Эта версия только петли один раз. Вы получаете сообщение mv '.' '.'
, но оно безопасно игнорируется.
#!/bin/sh
#first change the file names
#append '.' to change file in current directory
for D in $(find -d . -name "*foo*" -type d ) '.'
do
pushd $D >> /dev/null
echo 'directory: ' "$D"
for file in $(find . -name "*foo*" -type f -maxdepth 1)
do
echo ' change' "$file" 'to' `echo "$file" | sed s/foo/bar/g`
mv "$file" `echo "$file" | sed s/foo/bar/g`
done
popd >> /dev/null
echo 'change' "$D" 'to' `echo "$D" | sed 's/\(.*\)foo/\1bar/'`
#change only the last occurence of foo
mv "$D" `echo "$D" | sed 's/\(.*\)foo/\1bar/'`
done