我正在尝试重命名以下形式的一批文件:
test1_run1
test1_run2
...
test1_run10
...
test10_run1
test10_run2
...
test10_run10
到具有多个填充的窗体。对于第一个数字,我需要用长度为 5 的零填充,对于第二个长度为 3 的数字,我需要填充。
最终结果应为以下形式:
test00001_run001
test00001_run002
...
test00001_run010
...
test00010_run001
test00010_run002
...
test00010_run010
如何在 bash 中对特定文件夹中的所有文件执行此操作?
我们可以将字符串转换为 test
+ 5 digits
+ _run
+ 3 digits
格式,方法是说:
$ awk -F"test" '{split($2,a,"_run"); printf "%s%0.5d%s%0.3dn", FS, a[1], "_run", a[2]}' a
test00001_run001
test00001_run002
test00001_run010
test00010_run001
test00010_run002
test00010_run010
这是通过使用test
作为字段分隔符并将第二个字段分为两部分来工作的:_run
之前和之后。然后,它使用printf
的东西来获得正确的输出。
然后,您可以将mv
与以前的值一起打印并说:
$ awk -F"test" '{split($2,a,"_run"); printf "mv %s %s%0.5d%s%0.3dn", $0, FS, a[1], "_run", a[2]}' a
mv test1_run1 test00001_run001
mv test1_run2 test00001_run002
mv test1_run10 test00001_run010
mv test10_run1 test00010_run001
mv test10_run2 test00010_run002
mv test10_run10 test00010_run010
如果你然后用管道将它传送到 sh
,它将被执行。
如果你不想使用 perl 或 awk,并且严格使用 bash 和大多数发行版中可用的一些实用程序,你可以尝试这样的事情:
for i in * ; do
testpart=`echo $i | cut -d_ -f1`
testnum=${testpart#test}
runpart=`echo $i | cut -d_ -f2`
runnum=${runpart#run}
destfile=test`printf %05d $testnum`_run`printf %03d $runnum`
mv $i $destfile
done
In bash:
#!/bin/bash
shopt -s nullglob extglob
for file in test+([[:digit:]])_run+([[:digit:]]); do
[[ $file =~ ^test([[:digit:]]+)_run([[:digit:]]+)$ ]]
printf -v newfile 'test_%05d_run%03d' "$((10#${BASH_REMATCH[1]}))" "$((10#${BASH_REMATCH[2]}))"
echo mv "$file" "$newfile"
done
从要处理的文件夹中运行此命令。这将仅echo
要执行的mv
命令。如果您对结果感到满意,请删除echo
。
- 我们使用 shell 选项
nullglob
,以便不匹配的 glob 扩展到无; - 我们
extglob
使用 shell 选项,因为for
循环将使用扩展的 glob; - 扩展的 glob
test+([[:digit:]])_run+([[:digit:]])
将扩展到与此模式匹配的文件(如果有( - 我们使用正则表达式从文件名中获取数字;第一个数字将以
BASH_REMATCH[1]
为单位,第二个数字为BASH_REMATCH[2]
。 - 我们使用
printf
来格式化新文件名;修饰符%05d
和%03d
将根据您的意愿格式化数字(使用适当的前导零(。请注意,我们使用((10#${BASH_REMATCH[1]}))
来显式指定数字以基数 10 为单位,以防您有一个文件test09_run001
。09
部分会使 bash 误解基数 8 中的数字(因为前导 0(,你会得到投诉;-v
开关告诉printf
不要在标准输出上输出,而是将输出存储在可变newfile
中; - 最后我们执行
mv
。