我的目录中有数十万个文件。这些文件的命名如下:
left-00001.tiff
left-00002.tiff
...
left-99999.tiff
left-100000.tiff
...
left-245000.tiff
我想将文件重命名如下:
left-000001.tiff
...
left-099999.tiff
...
left-245000.tiff
我找到了解决这个问题的优雅方法这里。
该解决方案实现了一个名为 的 bash 脚本zeropad.sh
。 bash 的编码如下:
#!/bin/bash
num=`expr match "$1" '[^0-9]*\([0-9]\+\).*'`
paddednum=`printf "%06d" $num`
echo ${1/$num/$paddednum}
并且可以使用for loop
如下迭代应用:
for i in *.tiff;do mv $i `./zeropad.sh $i`; done
然而,这个解决方案需要很长时间,因为它做了很多不必要的工作来重命名所有已经正确填充的文件。i.e. as %06d type numbers
。出于我自己的目的,这个解决方案非常慢。
我有两个问题:
1-如何修改迭代器以仅应用于zeropad.sh
需要零填充的文件?
2-如何使用touch
a中的命令for loop
生成测试数据?在将脚本应用于原始数据之前,验证该脚本是否有效至关重要。
答案1
这就是我通常的做法(在 shell 上手动):
rename left- left-0 left-?.png # for 0-9
rename left- left-0 left-??.png # for 00-99
rename left- left-0 left-???.png # for 000-999
# result left-0000.png - left-9999.png
这在交互式 shell 会话中很容易做到...只需重复最后一个命令并?
添加一个附加命令即可。
但是,如果文件数量很大,您最终会得到一个太长的参数列表。显然这不是最有效的选择,因为它最终会多次重命名同一文件(left-1.png -> left-01.png -> left-001.png -> ...)。
about也有两种风格rename
,一种带有 Perl 正则表达式,一种不带有。根据发行版的不同,您最终会得到rename.ul
或perl-rename
或其他名称。基本上,它使使用该命令的任何脚本rename
都不可移植,因为您永远不知道会发生什么。
我正在使用 util-linux 重命名,您的问题实际上是他们的示例之一,来自手册页:
EXAMPLES Given the files foo1, ..., foo9, foo10, ..., foo278, the commands rename foo foo00 foo? rename foo foo0 foo?? will turn them into foo001, ..., foo009, foo010, ..., foo278.
哪种方法更有效(每个文件仅重命名一次)但您必须找出000
vs的正确分布???
,否则最终会得到错误的结果。
对我来说,在交互式 shell 上处理一组合理的小文件时,低效的方法是更实用的方法。
与自己编写脚本相比,优点在于rename
它不必mv
为每个文件生成一个进程,或者像您的情况一样,只需一个子脚本即可找出文件名。目前还不清楚什么有更多的开销,进程生成,或重复重命名,而且我懒得对其进行基准测试。
实际上,您链接的答案已经在最后包含了“最佳”解决方案......使用 perl-rename:
rename 's/\d+/sprintf("%04d",$&)/e' *.png
好吧,人们可能会争论正则表达式,但关键是,可以一次性完成所有这一切,mv
而不会产生不必要的进程。如果您仍然需要改进这一点,请编写一个直接读取目录内容的工具,而不是使用 shell 通配符(排序,速度很慢)并根据需要执行重命名。
也许这实际上就是您链接到的答案,也许这就是您被否决的原因。 ;)
答案2
成本高昂的是分叉如此多的进程并为每个文件运行如此多的命令。
和zsh
:
zmodload zsh/files # make mv builtin to speed things up
autoload zmv
zmv -n '(*-)(<->)(.tiff)' '$1${(l:6::0:)2}$3'
-n
(高兴时删除)
这都是内置函数,因此不会分叉任何进程,也不会执行任何文件。
或者与perl
's rename
:
rename -n 's/\d+(?=\.tiff\z)/sprintf "%06d", $&/e' ./*[0-9].tiff
答案3
循环花费的大部分时间可能是在调用zeropad.sh
脚本上。
相反,在一个脚本中完成这一切:
#!/bin/bash
for filename in left-*.tiff; do
if [[ "$filename" =~ ^left-0*([1-9]?[0-9]+)\.tiff$ ]]; then
num=${BASH_REMATCH[1]}
newname="left-$( printf '%06d' "$num" ).tiff"
if [ "$filename" != "$newname" ] && [ ! -e "$newname" ]; then
echo mv "$filename" "$newname"
fi
fi
done
echo
一旦您确认脚本正在执行正确的操作,请删除。
答案4
我喜欢 Perl 的俏皮话:
ls left-*.tiff | perl -ne 'if(m/(\S+)-(\d+).tiff/){chomp;printf "mv $_ left-%06d.tiff\n", $2}' | bash
PS,确保在管道输入之前仔细检查输出bash
。只是为了安全。