Подтвердить что ты не робот

Поиск + замена строк в именах файлов

Используя bash, как я могу искать все вхождения подстроки 'foo' во всех именах файлов (включая папки), которые рекурсивно содержались в каталоге и заменяли их на "bar"?

Например, если текущая структура выглядит следующим образом:

-foo_test
    - fooo.txt
    - xfoo
        - yfoo.h
- 1foo.c

Он должен выглядеть следующим образом после запуска bash script:

-bar_test
    - baro.txt
    - xbar
        - ybar.h
- 1bar.c
4b9b3361

Ответ 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