我必须使用命令rename
(带正则表达式)重命名一组文件。经过几次尝试,我找不到获得预期结果的表达式。
我有一个这样的文件模式:
字首_某个名字_其他.txt
所有文件都以“ prefix_
”字符串开头,以“ ”结尾_other.txt
,并且某个名字部分可以由下划线分隔的多个(字母数字)单词组成。因此,可以有:
prefix_one_name_other.txt
prefix_this_is_my_name_1_this1_other.txt
我需要重命名文件名,如下所示:
other_one-name_约会时间 other_这是我的名字-1-this1_约会时间
换句话说:
- 需要删除“
prefix
”(保留下划线) - “
other
”标记转到文件名的开头 - 在某个名字,将下划线 (_) 转换为破折号 (-)
- 文件名末尾的下划线(在某个名字)必须保留
- 需要删除
.txt
扩展名,替换为约会时间。
我尝试过的:
rename 's/fw_([a-z]+)_(\d)_(\w+\d)_(\w+)\.txt/$4_$1-$2-$3_'$datahora'/' *.txt
$datahora
有约会时间值(已测试)。这按预期工作
prefix_name_1_gnt1_other.txt
但不是
prefix_other_name_2_gnt2_other.txt
我哪里做错了?我还能怎样做到?
我一直犹豫不决,因为目前我无法找到一个适用于我拥有的所有文件名的正则表达式。我知道,字符串中的第一个元素始终是部分prefix
,最后一个元素是other.txt
字符串的一部分。因此可以将字符串拆分为数组,并获取构建新名称所需的项目。事实上,就是这样。
datahora="20140718-080000"
arrfiles=( *.txt )
for curfile in ${arrfiles[*]}
do
arrparts=( ${curfile//_/ } )
numitems=${#arrparts[*]}
newname=""
for (( c=1; c<numitems-1; c++ ))
do
newname+="${arrparts[c]}-"
done
newname=${newname%-}
arrparts[numitems-1]=${arrparts[numitems-1]/.txt/}
newname="${arrparts[numitems-1]}_${newname}_$datahora"
echo "$curfile pasa a $newname"
mv ${curfile} ${newname}
done
完成此操作后,我再次尝试了@peterph 的建议,最终使用一些重命名正则表达式组合完成了操作。 如下:
rename 's/_/-/g' *.txt
rename 's/^fw-(.*)-([^-]*)(\.txt)/$2.$1$3/' *.txt
rename 's/(\w+)\.(.*)(\.txt)/$1_$2_'$datahora'/' *.txt
我不确定哪种方法最好。在我看来,正则表达式变体似乎更优雅,但我需要三次重命名操作(三次访问磁盘)才能完成工作,而该array
变体仅将数据写入磁盘一次。
您对于这两个解决方案有何看法?...
再次感谢。
答案1
除非你rename
可以接受多个替换命令和文件名的根目录(some_name
)可能包含多个下划线,您必须分两步执行此操作:a)用破折号替换下划线和b)(重新)移动文件名中的块。
您正在寻找的正则表达式可以是例如:
rename 's/_/-/g' *.txt
rename 's/^prefix-(.*)-([^-]*).txt$/$2_$1_'$DATETIME'/' *txt
第一个将下划线转换为破折号,而后者将根和后缀交换并将DATETIME
环境变量的内容附加到名称中。当然,省略了前缀和扩展名。
该[^-]*
部分匹配任何不包含破折号的字符串。如果后缀始终相同,您可以将其逐字逐句地放入其中,就像前缀一样(反之亦然 - 如果前缀可能不同,则使用^[^-]*-
它来匹配任何不包含位于文件名开头和(因此)第一个破折号之间的破折号的字符串)。
如果你rename
支持多个命令,只需将它们连接起来:
rename 's/_/-/g;s/^prefix-(.*)-([^-]*).txt$/$2_$1_'$DATETIME'/' *txt