根据多种模式重命名文件的更好方法
- 2024-10-11 08:36:00
- admin 原创
- 80
问题描述:
我下载的很多文件的文件名中都有垃圾内容,例如
[ 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}"
我觉得参数扩展是两者中较差的,但我喜欢它,因为我能够在重命名后将相同的变量分配给文件以便进一步处理(上述代码用于在文件下载完成后为每个文件调用的脚本中)。
所以无论如何,我希望有一个更好/更干净的方法来完成上述操作,以便比我更有知识的人可以向我展示,最好以一种允许我轻松地将旧/原始变量重新分配给新/重命名文件的方式。
谢谢
解决方案 1:
两个答案:使用perl重命名或使用纯 狂欢
由于有些人不喜欢 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
命令时它是安全的:文件不会被覆盖,如果出现问题,您会收到一条错误消息。
使用重命名文件狂欢以及所谓的bashisms:
我更喜欢使用专用实用程序来执行此操作,但这甚至可以通过使用纯 狂欢(又名无叉)
除了 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 [*
用点替换空格和方括号
仅用一个替换
.-
、-.
或序列。--
`..`-
测试文件名是否相同,如果相同则无需执行。
测试文件是否存在且使用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
s@/www..*.com - *@/@
s@/[^]]* - *@/@
x
G
s/
/|/
'
}
此命令读取输出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/
/|/ 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
`joinpaste
sort`
解决方案 3:
您可以使用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:要搜索的正则表达式。
replace_part:要替换的字符串
修饰符:i(不区分大小写)、g(全局替换)
大写/小写:
形式为的替换字符串/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 使用 PCRE2(修订的 PCRE)正则表达式。
您可以通过运行撤消不需要的重命名操作
rnm -u
PS:我是这个工具的作者。
解决方案 4:
如果您使用的是 Ubunntu/Debian 操作系统,请使用 rename 命令一次重命名多个文件。
解决方案 5:
如果您想使用不依赖于 perl 的东西,您可以使用以下代码(我们称之为sanitizeNames.sh
)。它只显示了几个案例,但可以使用字符串替换、tr(以及 sed)轻松扩展。
#!/bin/bash
ls $1 |while read f; do
newfname=$(echo "$f" \n |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
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件