我下载的很多文件都有文件名中的垃圾/垃圾邮件,例如
[ 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}"
我觉得参数扩展是两者中最差的,但我喜欢它,因为我能够保留分配给文件的相同变量,以便在重命名后进一步处理(上面的代码用于调用的脚本中)文件下载完成后的每个文件)。
所以无论如何我希望有一个更好/更清洁的方式来做上面的事情,比我自己更有知识的人可以告诉我,最好以一种方式让我轻松地将旧/原始变量重新分配给新的/重命名的文件。
由于
答案 0 :(得分:7)
由于有些人不喜欢perl,我写了我的 bash only version
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
使用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(没有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 \[*
.-
替换-.
,--
,..
或-
的序列。答案 1 :(得分:6)
利用以下经典模式:
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
s@/www\..*\.com - *@/@
s@/\[^]]* - *@/@
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
- 脚本:
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
功能以避免覆盖现有文件并报告有问题的项目。
关于shell程序中的数据结构
注意使用管道将数据从一个阶段传输到另一个阶段:学徒通常依靠变量来表示这些信息,但事实证明这是一个笨拙的选择。相反,最好将数据表示为表格文件或表格数据流从一个流程移动到另一个流程,在这种形式下,数据可以通过强大的工具轻松处理,如sed
,awk
,{ {1}},join
和paste
- 仅引用最常见的内容。
答案 2 :(得分:1)
您可以使用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/\c/modifier
形式的替换字符串将使文件名的选定部分(正则表达式search_part
)小写,而替换部分中的\C
(大写\ 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
您可以找到其他一些示例here。
注意:
rnm -u
P.S:我是这个工具的作者。
答案 3 :(得分:0)
如果您使用的是Ubuntu / Debian,请使用rename命令重命名多个文件。
答案 4 :(得分:0)
如果你想使用不依赖于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