如何在 Bash 中将文件名中的填充数字归零?

Posted

技术标签:

【中文标题】如何在 Bash 中将文件名中的填充数字归零?【英文标题】:How to zero pad numbers in file names in Bash? 【发布时间】:2010-09-08 12:25:53 【问题描述】:

使用 Bash 重命名表单中的文件的最佳方法是什么:

(foo1, foo2, ..., foo1300, ..., fooN)

使用零填充的文件名:

(foo00001, foo00002, ..., foo01300, ..., fooN)

【问题讨论】:

【参考方案1】:

此答案源自 Chris Conway 接受的答案,但假设您的文件具有扩展名(与 Chris 的答案不同)。只需将这个(相当长的)一行代码粘贴到您的命令行中即可。

for f in foo[0-9]*; do mv "$f" "$(printf 'foo%05d' "$f#foo" 2> /dev/null)"; done; for f in foo[0-9]*; do mv "$f" "$f.ext"; done;

可选的附加信息

此脚本将重命名

foo1.ext    > foo00001.ext
foo2.ext    > foo00002.ext
foo1300.ext > foo01300.ext

要在您的机器上测试它,只需将这一行粘贴到 EMPTY 目录中。

rm * 2> /dev/null; touch foo1.ext foo2.ext foo1300.ext; for f in foo[0-9]*; do mv "$f" "$(printf 'foo%05d' "$f#foo" 2> /dev/null)"; done; for f in foo[0-9]*; do mv "$f" "$f.ext"; done;

这会删除目录的内容,创建上例中的文件,然后进行批量重命名。

对于那些不需要单行的人,缩进的脚本如下所示。

for f in foo[0-9]*;
  do mv "$f" "$(printf 'foo%05d' "$f#foo" 2> /dev/null)";
done;

for f in foo[0-9]*;
  do mv "$f" "$f.ext";
done;

【讨论】:

【参考方案2】:

这不是纯粹的 bash,但使用 rename 的 Perl 版本更容易:

rename 's/\d+/sprintf("%05d",$&)/e' foo*

's/\d+/sprintf("%05d",$&)/e' 是 Perl 替换正则表达式。

\d+ 将匹配第一组数字(至少一个数字) sprintf("%05d",$&) 会将匹配的数字传递给 Perl 的 sprintf%05d 将填充为五位数字

【讨论】:

它也处理带有空格的文件名。 注意:重命名!=重命名***.com/a/31408886/318765【参考方案3】:

如果N 不是先验固定的:

for f in foo[0-9]*; do
  mv "$f" "$(printf 'foo%05d' "$f#foo")"
done

【讨论】:

今天早上花了整整一个小时寻找一个适用的解决方案,这是迄今为止我遇到的“最好的”解决方案,即。在 Linux、macOS 上工作,并且不依赖于 rename 实用程序。附带说明一下,此解决方案确实删除了我正在使用的文件的“文件扩展名”,因此在应用此解决方案后,for f in *; do mv "$f" "$f.ext"; done 是一种快速创可贴®,可以将所需的扩展名重新添加到我正在工作的文件中和。 ? 这在名为 050 的文件上打嗝(例如:以零开头的数字被解释为八进制基数),输出 00040(不是预期的 00050)。请参阅下面的答案。 如果您的文件只是 foo1 和 foo01,这会停止。【参考方案4】:

文件名中的左填充数字:

$ ls -l
total 0
-rw-r--r-- 1 victoria victoria 0 Mar 28 17:24 010
-rw-r--r-- 1 victoria victoria 0 Mar 28 18:09 050
-rw-r--r-- 1 victoria victoria 0 Mar 28 17:23 050.zzz
-rw-r--r-- 1 victoria victoria 0 Mar 28 17:24 10
-rw-r--r-- 1 victoria victoria 0 Mar 28 17:23 1.zzz

$ for f in [0-9]*.[a-z]*; do tmp=`echo $f | awk -F. 'printf "%04d.%s\n", $1, $2'`; mv "$f" "$tmp"; done;

$ ls -l
total 0
-rw-r--r-- 1 victoria victoria 0 Mar 28 17:23 0001.zzz
-rw-r--r-- 1 victoria victoria 0 Mar 28 17:23 0050.zzz
-rw-r--r-- 1 victoria victoria 0 Mar 28 17:24 010
-rw-r--r-- 1 victoria victoria 0 Mar 28 18:09 050
-rw-r--r-- 1 victoria victoria 0 Mar 28 17:24 10

说明

