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

发布于 2024-07-06 03:55:36 字数 190 浏览 6 评论 0原文

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

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

使用零填充的文件名:

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

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

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

With zero-padded file names:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

ˉ厌 2024-07-13 03:55:37

要在文件名中添加数字(此处为带字母扩展名的数字文件名,例如 1.abc):

$ 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]*.[az]*< /code>
  • 回显该文件名 ($f) 以将其传递给 awk
  • -F. : awk 字段分隔符、句点 (.):如果匹配,则将文件名分隔为两个字段($1 = 数字;$2 = 扩展名),
  • 格式为 < code>printf:将第一个字段($1,数字部分)打印为 4 位数字 (%04d),然后打印句点,然后打印第二个字段($2:扩展名)作为字符串 (%s)。 最后,所有这些都分配给 $tmp 变量
  • ,将源文件 ($f) 移动到新文件名 ($tmp)

To left-pad numbers in filenames (here numeric file names with alphabetic extensions, e.g. 1.abc):

$ 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

Explanation

for f in [0-9]*.[a-z]*; do tmp=`echo $f | \
awk -F. '{printf "%04d.%s\n", $1, $2}'`; 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. : awk field 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 $tmp variable
  • lastly, move the source file ($f) to the new filename ($tmp)
埋葬我深情 2024-07-13 03:55:37

纯 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

Pure Bash, no external processes other than '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
人│生佛魔见 2024-07-13 03:55:37

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

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

PATTERN 可以是例如:

  • 使用增量计数器重命名: %04d.${f#*.} (保留原始文件扩展名)使用
  • 带有前缀的增量计数器重命名:< code>photo_%04d.${f#*.} (保留原始扩展名)
  • 使用增量计数器重命名并将扩展名更改为 jpg: %04d.jpg
  • 使用带有前缀和文件的增量计数器重命名basename: 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

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#*.}
  • ...

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

You have available the variable f that is the file name and i that is the counter.

For your question the right command is:

ls * | cat -n | while read i f; do mv "$f" `printf "foo%d05" "$i"`; done
故事未完 2024-07-13 03:55:37

以下内容将完成此操作:

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

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

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!

傲性难收 2024-07-13 03:55:37

我的解决方案替换了数字,字符串中的所有位置

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(不是数字)的相反数字,然后用空替换 // 将其删除。 为什么不直接删除 [az] 呢? 因为文件名可以包含点、破折号等。因此,我们删除所有内容(不是数字)并得到一个数字。

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

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

零填充

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

将数字替换为零填充数字

我们再次使用 sed 以及诸如 s/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

希望这对某人有帮助。

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

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 $f variable to loop body.

Grabbing the number from string

With echo $f | sed we pipe variable $f to sed program.

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.

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

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

Zero padding

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

Replacing number to zero-padded number

We use sed again with replacement command like s/substring/replacement/. To interpret our variables, we use double quotes and substitute our variables in this way ${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.

自在安然 2024-07-13 03:55:37

如果 N 不是先验固定的:

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

In case N is not a priori fixed:

for f in foo[0-9]*; do
  mv "$f" "$(printf 'foo%05d' "${f#foo}")"
done
寒江雪… 2024-07-13 03:55:37

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

例如,我希望 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${paddingLength}d$postfix $i)
done

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.png to become 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
禾厶谷欠 2024-07-13 03:55:37

此答案源自 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

要在您的计算机上测试它,只需将这一行粘贴到目录中即可。

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;

This answer is derived from Chris Conway's accepted answer but assumes your files have an extension (unlike Chris' answer). Just paste this (rather long) one liner into your command line.

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;

OPTIONAL ADDITIONAL INFO

This script will rename

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

To test it on your machine, just paste this one liner into an EMPTY directory.

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;

This deletes the content of the directory, creates the files in the above example and then does the batch rename.

For those who don't need a one liner, the script indented looks like this.

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;
離殇 2024-07-13 03:55:37

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

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.

#!/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
在你怀里撒娇 2024-07-13 03:55:36

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

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

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

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

It's not pure bash, but much easier with the Perl version of rename:

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

Where 's/\d+/sprintf("%05d",$&)/e' is the Perl replace regular expression.

  • \d+ will match the first set of numbers (at least one number)
  • sprintf("%05d",$&) will pass the matched numbers to Perl's sprintf, and %05d will pad to five digits
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文