Переименовать подмножество файлов в каталоге

У меня есть много сотен тысяч файлов в каталоге. Эти файлы имеют следующие имена:

left-00001.tiff left-00002.tiff ... left-99999.tiff left-100000.tiff ... left-245000.tiff 

Я хотел бы переименовать файлы следующим образом:

 left-000001.tiff ... left-099999.tiff ... left-245000.tiff 

Я нашел элегантное решение этой проблемы здесь .

Это решение реализует bash-скрипт с именем zeropad.sh . bash кодируется следующим образом:

 #!/bin/bash num=`expr match "$1" '[^0-9]*\([0-9]\+\).*'` paddednum=`printf "%06d" $num` echo ${1/$num/$paddednum} 

и может применяться итеративно с использованием for loop следующим образом:

 for i in *.tiff;do mv $i `./zeropad.sh $i`; done 

Однако это решение занимает очень много времени, потому что оно выполняет много ненужной работы, переименовывая все файлы, которые уже заполнены должным образом. ie as %06d type numbers . Для моих собственных целей это решение очень медленное.

У меня есть два вопроса:

1- Как я могу изменить iterator, чтобы применить zeropad.sh только к файлам, которые должны быть дополнены нулями?

2- Как я могу использовать команду touch в for loop для генерации тестовых данных? Очень важно убедиться, что этот скрипт работает, прежде чем применять его к исходным данным.

6 Solutions collect form web for “Переименовать подмножество файлов в каталоге”

Большая часть времени, потраченного вашим циклом, вероятно, zeropad.sh вызовом вашего скрипта zeropad.sh .

Вместо этого сделайте все это одним скриптом:

 #!/bin/bash for filename in left-*.tiff; do if [[ "$filename" =~ ^left-0*([1-9]?[0-9]+)\.tiff$ ]]; then num=${BASH_REMATCH[1]} newname="left-$( printf '%06d' "$num" ).tiff" if [ "$filename" != "$newname" ] && [ ! -e "$newname" ]; then echo mv "$filename" "$newname" fi fi done 

Удалите echo только вы убедитесь, что скрипт выполняет правильные действия.

Вот как я обычно это делаю (вручную на оболочке):

 rename left- left-0 left-?.png # for 0-9 rename left- left-0 left-??.png # for 00-99 rename left- left-0 left-???.png # for 000-999 # result left-0000.png - left-9999.png 

Это легко сделать в интерактивном сеансе оболочки … просто повторите последнюю команду с одной дополнительной ? добавлено.

Однако, с большим количеством файлов, вы в конечном итоге получите слишком длинный список аргументов. И, очевидно, это не самый эффективный выбор, поскольку он переименовывает один и тот же файл несколько раз (left-1.png -> left-01.png -> left-001.png -> …).

Также есть два варианта rename : один с регулярными выражениями perl, а другой без. В зависимости от дистрибутива вы rename.ul или perl-rename rename.ul или другие имена для них. По сути, он делает любой сценарий, использующий команду rename непереносимым, поскольку вы никогда не знаете, чего ожидать.

Я использую переименование util-linux, и ваш вопрос на самом деле является одним из их примеров со страницы руководства:

 EXAMPLES Given the files foo1, ..., foo9, foo10, ..., foo278, the commands rename foo foo00 foo? rename foo foo0 foo?? will turn them into foo001, ..., foo009, foo010, ..., foo278. 

Какой метод является более эффективным (каждый файл переименовывается только один раз), но вы должны выяснить, правильное распределение 000 против ??? или вы получите неправильный результат.

На мой взгляд, неэффективный метод является более практичным в интерактивной оболочке при работе с небольшим набором файлов.


Преимущество rename сравнению со сценарием самостоятельно заключается в том, что он не должен порождать один процесс mv для каждого файла или, как в вашем случае, подпрограмму только для того, чтобы выяснить имя файла. Непонятно, какие дополнительные издержки, процесс порождения или повторное переименование, и мне лень его сравнивать.

На самом деле ответ, который вы связали, уже содержит «оптимальное» решение в самом конце … используя perl-rename:

 rename 's/\d+/sprintf("%04d",$&)/e' *.png 

