虽然perl
的rename
工具已成为我日常工作的宝贵伴侣,但仍然存在一些怪癖,使工作变得更加困难 - 特别是当文件被不是在当前工作目录中。
假设有很多发票,并且我们知道来自 customer1 的所有发票都已分配给一个帐户。因此,为了使以后的脚本处理更容易,我们希望assi_
在前面添加“已分配”。
例如:
/home/user/bizz/invoices $ ls -1 t1/inv*
t1/inv_customer1_20130506.txt
t1/inv_customer1_20130823.txt
t1/inv_customer1_20130930.txt
t1/inv_customer2_20131207.txt
t1/inv_customer2_20131113.txt
现在看看会发生什么:
/home/user/bizz/invoices $ rename 's/^/assi_/' t1/inv_customer1*
Can't rename t1/inv_customer1_20130506.txt assi_t1/inv_customer1_20130506.txt: No such file or directory
(etc.)
呵呵。rename
已选择了整个路径用于添加assi_
到文件之前,而不仅仅是文件。所以难怪找不到inv_customer1_20130506.txt在不存在的目录“assi_t1”。
那么如何才能rename
教导将正则表达式仅应用于文件名呢?
注意:
1. 我并不总是想cd
进入子目录。2
. 如果可能的话,我会避免使用循环以及find
。
答案1
rename
以这种方式工作是因为它可以在目录之间移动文件。与 一样mv
,它作用于整个路径,而不仅仅是最后一个组件。
如果您只想修改最后一个组件,则可以将正则表达式锚定在(\A|?<=/)
,并确保它不匹配任何/
且仅匹配最后一个/
。
rename 's~(\A|?<=/)(?=[^/]*)\z~assi_~' t1/inv_customer1*
另一种方法是首先分割文件名。
rename 's~(.*/)~~s; my $d = $1; s/\A/assi_/; $_ = "$d$_"' t1/inv_customer1*
您也可以使用该File::Spec
模块。这具有处理非 Unix 路径的优点。
rename 'use File::Spec::Functions; my ($v,$d,$f) = splitpath($_); $f =~ s/\A/assi_/; $_ = catpath($v,$d,$f)' t1/inv_customer1*