使用零填充批量重命名连续文件

38

我有一堆像这样命名的文件:

output_1.png
output_2.png
...
output_10.png
...
output_120.png

最简单的重命名方法是什么,以匹配命名规则,例如最多四位小数,使文件名为:

output_0001.png
output_0002.png
...
output_0010.png
output_0120.png

在Unix/Linux/BSD上应该很容易,尽管我也可以使用Windows。任何语言都可以,但我对一些真正好用的单行代码很感兴趣(如果有的话?)。


还要记住,在重命名文件时,您应确保没有文件名冲突导致文件被覆盖。我建议创建一个临时目录,将每个文件移动到其中,并使用其新名称,然后将所有文件移回原来的位置。 - vincent gravitas
1
可能是Linux shell脚本添加文件名前导零的重复问题。 - Serge Stroobandt
可能是Bash脚本填充文件名的重复问题。 - Ciro Santilli OurBigBook.com
10个回答

56

Python

import os
path = '/path/to/files/'
for filename in os.listdir(path):
    prefix, num = filename[:-4].split('_')
    num = num.zfill(4)
    new_filename = prefix + "_" + num + ".png"
    os.rename(os.path.join(path, filename), os.path.join(path, new_filename))

假设所有以“output_”开头且以“.png”结尾的文件都是有效文件,您可以编译一个有效文件名列表:

l = [(x, "output" + x[7:-4].zfill(4) + ".png") for x in os.listdir(path) if x.startswith("output_") and x.endswith(".png")]

for oldname, newname in l:
    os.rename(os.path.join(path,oldname), os.path.join(path,newname))

Bash

