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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/55754/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 17:36:25  来源:igfitidea点击:

How to zero pad numbers in file names in Bash?

bashrename

提问by Trastle

What is the best way, using Bash, to rename files in the form:

使用 Bash 重命名以下形式的文件的最佳方法是什么:

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

With zero-padded file names:

使用零填充的文件名:

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

采纳答案by Chris Conway

In case Nis not a priori fixed:

如果N不是先验固定的:

 for f in foo[0-9]*; do mv $f `printf foo%05d ${f#foo}`; done

回答by KARASZI István

It's not pure bash, but much easier with the renamecommand:

它不是纯粹的 bash,但使用rename命令要容易得多:

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

回答by Michael Baltaks

I had a more complex case where the file names had a postfix as well as a prefix. I also needed to perform a subtraction on the number from the filename.

我有一个更复杂的情况,其中文件名有后缀和前缀。我还需要对文件名中的数字执行减法。

For example, I wanted foo56.pngto become foo00000055.png.

例如,我想foo56.png成为foo00000055.png.

I hope this helps if you're doing something more complex.

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

#!/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${paddingLength}d$postfix $i)
done

回答by Pioz

The oneline command that I use is this:

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

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

PATTERN can be for example:

模式可以是例如:

  • rename with increment counter: %04d.${f#*.}(keep original file extension)
  • rename with increment counter with prefix: photo_%04d.${f#*.}(keep original extension)
  • rename with increment counter and change extension to jpg: %04d.jpg
  • rename with increment counter with prefix and file basename: photo_$(basename $f .${f#*.})_%04d.${f#*.}
  • ...
  • 使用增量计数器重命名:(%04d.${f#*.}保留原始文件扩展名)
  • 使用带有前缀的增量计数器重命名:(photo_%04d.${f#*.}保留原始扩展名)
  • 使用增量计数器重命名并将扩展名更改为 jpg: %04d.jpg
  • 使用带有前缀和文件基名的增量计数器重命名: photo_$(basename $f .${f#*.})_%04d.${f#*.}
  • ...

You can filter the file to rename with for example ls *.jpg | ...

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

You have available the variable fthat is the file name and ithat is the counter.

您可以使用作为f文件名和i计数器的变量。

For your question the right command is:

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

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

回答by Fritz G. Mehner

Pure Bash, no external processes other than 'mv':

纯 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

回答by dF.

The following will do it:

以下将做到这一点:

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

EDIT:changed to use ((i=1,...)), thanks mweerden!

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

回答by Victoria Stuart

To left-pad numbers in filenames:

在文件名中左键盘数字:

$ 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", , }'`; 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

Explanation

解释

for f in [0-9]*.[a-z]*; do tmp=`echo $f | \
awk -F. '{printf "%04d.%s\n", , }'`; mv "$f" "$tmp"; done;
  • note the backticks: `echo ... $2}\`(The backslash, \, immediately above just splits that one-liner over two lines for readability)
  • in a loop find files that are named as numbers with lowercase alphabet extensions: [0-9]*.[a-z]*
  • echo that filename ($f) to pass it to awk
  • -F.: awkfield separator, a period (.): if matched, separates the file names as two fields ($1= number; $2= extension)
  • format with printf: print first field ($1, the number part) as 4 digits (%04d), then print the period, then print the second field ($2: the extension) as a string (%s). All of that is assigned to the $tmpvariable
  • lastly, move the source file ($f) to the new filename ($tmp)
  • 注意反引号:(`echo ... $2}\`反斜杠,\,正上方只是将一行行拆分为两行以提高可读性)
  • 在循环中查找以带有小写字母扩展名的数字命名的文件: [0-9]*.[a-z]*
  • 回显该文件名 ( $f) 以将其传递给awk
  • -F.:awk字段分隔符,句点 ( .):如果匹配,则将文件名分隔为两个字段 ( $1= number; $2= 扩展名)
  • 格式为printf:将第一个字段($1,数字部分)%04d打印为 4 位数字 ( ),然后打印句点,然后将第二个字段 ( $2: 扩展名)打印为字符串 ( %s)。所有这些都分配给$tmp变量
  • 最后,将源文件 ( $f)移动到新文件名 ( $tmp)

回答by AntonAL

My solution replaces numbers, everywhere in a string

我的解决方案替换字符串中无处不在的数字

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

You can easily try it, since it just prints transformed file names (no filesystem operations are performed).

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

Explanation:

解释:

Looping through list of files

循环遍历文件列表

A loop: for f in * ; do ;done, lists all files and passes each filename as $fvariable to loop body.

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

Grabbing the number from string

从字符串中获取数字

With echo $f | sedwe pipe variable $fto sedprogram.

随着echo $f | sed我们管变量$fsed程序。

In command sed 's/[^0-9]*//g', part [^0-9]*with modifier ^tells to match opposite from digit 0-9 (not a number) and then remove it it with empty replacement //. Why not just remove [a-z]? Because filename can contain dots, dashes etc. So, we strip everything, that is not a number and get a number.

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

Next, we assign the result to numbervariable. Remember to not put spaces in assignment, like number = …, because you get different behavior.

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

We assign execution result of a command to variable, wrapping the command with backtick symbols `.

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

Zero padding

零填充

Command printf "%04d" $numberchanges format of a number to 4 digits and adds zeros if our number contains less than 4 digits.

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

Replacing number to zero-padded number

将数字替换为零填充数字

We use sedagain with replacement command like s/substring/replacement/. To interpret our variables, we use double quotes and substitute our variables in this way ${number}.

我们sed再次使用替换命令,如s/substring/replacement/. 为了解释我们的变量,我们使用双引号并以这种方式替换我们的变量${number}



The script above just prints transformed names, so, let's do actual renaming job:

上面的脚本只是打印转换后的名称,所以,让我们做实际的重命名工作:

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

Hope this helps someone.

希望这可以帮助某人。

I spent several hours to figure this out.

我花了几个小时来弄清楚这一点。

回答by amrox

Here's a quick solution that assumes a fixed length prefix (your "foo") and fixed length padding. If you need more flexibility, maybe this will at least be a helpful starting point.

这是一个快速解决方案,它假定一个固定长度的前缀(你的“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