
Мне нужно переименовать набор файлов, используя rename
команду (с регулярным выражением). После нескольких попыток я не могу найти выражение, которое даст ожидаемый результат.
У меня есть такой шаблон файла:
префикс_некоторое_имя_другой.txt
Все файлы начинаются со prefix_
строки " " и заканчиваются на " _other.txt
", анекоторое_имячасть может состоять из нескольких (буквенно-цифровых) слов, разделенных подчеркиваниями. Таким образом, возможно иметь:
prefix_one_name_other.txt
prefix_this_is_my_name_1_this1_other.txt
Мне нужно переименовать имена файлов следующим образом:
другой_один-имя_дата и время other_это-мое-имя-1-это1_дата и время
Другими словами:
- Необходимо удалить "
prefix
" (оставив подчеркивание) - Маркер "
other
" помещается в начало имени файла - Внекоторое_имя, преобразовать подчеркивание (_) в тире (-)
- Подчеркивание в конце имени файла (посленекоторое_имя) должен оставаться
- Необходимо удалить
.txt
расширение, заменить надата и время.
Что я пробовал:
rename 's/fw_([a-z]+)_(\d)_(\w+\d)_(\w+)\.txt/$4_$1-$2-$3_'$datahora'/' *.txt
$datahora
имеетдата и времязначение (проверено). Это работает, как и ожидалось, с
prefix_name_1_gnt1_other.txt
но не с
prefix_other_name_2_gnt2_other.txt
Где я ошибся? Как еще я мог этого добиться?
Я повесил голову, так как на данный момент я не могу найти регулярное выражение, которое работает для всех имен файлов, которые у меня есть. Я знаю, что первый элемент в строке всегда является частью prefix
, а последний элемент тогда является other.txt
частью строки. Так что возможно ли разбить строку на массив и получить элементы, которые мне нужны для построения нового имени. На самом деле, что-то вроде этого.
datahora="20140718-080000"
arrfiles=( *.txt )
for curfile in ${arrfiles[*]}
do
arrparts=( ${curfile//_/ } )
numitems=${#arrparts[*]}
newname=""
for (( c=1; c<numitems-1; c++ ))
do
newname+="${arrparts[c]}-"
done
newname=${newname%-}
arrparts[numitems-1]=${arrparts[numitems-1]/.txt/}
newname="${arrparts[numitems-1]}_${newname}_$datahora"
echo "$curfile pasa a $newname"
mv ${curfile} ${newname}
done
После того, как я сделал это таким образом, я еще раз попробовал предложение @peterph и, наконец, закончил с некоторыми комбинациями регулярных выражений переименования. Что-то вроде этого:
rename 's/_/-/g' *.txt
rename 's/^fw-(.*)-([^-]*)(\.txt)/$2.$1$3/' *.txt
rename 's/(\w+)\.(.*)(\.txt)/$1_$2_'$datahora'/' *.txt
Я не уверен, какой подход лучше. По-моему, вариант с регулярным выражением выглядит более элегантно, но мне нужно три операции переименования (три обращения к диску), чтобы выполнить работу, в то время как вариант array
записывает на диск только один раз.
Что вы думаете об этих двух решениях?...
Еще раз спасибо.
решение1
Если только вы не rename
можете принять несколько команд заменыиКорень имени файла ( some_name
) может содержать более одного подчеркивания, это необходимо сделать в два этапа: а) заменить подчеркивания на тире и б) (пере)удалить фрагменты в именах файлов.
Регулярные выражения, которые вы ищете, могут быть, например:
rename 's/_/-/g' *.txt
rename 's/^prefix-(.*)-([^-]*).txt$/$2_$1_'$DATETIME'/' *txt
Первый делает перевод подчеркивания в тире, а последний делает замену корня и суффикса и добавляет содержимое DATETIME
переменной окружения к именам. И, конечно, опускает префикс и расширение.
Часть [^-]*
соответствует любой строке, не содержащей тире. В случае, если суффикс всегда один и тот же, вы можете поместить его туда дословно, как в случае с префиксом (и наоборот - если префикс может меняться, используйте ^[^-]*-
для сопоставления его с любой строкой, не содержащей тире, расположенной между началом имени файла и (таким образом) первым тире).
Если вы rename
поддерживаете несколько команд, просто объедините их:
rename 's/_/-/g;s/^prefix-(.*)-([^-]*).txt$/$2_$1_'$DATETIME'/' *txt