开发者

Batch rename sequential files by padding with zeroes

I have a bunch of files named like so:

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

What is the easi开发者_运维知识库est way of renaming those to match a convention, e.g. with maximum four decimals, so that the files are named:

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

This should be easy in Unix/Linux/BSD, although I also have access to Windows. Any language is fine, but I'm interested in some really neat one-liners (if there are any?).


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))

you could compile a list of valid filenames assuming that all files that start with "output_" and end with ".png" are valid files:

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

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

In other words I replace file1.png with file001.png and file20.png with file020.png and so on. Here’s how to do that in bash

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

Save the above to a file called zeropad.sh and then do the following command to make it executable

chmod +x ./zeropad.sh

You can then use the zeropad.sh script as follows

./zeropad.sh frame1.png

which will return the result

frame001.png

All that remains is to use this script to rename all of the .png files in the current directory such that they are zeropadded.

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

Perl

(from: Zero pad rename e.g. 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

Also from above answer:

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


Fairly easy, although it combines a few features not immediately obvious:

@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!"
)

Edit: Misread that you're not after a batch file but any solution in any language. Sorry for that. To make up for it, a PowerShell one-liner:

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

Stick a ?{$_.basename-match'_\d+'} in there if you have other files that do not follow that pattern.


I actually just needed to do this on OSX. Here's the scripts I created for it - single line!

> for i in output_*.png;do mv $i `printf output_%04d.png $(echo $i | sed 's/[^0-9]*//g')`; done


For mass renaming the only safe solution is mmv—it checks for collisions and allows renaming in chains and cycles, something that is beyond most scripts. Unfortunately, zero padding it ain't too hot at. A flavour:

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

Here's one workaround:

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


Here is a Python script I wrote that pads zeroes depending on the largest number present and ignores non-numbered files in the given directory. Usage:

python ensure_zero_padding_in_numbering_of_files.py /path/to/directory

Body of script:

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:]))


$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

That's it!


with 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


I'm following on from Adam's solution for OSX.

Some gotchyas I encountered in my scenario were:

  1. I had a set of .mp3 files, so the sed was catching the '3' in the '.mp3' suffix. (I used basename instead of echo to rectify this)
  2. My .mp3's had spaces within their names, E.g., "audio track 1.mp3", this was causing basename+sed to screw up a little bit, so I had to quote the "$i" parameter.

In the end, my conversion line looked like this:

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


Using ls + awk + sh:

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

If you want to test the command before runing it just remove the | sh


I just want to make time lapse movie using

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

and got a similar problem.

[image2 @ 000000000039c300] Pattern type 'glob' was selected but globbing is not supported by this libavformat build

glob not support on Windows 7 . Also if file list like below, and uses %2d.jpg or %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

here is my batch script to rename flies

@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"
)

after rename 143,323 jpg files,

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