Добавив ноль к именам файлов


Я недавно обнаружил силу СЭД; регулярные выражения кажутся они возьмут всю жизнь мастера. Этот крошечный скрипт использует как добавить ноль перед одной цифры в именах файлов.

Это может быть сделано более легко и без СЭД? Это выражение звучит?

#!/bin/bash

# > addzero.sh <
# Adds a zero before the single digit in file names separated by an underscore.
# Designed to help keep files in sequence in directory listings. Otherwise, you get listings like
# "_0.txt, _1.txt, _10.txt, _11.txt, _12.txt, ..., _2.txt, _20.txt, _21.txt, _22.txt, ..."

for filename in $(ls -1 ./*); do
  if [[ $filename =~ _[0-9]\. ]]; then
    new_filename=$(echo $filename | sed -n 's/_\([0-9]\)\./_0\1\./p')
    mv $filename $new_filename
  fi
done


841
2
задан 27 ноября 2011 в 10:11 Источник Поделиться
Комментарии
2 ответа

Что случилось

for filename in $(ls -1 ./*); do

Оуч! Как правило (с очень редкими исключениями), не использовать ЛС в сценарии. То, что вы написали-это почти равносильно с именем в ./*; делать, разве что если есть какие-то непечатные символы, пробельные символы, или \[?* в имена файлов, они будут подогнаны при использовании ЛС. Вам не нужно ./ (за исключением, чтобы убедиться, что имя файла не начинается с -) но это не больно.

[[ $filename =~ _[0-9]\. ]]

могла быть написана в слегка более простым и переносимым способом: [[ $именем = *_[0-9].* ]]. И поскольку вы можете использовать простой Глоб шаблон, вы могли бы также не поработали над все имена файлов: за именем в *_[0-9].*; делать. Но есть лучший способ выразить свой скрипт, чтобы воспользоваться BASH_REMATCH; см. ниже.

echo $filename

Всегда поставить двойные кавычки вокруг переменной и команды заменами. Исключение: когда вы понимаете, почему вы должны оставить двойные цитаты и почему это безопасно. Когда оболочка видит замены переменной ($фу или ${фу}) или команду (`фу` или $(ФОО)) снаружи двойные кавычки, в результате подмены претерпевает разбиение и подстановка (именем поколения). Это была одна из проблем, с $(ЛС -1 ./*) ранее. Это должно быть эхо "$имя".

На самом деле, лучше бы с printf "%S" с "именем", потому что Эхо само выполняет экспансии. В bash, если вы установите нестандартные параметры, чтобы включить обратную косую черту расширение, единственная проблема заключается в том, что еще несколько аргументов, начиная с - слушай, как вариант, и в данном конкретном случае имя файла будет начинаться с ./.

Есть пограничный случай, когда ваш СЕПГ вызов не сработает: если у вас есть файл, имя которого заканчивается символом перевода строки. Этого не происходит на практике, если кто-то допустил ошибку (например, сценарий, изгоев или плохой копипаст) или сознательно пытаетесь обмануть свой сценарий — так что следите в контекстах безопасности.

Кстати, это один из немногих случаев, когда это безопасно, чтобы исключить двойные кавычки вокруг команды замещения: в переменной, существуют неявные пары двойных кавычек в правой стороне, так new_filename=$(...) эквивалентно new_filename="$(...)". Обратите внимание, что это не распространяется на экспорт переменной="$(ценность)", где двойные кавычки необходимы.

Теперь мы переходим к вашему вопросу об использовании СЭД. Не стоит здесь; вы можете выполнять эту замену в bash. Баш имеет рисунок замена параметр подстановки функция, но она ограничивается постоянной замены текста. Баш также способ извлечения подстрок из регулярное выражение соответствует С =~, через BASH_REMATCH переменной. После матча, ${BASH_REMATCH[0]} содержит часть строки соответствует регулярному выражению, ${BASH_REMATCH[1]} содержится часть соответствует первой группе в скобках и так далее.

mv $filename $new_filename

Опять же, двойные кавычки.


Рабочий скрипт

Одна из возможностей заключается, чтобы извлечь текст для замены от матча регулярного выражения, тогда выполняется замена струн на ней. Так как я не использую ./*, имя файла может начинаться с -, так я позабочусь, чтобы использовать -- на звонок в МВ , чтобы убедиться, что имя файла не рассматривается как вариант.

for filename in *; do
if [[ $filename =~ _[0-9]\. ]]; then
from=${BASH_REMATCH[0]}
mv -- "$filename" "${filename//$from/${from/_/_0}}"
fi
done

Другая возможность состоит в том, чтобы соответствовать всем файле название как обработать и соединить с 0 в биты. Обратите внимание, что это будет вести себя по-разному в углу случае: если есть несколько вхождений _[0-9]\., приведенный выше код заменяет первое вхождение, а этот заменяет последнего вхождения.

for filename in *; do
if [[ $filename =~ ^(.*_)([0-9]\..*)$ ]]; then
mv -- "$filename" "${BASH_REMATCH[1]}0${BASH_REMATCH[2]}"
fi
done

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

for filename in *_[0-9].*; do
digit=${filename%.${filename##*_[0-9].}}; digit=${digit##*.}
mv -- "$filename" "${filename%%_[0-9].*}_0${digit}.${filename#*_[0-9].*}"
done

Есть и другие методы для выполнения такого рода переименования файла. Если вы на Debian и производные (в том числе Ubuntu) или переименовать Perl-скрипт, который плавает вокруг (отметим, что это не стандартный Линукс переименовать утилиты):

rename 's/_([0-9]\.)/_0$1/' *_[0-9].*

Или с ММВ:

mmv -x '*_[0-9].*' '#1_0#2.#3'

Или в ЗШ, после загрузки ЗМВ:

zmv '(*_)([0-9].*)' '${1}0$2'

Дальнейшее чтение

В переименовать и цитировании теги на UNIX и стека обмен.

7
ответ дан 28 ноября 2011 в 12:11 Источник Поделиться

Я бы не стал использовать регулярные выражения здесь. Я хотел бы использовать функции printf для рукописного ввода цифр и bash Глоб-модели, чтобы извлечь цифры.

shopt -s extglob
for filename in *; do
tmp=${filename%.txt} # remove the ".txt" extension
digits=${tmp##*_} # remove everything up to the final underscore
case $digits in
+([0-9]))
# 'digits' contains only digits
newname=${filename%_*}_$(printf "%02d" $digits).txt
mv "$filename" "$newname"
;;
esac
done

2
ответ дан 28 ноября 2011 в 01:11 Источник Поделиться