(来源: http://www.walkingrandomly.com/?p=2850)

换句话说,我要用file001.png 替换 file1.png ,用 file020.png 替换 file20.png 等等。以下是如何在bash中实现:

#!/bin/bash
num=`expr match "$1" '[^0-9]*\([0-9]\+\).*'`
paddednum=`printf "%03d" $num`
echo ${1/$num/$paddednum}

将上述内容保存到名为zeropad.sh的文件中,然后执行以下命令使其可执行:

chmod +x ./zeropad.sh

然后您可以按如下方式使用zeropad.sh脚本

./zeropad.sh frame1.png

这将返回结果

frame001.png

现在只需要使用这个脚本将当前目录中所有的.png文件重命名为零填充即可。

for i in *.png;do mv $i `./zeropad.sh $i`; done

Perl

(来源:补零重命名,例如将 Image (2).jpg 重命名为 Image (002).jpg)

use strict;
use warnings;
use File::Find;

sub pad_left {
   my $num = shift;

   if ($num < 10) {
      $num = "00$num";
   }
   elsif ($num < 100) {
      $num = "0$num";
   }

   return $num;
}

sub new_name {
   if (/\.jpg$/) {
      my $name = $File::Find::name;
      my $new_name;
      ($new_name = $name) =~ s/^(.+\/[\w ]+\()(\d+)\)/$1 . &pad_left($2) .')'/e;
      rename($name, $new_name);
      print "$name --> $new_name\n";
   }
}

chomp(my $localdir = `pwd`);# invoke the script in the parent-directory of the
                            # image-containing sub-directories

find(\&new_name, $localdir);

重命名

也来自上面的答案:

rename 's/\d+/sprintf("%04d",$&)/e' *.png

prefix, num = filename[:-4].split('_') 中,我得到了以下错误:ValueError: 需要多于一个值来解包 - slhck
啊,罪魁祸首就是它只能在目标目录中不包含任何其他文件时才能起作用。这很好,但仍然不太可移植。我会看看其他人是否有其他的解决方案:) - slhck
可以访问Windows。你可以将你刚才发布的Bash脚本插入到你的答案中,我会接受的。只是我喜欢StackOverflow的答案中已经包含了代码,而不仅仅是超链接到某个解决方案。 - slhck
1
在Bash脚本中应该使用printf "%03d" $((10#${num})),否则"08"和"09"将无法被接受。https://dev59.com/VGsz5IYBdhLWcg3wLEq8 - ironsand
@DTing,Bash版本似乎在某些文件不需要重命名时存在问题。例如,当mv无需执行任何操作时返回mv: 'test-100001.txt' and 'test-100001.txt' are the same file。(在我的情况下已经是test-%06d.txt)。这会带来任何问题吗?或者只是抑制错误消息的问题? - kevinkayaks
非常惊人的答案。我甚至有点倾向于用Ruby编写它,以添加为注释!真的很棒。 - Fernando Cordeiro

13

相当容易,尽管它结合了一些不太明显的功能:

@echo off
setlocal enableextensions enabledelayedexpansion
rem iterate over all PNG files:
for %%f in (*.png) do (
    rem store file name without extension
    set FileName=%%~nf
    rem strip the "output_"
    set FileName=!FileName:output_=!
    rem Add leading zeroes:
    set FileName=000!FileName!
    rem Trim to only four digits, from the end
    set FileName=!FileName:~-4!
    rem Add "output_" and extension again
    set FileName=output_!FileName!%%~xf
    rem Rename the file
    rename "%%f" "!FileName!"
)

编辑:我误读了您的意思,您不是在寻找批处理文件,而是在寻找任何语言的解决方案。对此我表示抱歉。为了弥补这一点,以下是一个 PowerShell 一行代码:

gci *.png|%{rni $_ ('output_{0:0000}.png' -f +($_.basename-split'_')[1])}

如果有其他不遵循该模式的文件,请将?{$_.basename-match'_\d+'}放在那里。


8
我实际上只需要在OSX上完成这个任务。这是我为此创建的脚本-单行代码!
> for i in output_*.png;do mv $i `printf output_%04d.png $(echo $i | sed 's/[^0-9]*//g')`; done

谢谢。我遇到了一些问题,与输入文件中带有数字后缀(例如.mp3)和文件名中带有空格有关。我最好在自己的答案中详细说明,因为注释不允许任何格式。 - Gurce

3

如果要进行大规模重命名,唯一安全的解决方案是使用mmv——它可以检查冲突并允许链式和循环重命名,这是大多数脚本无法实现的。不幸的是,它对零填充的支持并不太好。一个示例:

c:> mmv output_[0-9].png output_000#1.png

以下是一种解决方法:

c:> type file
mmv
[^0-9][0-9] #1\00#2
[^0-9][0-9][^0-9] #1\00#2#3
[^0-9][0-9][0-9] #1\0#2#3
[^0-9][0-9][0-9][^0-9] #1\0#2#3
c:> mmv <file

2

这里是我编写的一个Python脚本,它根据给定目录中最大的编号来填充零,并忽略非编号文件。使用方法:

python ensure_zero_padding_in_numbering_of_files.py /path/to/directory

脚本主体:

import argparse
import os
import re
import sys

def main(cmdline):

    parser = argparse.ArgumentParser(
        description='Ensure zero padding in numbering of files.')
    parser.add_argument('path', type=str,
        help='path to the directory containing the files')
    args = parser.parse_args()
    path = args.path

    numbered = re.compile(r'(.*?)(\d+)\.(.*)')

    numbered_fnames = [fname for fname in os.listdir(path)
                       if numbered.search(fname)]

    max_digits = max(len(numbered.search(fname).group(2))
                     for fname in numbered_fnames)

    for fname in numbered_fnames:
        _, prefix, num, ext, _  = numbered.split(fname, maxsplit=1)
        num = num.zfill(max_digits)
        new_fname = "{}{}.{}".format(prefix, num, ext)
        if fname != new_fname:
            os.rename(os.path.join(path, fname), os.path.join(path, new_fname))
            print "Renamed {} to {}".format(fname, new_fname)
        else:
            print "{} seems fine".format(fname)

if __name__ == "__main__":
    sys.exit(main(sys.argv[1:]))

1
$rename output_ output_0 output_?   # adding 1 zero to names ended in 1 digit
$rename output_ output_0 output_??  # adding 1 zero to names ended in 2 digits
$rename output_ output_0 output_??? # adding 1 zero to names ended in 3 digits

就是这样!


1
使用bash split,
Linux
for f in *.png;do n=${f#*_};n=${n%.*};mv $f $(printf output_"%04d".png $n);done

Windows(Bash)
for f in *.png;do n=${f#*_};mv $f $(printf output_"%08s" $n);done

0

我正在继续Adam在OSX上的解决方案。

在我的情况下,我遇到了一些问题:

  1. 我有一组.mp3文件,所以sed会捕捉到.mp3后缀中的“3”。(我使用basename而不是echo来纠正这个问题)
  2. 我的.mp3文件名中有空格,例如“audio track 1.mp3”,这会导致basename+sed出现一些问题,因此我必须引用“$i”参数。

最终,我的转换行如下:

for i in *.mp3 ; do mv "$i" `printf "track_%02d.mp3\n" $(basename "$i" .mp3 | sed 's/[^0-9]*//g')` ; done

0

我只想使用编程制作时间流逝的电影。

ffmpeg  -pattern_type glob -i "*.jpg" -s:v 1920x1080 -c:v libx264 output.mp4 

我遇到了类似的问题。

[image2 @ 000000000039c300] 选择了模式类型“glob”,但此 libavformat 构建不支持 globbing

在 Windows 7 上不支持 glob。 如果文件列表如下,并且使用 %2d.jpg 或 %02d.jpg

1.jpg 2.jpg ... 10.jpg 11.jpg ...

[image2 @ 00000000005ea9c0] Could find no file with path '%2d.jpg' and index in the range 0-4  
%2d.jpg: No such file or directory 
[image2 @ 00000000005aa980] Could find no file with path '%02d.jpg' and index in the range 0-4  
%02d.jpg: No such file or directory

这是我的批处理脚本来重命名文件

@echo off
setlocal enabledelayedexpansion

set i=1000000
set X=1
for %%a in (*.jpg) do (
    set /a i+=1
    set "filename=!i:~%X%!"
    echo ren "%%a" "!filename!%%~xa"
    ren "%%a" "!filename!%%~xa"
)

重命名了143,323个jpg文件之后,

ffmpeg -i %6d.jpg -s:v 1920x1080 -c:v libx264 output.mp4 

0

使用 ls + awk + sh

ls -1 | awk -F_ '{printf "%s%04d.png\n", "mv "$0" "$1"_", $2}' | sh

如果你想在运行命令之前测试它,只需要删除 | sh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接