for f in [0-9]*.[a-z]*; do tmp=`echo $f | \
awk -F. 'printf "%04d.%s\n", $1, $2'`; mv "$f" "$tmp"; done;
注意反引号:`echo ... $2\`(反斜杠 \,为了便于阅读,正上方只是将单行拆分为两行) 在循环中查找以数字命名且带有小写字母扩展名的文件:[0-9]*.[a-z]* 回显该文件名 ($f) 以将其传递给 awk -F. : awk 字段分隔符,句点 (.):如果匹配,将文件名分隔为两个字段($1 = 数字;$2 = 扩展名) format with printf:将第一个字段($1,数字部分)打印为 4 位数字(%04d),然后打印句点,然后将第二个字段($2:扩展名)打印为字符串 (%s)。所有这些都分配给$tmp 变量 最后,将源文件 ($f) 移动到新文件名 ($tmp)

【讨论】:

【参考方案5】:

下面会做:

for ((i=1; i<=N; i++)) ; do mv foo$i `printf foo%05d $i` ; done

编辑:改为使用 ((i=1,...)),谢谢mweerden!

【讨论】:

我建议不要使用 seq ((i=1; i 您不需要使用子shell 来调用printf。 print -v var 会将结果存储在 var 变量中。【参考方案6】:

我的解决方案替换数字,字符串中的任何位置

for f in * ; do
    number=`echo $f | sed 's/[^0-9]*//g'`
    padded=`printf "%04d" $number`
    echo $f | sed "s/$number/$padded/";
done

您可以轻松尝试,因为它只打印转换后的文件名(不执行文件系统操作)。

解释:

循环文件列表

一个循环:for f in * ; do ;done,列出所有文件并将每个文件名作为$f 变量传递给循环体。

从字符串中获取数字

使用echo $f | sed,我们将变量$f 传递给sed 程序。

在命令sed 's/[^0-9]*//g' 中,带有修饰符^ 的部分[^0-9]* 告诉与数字0-9(不是数字)匹配,然后用空替换// 将其删除。为什么不直接删除[a-z]?因为文件名可以包含点、破折号等。所以,我们去掉所有不是数字的东西并得到一个数字。

接下来,我们将结果分配给number 变量。请记住不要在分配中放置空格,例如number = …,因为您会得到不同的行为。

我们将命令的执行结果赋给变量,用反引号`包裹命令。

零填充

命令 printf "%04d" $number 将数字格式更改为 4 位数字,如果我们的数字包含少于 4 位数字,则添加零。

将数字替换为零填充数字

我们再次使用seds/substring/replacement/ 之类的替换命令。为了解释我们的变量,我们使用双引号并以这种方式替换我们的变量$number


上面的脚本只是打印转换后的名字,所以,让我们来做真正的重命名工作吧:

for f in *.js ; do
    number=`echo $f | sed 's/[^0-9]*//g'`
    padded=`printf "%04d" $number`
    new_name=`echo $f | sed "s/$number/$padded/"`
    mv $f $new_name;
done

希望这对某人有所帮助。

我花了几个小时才弄明白。

【讨论】:

即使只有两个文件,例如 file1.js 和 file01.js,这也可能会因覆盖问题而停止。【参考方案7】:

我有一个更复杂的情况,文件名有后缀和前缀。我还需要从文件名中减去数字。

例如,我想让foo56.png 变成foo00000055.png

如果您正在做更复杂的事情,我希望这会有所帮助。

#!/bin/bash

prefix="foo"
postfix=".png"
targetDir="../newframes"
paddingLength=8

for file in $prefix[0-9]*$postfix; do
  # strip the prefix off the file name
  postfile=$file#$prefix
  # strip the postfix off the file name
  number=$postfile%$postfix
  # subtract 1 from the resulting number
  i=$((number-1))
  # copy to a new name with padded zeros in a new folder
  cp $file "$targetDir"/$(printf $prefix%0$paddingLengthd$postfix $i)
done

【讨论】:

【参考方案8】:

我使用的单行命令是这样的:

ls * | cat -n | while read i f; do mv "$f" `printf "PATTERN" "$i"`; done

PATTERN 可以是例如:

使用增量计数器重命名:%04d.$f#*.(保留原始文件扩展名) 使用带前缀的增量计数器重命名:photo_%04d.$f#*.(保留原始扩展名) 使用增量计数器重命名并将扩展名更改为 jpg:%04d.jpg 使用带前缀和文件基名的增量计数器重命名:photo_$(basename $f .$f#*.)_%04d.$f#*. ...

您可以过滤要重命名的文件,例如ls *.jpg | ...

您可以使用变量f(文件名)和i(计数器)。

对于您的问题,正确的命令是:

ls * | cat -n | while read i f; do mv "$f" `printf "foo%d05" "$i"`; done

【讨论】:

【参考方案9】:

纯 Bash,除了 'mv' 之外没有外部进程:

for file in foo*; do
  newnumber='00000'$file#foo      # get number, pack with zeros
  newnumber=$newnumber:(-5)       # the last five characters
  mv $file foo$newnumber            # rename
done

【讨论】:

【参考方案10】:

这是一个快速解决方案,它假定一个固定长度的前缀(您的“foo”)和固定长度的填充。如果您需要更大的灵活性,也许这至少是一个有用的起点。

#!/bin/bash

# some test data
files="foo1
foo2
foo100
foo200
foo9999"

for f in $files; do
    prefix=`echo "$f" | cut -c 1-3`        # chars 1-3 = "foo"
    number=`echo "$f" | cut -c 4-`         # chars 4-end = the number
    printf "%s%04d\n" "$prefix" "$number"
done

【讨论】:

以上是关于如何在 Bash 中将文件名中的填充数字归零?的主要内容,如果未能解决你的问题,请参考以下文章

如何在java中将密钥归零?

如何在bash中将所有子目录中的所有文件gzip到一个压缩文件中

如何在 unix 环境中填充超过 1024 个字符的文件?

如何在 Java 中将左填充零添加到数字中? [复制]

在C代码中将数组归零[重复]

如何在 Bash 脚本中将 (FTP) 文件上传到服务器?