基于多种模式重命名文件的更好方法 [英] Better way to rename files based on multiple patterns
问题描述
我下载的很多文件的文件名中都有废话/垃圾邮件,例如
a lot of files I download have crap/spam in their filenames, e.g.
[ www.crap.com ] file.name.ext
www.crap.com - file.name.ext
我想出了两种处理它们的方法,但它们看起来都很笨拙:
I've come up with two ways for dealing with them but they both seem pretty clunky:
带参数扩展:
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:
and then with echo/sed:
tmp=`echo "${base_name}" | sed -e 's/[[^][]*]//g' | sed -e 's/s-s//g'`
mv "${base_name}" "{tmp}"
我觉得参数扩展是两者中最差的,但我喜欢它,因为我能够在重命名后保留分配给文件的相同变量以供进一步处理(上面的代码用于名为文件下载完成后的每个文件).
I feel like the parameter expansion is the worse of the two but I like it because I'm able to keep the same variable assigned to the file for further processing after the rename (the above code is used in a script that's called for each file after the file download is complete).
所以无论如何,我希望有一种更好/更干净的方法来执行上述操作,让比我知识渊博的人可以向我展示,最好以一种允许我轻松地将旧/原始变量重新分配给新/重命名文件的方式.
So anyway I was hoping there's a better/cleaner way to do the above that someone more knowledgeable than myself could show me, preferably in a way that would allow me to easily reassign the old/original variable to the new/renamed file.
谢谢
推荐答案
两个答案:使用 perl 重命名或使用 pure bash
由于有些人不喜欢 perl,我写了我的 bash only 版本
Two answer: using perl rename or using pure bash
As there are some people who dislike perl, I wrote my bash only version
是的,这是 rename
命令的典型工作,它专门用于:
Yes, this is a typical job for rename
command which was precisely designed for:
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 all .jpg
by numbering from 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
实用程序有一种强大且安全的方法:
Full syntax for matching SO question, in safe way
There is a strong and safe way using rename
utility:
由于这是 perl 常用工具,我们必须使用 perl 语法:
As this is perl common tool, we have to use perl syntax:
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
命令时它是安全的:文件不会被覆盖,如果出现以下情况,您将收到错误消息出了点问题.
... and it's safe while you don't use -f
flag to rename
command: file won't be overwrited and you will get an error message if something goes wrong.
我更喜欢使用专用实用程序来完成此操作,但这甚至可以通过使用 pure bash(也就是没有任何叉子)
I prefer doing this by using dedicated utility, but this could even be done by using pure bash (aka without any fork)
没有使用除 bash 之外的任何其他二进制文件(没有 sed
、awk
、tr
或其他):
There is no use of any other binary than bash (no sed
, awk
, tr
or other):
#!/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
以文件为参数运行,示例:
To be run with files as argument, for sample:
/path/to/my/script.sh [*
- 用点代替空格和方括号
- 仅用一个
替换
..-
、-.
、--
或..
的序列>- - 测试文件名是否相同,则无事可做.
- 测试文件是否存在 newname...
- 拆分文件名、计数器和扩展名,用于制作索引新名称
- 循环,如果一个文件存在 newname
- 最后重命名文件.
- Replacing spaces and square bracket by dot
- Replacing sequences of
.-
,-.
,--
or..
by only one-
. - Test if filename don't differ, there is nothing to do.
- Test if a file exist with newname...
- split filename, counter and extension, for making indexed newname
- loop if a file exist with newname
- Finaly rename the file.
这篇关于基于多种模式重命名文件的更好方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!