2

I have extracted clips from a video with ffmpeg, following this pattern 'clip-%4d.png'.

This has yielded files like: clip-0001.png, clip-0002.png and so on.

I have then applied many filters with imagemagick "convert" to those pictures, and it ran for a couple hours.

I realised my list order is broken after clip-9999.png, it becomes clip-10000.png and up to clip-40000.png, successfully ruining my sequence of clips.

I would like to convert all my clips to follow the sequence clip-00000.png, clip-00001.png up to clip-40000.png.

I could restart the whole process using pattern 'clip-%5d', but I am told rename utility might solve my problem; however I am myself quite unfamiliar with regular expressions.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
biximilien
  • 23
  • 3

4 Answers4

5

Use this command in bash:

for file in clip-????.png ; do
    mv ${file} clip-0${file#clip-}
done

to check that the command will do the right thing, replace "mv" with "echo mv" to see the list of renamings that will be done.

Michael Foukarakis
  • 39,737
  • 6
  • 87
  • 123
Emanuele Paolini
  • 9,912
  • 3
  • 38
  • 64
  • 5
    `${file//clip-}` is a bashism. Since this is prefix, simple `${file#clip-}` (remove-prefix) can be used and is more portable. – Jan Hudec Feb 19 '13 at 07:03
  • lol, good manu-fatto, you were fastest, now think why after this operation some files will still have different quantity of digits in their names :) Q. author thinks well, althouhg a combination of find + sed or awk would be better. What you need, is simply padding expression, to pad any number to specified length in characters with leading zeros. – Piotr Wadas Feb 19 '13 at 07:04
  • @Jan: I only remember the // command by heart... this is the most useful. The others I should look in the documentation. – Emanuele Paolini Feb 19 '13 at 07:07
  • @Piotr: I don't get it... please tell me! – Emanuele Paolini Feb 19 '13 at 07:08
  • @manu-fatto: For me it's the one I never use, because I usually write scripts in `/bin/sh` and use a faster but less featureful shell for that. – Jan Hudec Feb 19 '13 at 07:09
  • this will be an expression to find matching files: find -E . -type f -regex '.*[:digit:]{1,}.*' -exec echo {} \; Now think what to use instead of echo to print names containing numbers with numbers padded, and then replace echo with mv. I'd do this, but I'm late for work :) Good luck :) – Piotr Wadas Feb 19 '13 at 07:19
  • 1
    @PiotrWadas: the regex is too permissive; it picks up the already existing 5-digit names but must not. A better regex would be `'clip-[[:digit:]]{4}.png'` which matches the names to be mapped exactly. – Jonathan Leffler Feb 19 '13 at 07:23
  • You are right, however the point is to find clip-12.png as well as clip-56789.png, and pad both numbers to length e.g. ten, so clip-12.png would have 8 leading zeros, and clip-56789.png would have 5 leading zeroes, between "-" and first original digit. Therefore it would be a combination of your expression and mine expression. :) – Piotr Wadas Feb 19 '13 at 18:38
2

You could do something like this:

for f in clip-[0-9][0-9][0-9][0-9].png; do mv $f `echo $f | sed 's/clip-/clip-0/'`; done

Note: To test without actually doing anything, replace mv with echo mv.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
2

Using a Perl-based rename command

If you've got a Perl-based rename command, that's pretty easy:

rename 's/(\d{4})/0$1/' *-????.png

I note in passing that if you've really got about 10,000 clips to rename, you may need to work with xargs or find to avoid problems with 'argument list too long'.

find . -name 'clip-????.png' -exec rename 's/(\{4})/0$1/' {} +

or:

find . -name 'clip-????.png' -print | xargs rename 's/(\d{4})/0$1/'

Since your names don't contain spaces, either will work. If there are spaces to worry about, use the find-only variant, assuming your find supports the POSIX 2008 + notation to find. This can be applied to any of the other answers with a little care.

I note that this Perl script avoids having to execute mv 10,000 times, so it is likely to outpace any of the shell scripts that executes mv for each file rename. For a one-off exercise, this may not matter, but it will become more of an issue when you get to more than 100,000 clips.

Also, the script below has the option to read the file names, one per line, but it slurps the whole lot into memory, which isn't dreadfully efficient (but was present in the original version of the script and has been kept, even though I've hardly ever used the option). Assuming a sane modern machine (say 1 GiB of memory, but less would be sufficient), that is not going to be a problem with just 10,000 names of length 12 characters each. So you could also use:

find . -name 'clip-????.png' -print | rename 's/(\d{4})/0$1/'

A Perl-based rename script

If you've not got a Perl-based rename command, this is the one I use. It was originally from the 1st Edition of the Camel Book, but has been modified a little over the years:

#!/usr/bin/env perl
#
# @(#)$Id: rename.pl,v 1.8 2011/06/03 22:30:22 jleffler Exp $
#
# Rename files using a Perl substitute or transliterate command

use strict;
use warnings;
use Getopt::Std;

my(%opts);
my($usage) = "Usage: $0 [-fnxV] perlexpr [filenames]\n";
my($force) = 0;
my($noexc) = 0;
my($trace) = 0;

die $usage unless getopts('fnxV', \%opts);

if ($opts{V})
{
    printf "%s\n", q'RENAME Version $Revision: 1.8 $ ($Date: 2011/06/03 22:30:22 $)';
    exit 0;
}
$force = 1 if ($opts{f});
$noexc = 1 if ($opts{n});
$trace = 1 if ($opts{x});

my($op) = shift;
die $usage unless defined $op;

if (!@ARGV) {
    @ARGV = <STDIN>;
    chop(@ARGV);
}

for (@ARGV)
{
    if (-e $_ || -l $_)
    {
        my($was) = $_;
        eval $op;
        die $@ if $@;
        next if ($was eq $_);
        if ($force == 0 && -f $_)
        {
            print STDERR "rename failed: $was - $_ exists\n";
        }
        else
        {
            print "+ $was --> $_\n" if $trace;
            print STDERR "rename failed: $was - $!\n"
                unless ($noexc || rename($was, $_));
        }
    }
    else
    {
        print STDERR "$_ - $!\n";
    }
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
0

you can check here:

you can replace the command gsub(/-\(.*\)/,"",$0); in the link with

gsub(/-/,"-0",$0);

and the ls -1 part will be replaced with ls -1 clip-????.png also you need to remove the search part in the awk command. I tested below with a sample file.

@jonathan,I did not really understand what you said.But below is test i made:

> ls -1 clip-????.png
clip-0003.png
clip-1111.png

> ls -1 clip-????.png | nawk '{old=$0;gsub(/-/,"-0",$0);system("mv \""old"\" "$0)}'
phoenix.332> ls -1 clip-?????.png
clip-00003.png
clip-01111.png
> ls -1 clip-????.png
ls: No match.

nawk is on solaris . you can use awk for other flavours of unix.

Vijay
  • 65,327
  • 90
  • 227
  • 319
  • Hmmm...the link embeds that in some surrounding `awk`. Of itself, there is no standard `gsub` command and the notation `gsub(...)` would give the shell fits as it would object to the `(` where it appears (and would fail to find a command called `/0/,-0,-sh` or thereabouts if it didn't object to the parentheses). – Jonathan Leffler Feb 19 '13 at 07:12
  • OK; so the answer is not just `gsub(/-/,"-0",$0);` but an `awk` script that embeds that. People should not have to go to the link to understand your answer. Now you've just got the problem that it is slow because it executes `mv` 10,000 times (given that there are about 10,000 files to rename). However, that doesn't stop it working. – Jonathan Leffler Feb 19 '13 at 07:20