2013-04-12 3 views
1

У меня есть сценарий с использованием цикла for, который будет переименовывать папки и файлы. Сценарий возьмет список файлов и папок и переименует их условно. Я хотел бы вызвать файл с помощью команды:Переименование подкаталогов и файлов

find test/* -exec ./replace.sh {} \; 

Мой replace.sh сценарий будет содержать что-то подобное:

for i in [email protected] 
mv $OLDFILE $NEWFILE 
done 

$ старый_файл и $ NewFile было установлено ранее, и я не верю из них возникнут проблемы.

Моя проблема возникает, когда я попадаю в подкаталоги. Первоначально, я бы папки, такие как:

folder_1 

-file1 

-file2 

Когда мой сценарий изменяет folder_1 в folderX1, следующий аргумент, folder_1/file1 woudl недействительным как измененный путь будет folderX1/file1. Я решил, что могу создать стек со списком папок, которые меняются, и выталкивать их позже, чтобы переименовать файлы, но это кажется трудным для bash. Есть ли лучший метод, который мне не хватает?

P.S Я мог бы запускать программу несколько раз, чтобы пройти через все подкаталоги, но это не кажется эффективным.

ответ

2

Вы можете добавить -depth в команду find. Это будет обрабатывать файлы каталога перед самой директорией. См. man find.

1

Ваше использование find проблематично. Первым вариантом является стартовое местоположение для поиска, поэтому вы не хотите использовать там глобус. Если вы хотите только файлы в test/, а не любые его подкаталоги, используйте опцию -depth, как предложил Олаф.

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

Для обработки только верхнего уровня файлов, вы можете сделать это:

$ touch foo.txt bar.txt baz.ext 
$ find test -depth 1 -type f -name \*.txt -exec bash -c 'f="{}"; mv -v "{}" "${f/.txt/.csv}"' \; 
./foo.txt -> ./foo.csv 
./bar.txt -> ./bar.csv 
$ 

Но ваше беспокойство действительно - find построит список матчей, и если ваш -exec изменяет список из под find, некоторые переименования не удастся.

Я подозреваю, что ваше самое быстрое решение - сделать это на двух этапах (не несколько): один для файлов, за которым следует один для каталогов. (Или измените порядок, я не думаю, что это имеет значение.)

$ mkdir foo_1; touch red_2 foo_1/blue_3 
$ find . -type f -name \*_\* -exec bash -c 'f="{}"; mv -v "{}" "${f%_?}X${f##*_}"' \; 
./foo_1/blue_3 -> ./foo_1/blueX3 
./red_2 -> ./redX2 
$ find . -type d -name \*_\* -exec bash -c 'f="{}"; mv -v "{}" "${f%_?}X${f##*_}"' \; 
./foo_1 -> ./fooX1 

Расширение параметра Bash даст вам долгий путь.

Другой вариант, в зависимости от реализации find, является -d вариант:

-d  Cause find to perform a depth-first traversal, i.e., directories 
     are visited in post-order and all entries in a directory will be 
     acted on before the directory itself. By default, find visits 
     directories in pre-order, i.e., before their contents. Note, the 
     default is not a breadth-first traversal. 

Итак:

$ mkdir -p foo_1/bar_2; touch red_3 foo_1/blue_4 foo_1/bar_2/green_5 
$ find . -d -name \*_\* -exec bash -c 'f="{}"; mv -v "{}" "${f%_?}X${f##*_}"' \; 
./foo_1/bar_2/green_5 -> ./foo_1/bar_2/greenX5 
./foo_1/bar_2 -> ./foo_1/barX2 
./foo_1/blue_4 -> ./foo_1/blueX4 
./foo_1 -> ./fooX1 
./red_3 -> ./redX3 
$ 
+0

Вы правы, что Glob ненужно; вы ошибаетесь в том, что это не вредно. Вы можете указать список мест для поиска, прежде чем указывать критерии поиска.Например: 'find/work1/work2/work3/work4/work5 -name '* bug *' -type f' в порядке и может быть сокращен до' find/work [1-5] -name '* bug *' -тип f'. –

+0

@JonathanLeffler - если бы существовал файл, чье имя начиналось с дефиса, 'find' не считал бы начало его списка выражений? Хотя, конечно, можно указать несколько путей, так что * случайно * никогда не может быть хорошим. – ghoti

+0

Имя, начинающееся с тире, будет иметь значение только в том случае, если оно было забито простой звездой. Например, если вы делаете './*', важно не иметь в каталоге такие файлы, как '-delete'. –

Смежные вопросы