Bash删除文件名的一部分

15

我有以下文件,格式如下:

$ ls CombinedReports_LLL-*'('*.csv
CombinedReports_LLL-20140211144020(Untitled_1).csv
CombinedReports_LLL-20140211144020(Untitled_11).csv
CombinedReports_LLL-20140211144020(Untitled_110).csv
CombinedReports_LLL-20140211144020(Untitled_111).csv
CombinedReports_LLL-20140211144020(Untitled_12).csv
CombinedReports_LLL-20140211144020(Untitled_13).csv
CombinedReports_LLL-20140211144020(Untitled_14).csv
CombinedReports_LLL-20140211144020(Untitled_15).csv
CombinedReports_LLL-20140211144020(Untitled_16).csv
CombinedReports_LLL-20140211144020(Untitled_17).csv
CombinedReports_LLL-20140211144020(Untitled_18).csv
CombinedReports_LLL-20140211144020(Untitled_19).csv

我希望删除这部分内容:
20140211144020(这是报告运行的时间戳,因此会有所变化)

最终应该得到类似这样的结果:

CombinedReports_LLL-(Untitled_1).csv
CombinedReports_LLL-(Untitled_11).csv
CombinedReports_LLL-(Untitled_110).csv
CombinedReports_LLL-(Untitled_111).csv
CombinedReports_LLL-(Untitled_12).csv
CombinedReports_LLL-(Untitled_13).csv
CombinedReports_LLL-(Untitled_14).csv
CombinedReports_LLL-(Untitled_15).csv
CombinedReports_LLL-(Untitled_16).csv
CombinedReports_LLL-(Untitled_17).csv
CombinedReports_LLL-(Untitled_18).csv
CombinedReports_LLL-(Untitled_19).csv

我只是想简单地沿着mv命令的思路去做,也许像这样:

$ ls CombinedReports_LLL-*'('*.csv

但也许使用 sed 命令或其他方法可能更好。


可能是批量删除文件名中的一部分的重复问题。 - Joel
5个回答

20

rename 是 Perl 包的一部分。它根据 Perl 风格的正则表达式重命名文件。要从文件名中删除日期:

rename 's/[0-9]{14}//' CombinedReports_LLL-*.csv

如果没有可用的 rename 命令,可以使用 sed+shell

for fname in Combined*.csv ; do mv "$fname" "$(echo "$fname" | sed -r 's/[0-9]{14}//')" ; done

上面的代码循环遍历每个文件。对于每个文件,它执行一个mv命令:mv "$fname" "$(echo "$fname" | sed -r 's/[0-9]{14}//')" 在这种情况下,sed可以使用与上面的rename命令相同的正则表达式。s/[0-9]{14}//告诉sed查找连续14个数字,并用空字符串替换它们。

1
不需要使用sed -- 现代的shell可以内置自己的替换功能。 - Charles Duffy
@CharlesDuffy (1) 我通常的目标是实现与Dash兼容,不仅因为它比Bash快得多,而且因为纯shell更受广泛支持。(2) 虽然Bash可以进行原始替换,但sed的正则表达式最终更加强大。 - John1024
然而,最大的区别在于效率;如果你正在遍历数百个文件,那么分叉一个子shell、在其中exec sed、等待该子shell完成、读取其输出等操作所带来的时间惩罚将会迅速累积。 - Charles Duffy
此外,使用命令替换也会产生副作用 -- 每当您使用 $(foo) 时,IFS 中的尾随字符都会被剥离,因此即使这不是您想要的行为,以空格结尾的文件名也会被剥离其空格。 - Charles Duffy
1
rename might works a little bit different than described by @John1024: rename from to files. And with the same examples: rename 20140211144020 '' CombinedReports_LLL-*'('*.csv - user890739
显示剩余2条评论

9

不使用其他工具,如renamesed,严格遵循仅限于bash:

for f in CombinedReports_LLL-*.csv
do
  newName=${f/LLL-*\(/LLL-(}
  mv -i "$f" "$newName"
done

+1。但为什么不直接将所有内容放入mv命令中呢?mv -i "$f" "${f/LLL-*\(/LLL-(}" - BMW
3
为什么?当然是为了清晰明了。深入了解其他程序员的内心世界,猜测哪种解决方案会被最好地理解。顺便说一下,根据提问者的问题表述方式,我的猜测是他会更好地理解具有口语化变量名称的解决方案,比如 newName - Alfe
那是标准的正则表达式吗?还是某种特定于Bash的类型?我找不到有关Bash特定的正则表达式语言的文档,但不明白为什么会重复出现“LLL”或第二个开括号。 - Chris
这不是正则表达式。它更像是一种glob模式替换。请参阅man bash并搜索“Pattern substitution”。 - Alfe

2
for f in CombinedReports_LLL-* ; do
    b=${f:0:20}${f:34:500}
    mv "$f" "$b"
done

您可以在shell上逐行尝试:
f="CombinedReports_LLL-20140211144020(Untitled_11).csv"
b=${f:0:20}${f:34:500}
echo $b

1
希望所有数字(时间戳)的宽度都相同。然而,任何阅读该代码的人都必须猜测这些常量(02034500)来自何处以及它们的含义是什么。当然,猜测并不是不可能的,但这也不是最清晰的表达方式。 - Alfe

0
你可以使用 rename 工具来完成这个任务。它的语法类似于 sed,用于更改文件名。下面是一个例子(来自 rename 手册),展示如何从本地目录中的备份文件列表中删除尾随的 '.bak' 扩展名:
rename 's/\.bak$//' *.bak

我目前的Cygwin版本中没有rename命令。是否有其他方法? - HattrickNZ

0

我正在使用顶部回复中给出的建议,并将以下行放入shell脚本中:

ls *.nii | xargs rename 's/[f_]{2}//' f_0*.nii

在终端中,这行代码可以完美运行,但在我的脚本中却无法执行,并将 * 作为文件名的一个字面部分读取。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接