Ну, можно спорить о регулярном выражении, но суть в том, что все это можно сделать за один раз, без ненужных mv или порождения процессов. Если вам все еще нужно это улучшить, напишите инструмент, который читает содержимое каталога напрямую, а не использует глобализацию оболочки (которая сортирует, которая медленная) и выполняет переименование по мере необходимости.

Может быть, на самом деле это ответ, с которым вы связаны, и, возможно, именно поэтому вы получаете отрицательный голос. 😉

То, что является дорогостоящим, – это разветвлять так много процессов и запускать так много команд для каждого файла.

С zsh :

 zmodload zsh/files # make mv builtin to speed things up autoload zmv zmv -n '(*-)(< ->)(.tiff)' '$1${(l:6::0:)2}$3' 

(удаляй -n когда счастлив)

Это все со встроенными функциями, так что не работает ни процесс, ни исполняемый файл.

Или с rename perl :

 rename -n 's/\d+(?=\.tiff\z)/sprintf "%06d", $&/e' ./*[0-9].tiff 

Для первой части рассмотрим:

 for i in left-?????.tiff left-????.tiff left-???.tiff left-??.tiff left-?.tiff ...` 

Если это создает слишком много файлов, разбейте его на разделы:

 for i in left-?????.tiff ...` 

 for i in left-????.tiff ...` 

Выше работает с помощью ? глобус символ, чтобы заменить любой отдельный символ, когда он появляется. Здесь я специально запросил 5, 4, 3, 2, а затем 1 цифру после ведущего left- .

Для второй части, один вариант:

 dir=$(mktemp) cd "$dir" for i in $(seq 10); do touch $(printf 'left-%05d.tiff' $((RANDOM % 10000))); done 

Настройте seq 10 чтобы генерировать больше или меньше имен файлов. Настройте % 10000 чтобы генерировать меньшие или большие числа. Обратите внимание, что $ RANDOM bash генерирует числа от 0 до 32 767.

Я люблю Perl однострочников:

 ls left-*.tiff | perl -ne 'if(m/(\S+)-(\d+).tiff/){chomp;printf "mv $_ left-%06d.tiff\n", $2}' | bash 

PS, убедитесь, что дважды проверили выход, прежде чем пускать в bash . Просто чтобы быть в безопасности.

Вы можете переименовать все файлы параллельно. Внесите следующие тривиальные изменения в тот же медленный код, который вы указали в своем вопросе, следующим образом:

 cd data_folder # cd the folder where you put the *.tiff files for i in *.tiff;do { mv $i `./zeropad.sh $i`; }& 

Это переименует все файлы одновременно. Помните, что на рабочей станции должно быть достаточно ресурсов памяти, прежде чем запускать этот код в папке, содержащей файлы *.tiff . Недостаточно ресурсов памяти может привести к сбою памяти. Но, учитывая, что процесс переименовывает только файлы, все должно быть в порядке!

Для того, чтобы учесть ресурсы памяти на вашей рабочей станции. Сохраните следующий код в файле с именем code , дайте ему разрешения и запустите его:

 mem=$(free -m | awk 'NR==2{printf "Memory Usage: %s/%sMB (%.2f%%)\n", $3,$2,$3*100/$2 }' | grep Memory | awk '{print $3}' | tr -d "()%MB" | cut -d / -f 2 ) for i in *.tiff;do { mv $i `./zeropad.sh $i`; }& if [ $mem -lt 100000 ] then if (( "$i" % 75 == 0 )) then sleep 4 fi fi if [ $mem -gt 100000 ] then if (( "$i" % 300 == 0 )) then sleep 3 fi fi done 

Когда вы запускаете code , он проверяет ресурсы памяти на вашей рабочей станции, используя переменную mem . Если объем памяти меньше 100000MB , он переименует 75 files одновременно. Если объем памяти превышает 100000MB , он будет переименовывать до 300 files одновременно. Тем не менее, вы можете настроить все переменные, как вы хотите.

Linux и Unix - лучшая ОС в мире.