Лучший способ переименования файлов на основе нескольких шаблонов
много файлов, которые я загружаю, имеют дерьмо/спам в именах файлов, например.
[ www.crap.com ] file.name.ext
www.crap.com - file.name.ext
У меня есть два способа справиться с ними, но они оба кажутся довольно неуклюжими:
с расширением параметра:
if [[ ${base_name} != ${base_name//\[+([^\]])\]} ]]
then
mv -v "${dir_name}/${base_name}" "${dir_name}/${base_name//\[+([^\]])\]}" &&
base_name="${base_name//\[+([^\]])\]}"
fi
if [[ ${base_name} != ${base_name//www.*.com - /} ]]
then
mv -v "${dir_name}/${base_name}" "${dir_name}/${base_name//www.*.com - /}" &&
base_name="${base_name//www.*.com - /}"
fi
# more of these type of statements; one for each type of frequently-encountered pattern
а затем с помощью echo/sed:
tmp=`echo "${base_name}" | sed -e 's/\[[^][]*\]//g' | sed -e 's/\s-\s//g'`
mv "${base_name}" "{tmp}"
Мне кажется, что расширение параметра хуже из двух, но мне оно нравится, потому что я могу сохранить одну и ту же переменную, назначенную файлу для дальнейшей обработки после переименования (приведенный выше код используется в script который вызывается для каждого файла после завершения загрузки файла).
Так или иначе, я надеялся, что там будет лучший/более чистый способ сделать выше, чем кто-то более осведомленный, чем я, может показать мне, предпочтительно таким образом, чтобы я мог легко переназначить старую/оригинальную переменную в новый/переименованный файл,
Спасибо
Ответы
Ответ 1
Два ответа: использование perl переименовать или используя чистый bash
Как есть некоторые люди, которым не нравится perl, я написал свою версию bash только
Переименование файлов с помощью команды rename
.
Введение
Да, это типичное задание для команды rename
, которая была специально разработана для:
man rename | sed -ne '/example/,/^[^ ]/p'
For example, to rename all files matching "*.bak" to strip the
extension, you might say
rename 's/\.bak$//' *.bak
To translate uppercase names to lower, you'd use
rename 'y/A-Z/a-z/' *
Более ориентированные образцы
Просто отбросьте все пробелы и квадратные скобки:
rename 's/[ \[\]]*//g;' *.ext
Переименуйте все .jpg
нумерацией из 1
:
rename 's/^.*$/sprintf "IMG_%05d.JPG",++$./e' *.jpg
Демо:
touch {a..e}.jpg
ls -ltr
total 0
-rw-r--r-- 1 user user 0 sep 6 16:35 e.jpg
-rw-r--r-- 1 user user 0 sep 6 16:35 d.jpg
-rw-r--r-- 1 user user 0 sep 6 16:35 c.jpg
-rw-r--r-- 1 user user 0 sep 6 16:35 b.jpg
-rw-r--r-- 1 user user 0 sep 6 16:35 a.jpg
rename 's/^.*$/sprintf "IMG_%05d.JPG",++$./e' *.jpg
ls -ltr
total 0
-rw-r--r-- 1 user user 0 sep 6 16:35 IMG_00005.JPG
-rw-r--r-- 1 user user 0 sep 6 16:35 IMG_00004.JPG
-rw-r--r-- 1 user user 0 sep 6 16:35 IMG_00003.JPG
-rw-r--r-- 1 user user 0 sep 6 16:35 IMG_00002.JPG
-rw-r--r-- 1 user user 0 sep 6 16:35 IMG_00001.JPG
Полный синтаксис для сопоставления SO-запроса безопасным способом
Существует мощный и безопасный способ использования утилиты rename
:
Как это perl общий инструмент, мы должны использовать синтаксис perl:
rename 'my $o=$_;
s/[ \[\]]+/-/g;
s/-+/-/g;
s/^-//g;
s/-\(\..*\|\)$/$1/g;
s/(.*[^\d])(|-(\d+))(\.[a-z0-9]{2,6})$/
my $i=$3;
$i=0 unless $i;
sprintf("%s-%d%s", $1, $i+1, $4)
/eg while
$o ne $_ &&
-f $_;
' *
Правило тестирования:
touch '[ www.crap.com ] file.name.ext' 'www.crap.com - file.name.ext'
ls -1
[ www.crap.com ] file.name.ext
www.crap.com - file.name.ext
rename 'my $o=$_; ...
...
...' *
ls -1
www.crap.com-file.name-1.ext
www.crap.com-file.name.ext
touch '[ www.crap.com ] file.name.ext' 'www.crap.com - file.name.ext'
ls -1
www.crap.com-file.name-1.ext
[ www.crap.com ] file.name.ext
www.crap.com - file.name.ext
www.crap.com-file.name.ext
rename 'my $o=$_; ...
...
...' *
ls -1
www.crap.com-file.name-1.ext
www.crap.com-file.name-2.ext
www.crap.com-file.name-3.ext
www.crap.com-file.name.ext
... и так далее...
... и он безопасен, пока вы не используете флаг -f
для rename
: файл не будет перезаписан, и вы получите сообщение об ошибке, если что-то пойдет не так.
Переименование файлов с помощью bash и так называемые базисы:
Я предпочитаю делать это с помощью специальной утилиты, но это можно сделать даже с помощью чистого bash (он же без какой-либо вилки)
Нет никакого другого двоичного кода, кроме bash (no sed
, awk
, tr
или другого):
#!/bin/bash
for file;do
newname=${file//[ \]\[]/.}
while [ "$newname" != "${newname#.}" ] ;do
newname=${newname#.}
done
while [ "$newname" != "${newname//[.-][.-]/.}" ] ;do
newname=${newname//[.-][.-]/-};done
if [ "$file" != "$newname" ] ;then
if [ -f $newname ] ;then
ext=${newname##*.}
basename=${newname%.$ext}
partname=${basename%%-[0-9]}
count=${basename#${partname}-}
[ "$partname" = "$count" ] && count=0
while printf -v newname "%s-%d.%s" $partname $[++count] $ext &&
[ -f "$newname" ] ;do
:;done
fi
mv "$file" $newname
fi
done
Для запуска с файлами в качестве аргумента для образца:
/path/to/my/script.sh \[*
- Замена пробелов и квадратных скобок точкой
- Замена последовательностей
.-
, -.
, --
или ..
только одним -
.
- Проверьте, не отличается ли имя файла, нечего делать.
- Проверить, существует ли файл с новым именем...
- разделять имя файла, счетчик и расширение, для создания индексированного newname
- если файл существует с новым именем
- Окончательно переименуйте файл.
Ответ 2
Воспользуйтесь следующей классической схемой:
job_select /path/to/directory| job_strategy | job_process
где job_select
отвечает за выбор объектов вашего задания, job_strategy
готовит план обработки для этих объектов, а job_process
в конечном итоге выполняет план.
Предполагается, что имена файлов не содержат вертикальную полосу |
и символ новой строки.
Функция job_select
# job_select PATH
# Produce the list of files to process
job_select()
{
find "$1" -name 'www.*.com - *' -o -name '[*] - *'
}
Команда find
может проверять все свойства файла, поддерживаемого файловой системой, такие как время создания, время доступа, время модификации. Также можно контролировать, как файловая система исследуется, сообщая find
не опускаться в смонтированные файловые системы, сколько допустимых уровней рекурсий. Обычно добавлять команды в команду find
для выполнения более сложных выборов на основе имени файла.
Избегайте общей ошибки, связанной с содержимым скрытых каталогов на выходе функции job_select
. Например, каталоги CVS
, .svn
, .svk
и .git
используются соответствующими средствами управления средствами управления версиями, и почти всегда неправильно включать их содержимое в вывод функции job_select
. Посредством случайной пакетной обработки этих файлов можно легко сделать поврежденную рабочую копию непригодной.
Функция job_strategy
# job_strategy
# Prepare a plan for renaming files
job_strategy()
{
sed -e '
h
[email protected]/www\..*\.com - *@/@
[email protected]/\[^]]* - *@/@
x
G
s/\n/|/
'
}
Эти команды считывают вывод job_select
и составляют план для нашего задания на переименование. План представлен текстовыми строками, имеющими два поля, разделенных символом |
, причем первым полем является старое имя файла, а второе - новый вычисленный файл файла, он выглядит как
[ www.crap.com ] file.name.1.ext|file.name.1.ext
www.crap.com - file.name.2.ext|file.name.2.ext
Конкретная программа, используемая для составления плана, по существу не имеет значения, но обычно используется sed
, как в примере; awk
или perl
для этого. Пройдем через sed
- script, который используется здесь:
h Replace the contents of the hold space with the contents of the pattern space.
… Edit the contents of the pattern space.
x Swap the contents of the pattern and hold spaces.
G Append a newline character followed by the contents of the hold space to the pattern space.
s/\n/|/ Replace the newline character in the pattern space by a vertical bar.
Для подготовки плана может быть проще использовать несколько фильтров. Другим распространенным случаем является использование команды stat
для добавления времени создания к именам файлов.
Функция job_process
# job_process
# Rename files according to a plan
job_process()
{
local oldname
local newname
while IFS='|' read oldname newname; do
mv "$oldname" "$newname"
done
}
Разделитель IFS поля ввода настроен так, чтобы функция считывала вывод job_strategy
. Объявление oldname
и newname
, поскольку локально полезно в больших программах, но может быть опущено в очень простых скриптах. Функция job_process
может быть скорректирована, чтобы избежать перезаписи существующих файлов и сообщить о проблемных элементах.
О структурах данных в программах оболочки
Обратите внимание на использование труб для передачи данных с одного этапа на другой: ученики часто полагаются на переменные для представления такой информации, но это оказывается неуклюжий выбор. Вместо этого предпочтительно представлять данные в виде табличных файлов или в виде потоков табличных данных, перемещающихся от одного процесса к другому, в этой форме данные могут быть легко обработаны мощными инструментами, такими как sed
, awk
, join
, paste
и sort
- только для цитирования наиболее распространенных.
Ответ 3
Если вы используете команду Ubunntu/Debian os, переименуйте команду переименовать несколько файлов во время.
Ответ 4
Если вы хотите использовать что-то, не зависящее от perl, вы можете использовать следующий код (позвоните ему sanitizeNames.sh
). Он показывает только несколько случаев, но легко расширяется с помощью подстановки строк, tr (и sed тоже).
#!/bin/bash
ls $1 |while read f; do
newfname=$(echo "$f" \
|tr -d '\[ ' \ # Removing opened square bracket
|tr ' \]' '-' \ # Translating closing square bracket to dash
|tr -s '-' \ # Squeezing multiple dashes
|tr -s '.' \ # Squeezing multiple dots
)
newfname=${newfname//-./.}
if [ -f "$newfname" ]; then
# Some string magic...
extension=${newfname##*\.}
basename=${newfname%\.*}
basename=${basename%\-[1-9]*}
lastNum=$[ $(ls $basename*|wc -l) ]
mv "$f" "$basename-$lastNum.$extension"
else
mv "$f" "$newfname"
fi
done
И используйте его:
$ touch '[ www.crap.com ] file.name.ext' 'www.crap.com - file.name.ext' '[ www.crap.com ] - file.name.ext' '[www.crap.com ].file.anothername.ext2' '[www.crap.com ].file.name.ext'
$ ls -1 *crap*
[ www.crap.com ] - file.name.ext
[ www.crap.com ] file.name.ext
[www.crap.com ].file.anothername.ext2
[www.crap.com ].file.name.ext
www.crap.com - file.name.ext
$ ./sanitizeNames.sh *crap*
$ ls -1 *crap*
www.crap.com-file.anothername.ext2
www.crap.com-file.name-1.ext
www.crap.com-file.name-2.ext
www.crap.com-file.name-3.ext
www.crap.com-file.name.ext
Ответ 5
Вы можете использовать rnm
rnm -rs '/\[crap\]|\[spam\]//g' *.ext
Вышеупомянутое удалит [crap]
или [spam]
из имени файла.
Вы можете передать несколько шаблонов регулярных выражений, завершая их с помощью ;
или перегружая параметр -rs
.
rnm -rs '/[\[\]]//g;/\s*\[crap\]//g' -rs '/crap2//' *.ext
Общий формат этой строки замены /search_part/replace_part/modifier
- search_part: regex для поиска.
- replace_part: строка для замены
- модификатор: я (регистр нечувствителен), g (глобальная замена)
прописные/строчные буквы:
Заменить строку формы /search_part/\c/modifier
сделает выбранную часть имени файла (с помощью регулярного выражения search_part
) в нижнем регистре, а \C
(capital\C) в замещающей части сделает его прописным.
rnm -rs '/[abcd]/\C/g' *.ext
## this will capitalize all a,b,c,d in the filenames
Если у вас много шаблонов регулярных выражений, которые нужно решать, затем поместите эти шаблоны в файл и передайте файл с опцией -rs/f
.
rnm -rs/f /path/to/regex/pattern/file *.ext
Здесь вы можете найти другие примеры .
Примечание:
- rnm использует регулярное выражение PCRE2 (исправленное PCRE).
- Вы можете отменить операцию нежелательного переименования, запустив
rnm -u
P.S: Я являюсь автором этого инструмента.