[英]bash shell: change multiple file names to add leading zeros
I have several files with this pattern: prefix.1.*
, prefix.2.*
, prefix.3.*
, etc... and I want their name to be changed, respectively, to prefix.01.*
, prefix.02.*
, prefix.03.*
, etc. With that, they will be properly sorted by name as there are filenames with already two digits (eg prefix.27.*
) in the set.我有几个具有这种模式的文件: prefix.1.*
, prefix.2.*
, prefix.3.*
等......我希望它们的名称分别更改为prefix.01.*
, prefix.02.*
, prefix.03.*
等。有了这个,它们将按名称正确排序,因为集合中已经有两位数字的文件名(例如prefix.27.*
)。
How can I do that using commands available in the bash shell?如何使用 bash shell 中可用的命令来做到这一点?
Note: Just after this issue, I had to cope with a list of files like prefix.1.*
, prefix.2.*
, prefix.17.*
, prefix.157.*
(arbitrary, not sequentially numbered), with the aim to convert to prefix.001.*
, prefix.002.*
, prefix.017.*
, prefix.157.*
, with a generic way of inserting the right number of leading zeros.注意:就在这个问题之后,我不得不处理像prefix.1.*
、 prefix.2.*
、 prefix.17.*
、 prefix.157.*
(任意,不按顺序编号)这样的文件列表,旨在转换为prefix.001.*
, prefix.002.*
, prefix.017.*
, prefix.157.*
,使用插入正确数量的前导零的通用方法。 If you face this situation, I strongly recommend you to follow the general solution provided by mklement0 answer ( More generic renaming solutions ) below.如果您遇到这种情况,我强烈建议您遵循下面mklement0 答案(更通用的重命名解决方案)提供的通用解决方案。
for file in prefix.[0-9].*
do
mv "$file" "${file/prefix./prefix.0}"
done
More generic renaming solutions , prompted by later comments by the OP:更通用的重命名解决方案,由 OP 稍后评论提示:
Uses the Perl-based rename
utility , which comes standard on some Linux distros (eg, Ubuntu).使用基于 Perl 的rename
实用程序,它是某些Linux 发行版(例如,Ubuntu)的标准配置。 Caveat : some distros have a different utility of the same name from the util-linux
package.警告:某些发行版具有与util-linux
软件包同名的不同实用程序。
On OS X, you can install via Homebrew using brew install rename
.在 OS X 上,您可以使用brew install rename
通过Homebrew brew install rename
。
Both solutions determine the required padding width dynamically .两种解决方案都可以动态确定所需的填充宽度。
printf '%s\n' prefix.[0-9]*.* | # enumerate files of interest
sort -t. -n -k2,2 | # sort them by the embedded number
rename -n -N '...01' -e 's/\.\d+\./.$N./' # rename with appropriate padding
-n
performs a dry-run only, showing only what would happen; -n
执行空运行,只显示会发生什么; remove it to perform actual renaming删除它以执行实际重命名-N '...01'
specifies the format for the special counter variable $N
in a way that auto-determines appropriate zero-padding based on the number of input files . -N '...01'
指定特殊计数器变量$N
的格式,其方式是根据输入文件的数量自动确定适当的零填充。's/\\.\\d+\\./.$N./'
is a Perl expression that replaces the existing number component with the appropriately padded counter variable $N
. 's/\\.\\d+\\./.$N./'
是一个 Perl 表达式,它用适当填充的计数器变量$N
替换现有的数字组件。In this case, the padding width is derived from the largest existing number embedded in the filenames :在这种情况下,填充宽度来自嵌入在文件名中的最大现有数字:
# Determine the longest (largest) number contained in the matching filenames...
maxNum=$(printf '%s\n' prefix.[0-9]*.* | sort -nrt. -k2,2 | head -1 | cut -d. -f2)
# ... and derive the padding length from it.
padLength=${#maxNum}
# Perform renaming by zero-padding the *existing* numbers in the filenames.
rename -n -e 's/\.(\d+)\./sprintf(".%0'${padLength}'s.", $1)/e' prefix.[0-9]*.*
-n
performs a dry-run only, showing only what would happen; -n
执行空运行,只显示会发生什么; remove it to perform actual renaming删除它以执行实际重命名e
flag in the s/.../.../
command, which allows use of an arbitrary Perl expression in the replacement string;注意在s/.../.../
命令中使用 Perl 的e
标志,它允许在替换字符串中使用任意 Perl 表达式; in this case, an sprintf
expression is used to zero-pad the input number;在这种情况下, sprintf
表达式用于对输入数字进行零填充; credit for this approach belongs to @Adrian Frühwirth's answer here .这种方法的功劳属于@Adrian Frühwirth在此处的回答。 On a general note, matching filenames could be made more robust with shopt -s extglob
and prefix.+([0-9]).*
;一般而言,使用shopt -s extglob
和prefix.+([0-9]).*
可以使匹配的文件名更加可靠; similarly, shopt -s nullglob
would make it easier to determine whether any files matched.同样, shopt -s nullglob
可以更轻松地确定是否有任何文件匹配。
Note a pure bash
solution, but a little more concise(*):注意纯bash
解决方案,但更简洁一些(*):
printf '%s\0' prefix.[0-9].* |
xargs -0 -I % bash -c 'f="%"; mv "$f" "${f/\./.0}"'
This assumes that glob prefix.[0-9].*
expands to at least 1 actual filename.这假设 glob prefix.[0-9].*
扩展为至少 1 个实际文件名。
If it's possible that NO files match, execute shopt -s nullglob
first (and, if required, restore that shell option's value later).如果可能没有文件匹配,请先执行shopt -s nullglob
(如果需要,稍后恢复该 shell 选项的值)。
Performance note :性能说明:
While this is more concise than @R Sahu's solution , it will perform worse , due to spawning an additional child shell ( bash -c
) for every input filename - so as to be able to assign the filename to a variable and use expansion on it.虽然这比@R Sahu 的解决方案更简洁,但它的性能会更差,因为为每个输入文件名生成一个额外的子 shell ( bash -c
) - 以便能够将文件名分配给一个变量并对其使用扩展.
(*) In terms of character count, @R Sahu's solution is actually shorter, but the absence of a loop (and fewer lines) may result in this answer being perceived as shorter. (*) 在字符数方面,@R Sahu 的解决方案实际上更短,但没有循环(和更少的行)可能会导致这个答案被认为更短。 At the end of the day, this solution doesn't add much in itself - except perhaps as an advanced example of using xargs
:归根结底,这个解决方案本身并没有增加太多 - 除了作为使用xargs
的高级示例:
printf '%s\\0'
ensures that all matching filenames are passed through the pipeline with NUL chars. printf '%s\\0'
确保所有匹配的文件名都通过管道以 NUL 字符传递。 as separators.作为分隔符。xargs -0
then ensures that the filenames are recognized individually, even if they contain embedded whitespace. xargs -0
然后确保文件名被单独识别,即使它们包含嵌入的空格。-I %
ensures that each input filename results in its own invocation of the command that follows, with %
instances replaced with the filename at hand. -I %
确保每个输入文件名都会导致其对随后的命令的调用,并将%
实例替换为手头的文件名。bash -c
then invokes a child shell (a child process that happens to be another bash
instance) and evaluates the specified string. bash -c
然后调用一个子 shell(一个恰好是另一个bash
实例的子进程)并计算指定的字符串。我发现的最简单的方法是在 bash shell 中输入这一行:
for ITER in 1 2 3 4 5 6 7 8 9; do for FILE in prefix."$ITER".*; do mv "$FILE" $(echo "$FILE" | sed 's/prefix.'$ITER'/prefix.0'$ITER'/'); done; done
One more way>>另一种方式>>
#!/bin/bash
count=1
for i in $(ls prefix*)
do
Newname=prefix\.$(printf "%02d" $count)\.txt
mv $i $Newname
count=$(($count + 1))
done
Great answers so far.到目前为止很好的答案。 If the extension is same of all the files.如果所有文件的扩展名相同。 Just use following.只需使用以下。 Here the extension is .jpg
you can change prefix_
to your choice of prefix.这里的扩展名是.jpg
您可以将prefix_
更改为您选择的前缀。 Also if you want more leading zeros just change %02 to %03 and so on.此外,如果您想要更多前导零,只需将 %02 更改为 %03 等等。
rename -n -e 's/\d+/sprintf("prefix_%02d",$&)/e' -- *.jpg
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.