我下载的很多文件都有垃圾文件,例如
[ 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}"
我觉得参数扩展是更糟糕的两个,但我喜欢它,因为我能够保持相同的variables分配给文件进行进一步处理后重命名(上面的代码是在一个脚本中使用的每个文件文件下载完成后)。
所以无论如何,我希望有一个更好的/更清洁的方式来做到这一点,比我更有知识的人可以告诉我,最好以一种方式,让我很容易重新分配旧/原来的variables到新的/重命名的文件。
谢谢
因为有些人不喜欢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/AZ/az/' *
简单地删除所有空格和方括号 :
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 \[*
.-
, -.
序列-.
, --
或者只有一个-
。 利用以下经典模式:
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
函数的输出中的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
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
声明为local在大型程序中很有用,但可以在非常简单的脚本中省略。 可以调整job_process
函数以避免覆盖现有文件并报告有问题的项目。
关于shell程序中的数据结构请注意,使用管道将数据从一个阶段传输到另一个阶段:学徒通常依靠变量来表示这样的信息,但事实证明这是一个笨拙的选择。 相反,最好将数据表示为表格文件,或将数据表示为从一个进程移动到另一个进程的表格数据流,在这种形式下,数据可以通过sed
, awk
, join
, paste
和sort
等强大的工具轻松处理 – 仅用于引用最常见的。
如果您使用Ubunntu / Debian os use rename命令来重命名多个文件。
如果你想使用不依赖于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
你可以使用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
你可以在这里找到一些其他的例子。
注意:
rnm -u
来撤销不需要的重命名操作 PS:我是这个工具的作者。