13

I've tried a bunch of examples of the rename command but I can't figure out the syntax to do what I want.

I have a bunch of files labeled something like

File_Ex_1.jpg
File_Ex_2.jpg
File_Ex_3.jpg
File_Ex_4.jpg
...
File_Ex_10.jpg
File_Ex_11.jpg
etc. 

I want to change only some of them by inserting a 0 so that the file names have the same number of characters.

So I want File_Ex_1.jpg to go to File_Ex_01.jpg, File_Ex_2.jpg to File_Ex_02.jpg

How do you do this with the rename command?

This rename command was installed via home-brew on a Mac. Output of rename -v:

rename -v
Usage:
    rename [switches|transforms] [files]

    Switches:

    -0/--null (when reading from STDIN)
    -f/--force or -i/--interactive (proceed or prompt when overwriting)
Wide character in print at /System/Library/Perl/5.18/Pod/Text.pm line 286.

    -g/--glob (expand "*" etc. in filenames, useful in Windows™ CMD.EXE)
    -k/--backwards/--reverse-order
    -l/--symlink or -L/--hardlink
    -M/--use=*Module*
    -n/--just-print/--dry-run
    -N/--counter-format
    -p/--mkpath/--make-dirs
    --stdin/--no-stdin
    -t/--sort-time
    -T/--transcode=*encoding*
    -v/--verbose

    Transforms, applied sequentially:

    -a/--append=*str*
    -A/--prepend=*str*
    -c/--lower-case
    -C/--upper-case
    -d/--delete=*str*
    -D/--delete-all=*str*
    -e/--expr=*code*
    -P/--pipe=*cmd*
    -s/--subst *from* *to*
    -S/--subst-all *from* *to*
    -x/--remove-extension
    -X/--keep-extension
    -z/--sanitize
    --camelcase --urlesc --nows --rews --noctrl --nometa --trim (see manual)
0

5 Answers 5

25

Try with this:

rename -e 's/\d+/sprintf("%02d",$&)/e' -- *.jpg

Example:

$ ls
Device_Ex_10.jpg  Device_Ex_1.jpg  Device_Ex_4.jpg  Device_Ex_7.jpg
Device_Ex_11.jpg  Device_Ex_2.jpg  Device_Ex_5.jpg  Device_Ex_8.jpg
Device_Ex_12.jpg  Device_Ex_3.jpg  Device_Ex_6.jpg  Device_Ex_9.jpg
$ rename -e 's/\d+/sprintf("%02d",$&)/e' -- *.jpg
$ ls
Device_Ex_01.jpg  Device_Ex_04.jpg  Device_Ex_07.jpg  Device_Ex_10.jpg
Device_Ex_02.jpg  Device_Ex_05.jpg  Device_Ex_08.jpg  Device_Ex_11.jpg
Device_Ex_03.jpg  Device_Ex_06.jpg  Device_Ex_09.jpg  Device_Ex_12.jpg

I took the reference from here: https://stackoverflow.com/questions/5417979/batch-rename-sequential-files-by-padding-with-zeroes

Here adapted to your particular rename implementation.

9
  • This looks like a proper call for the Perl-based version of rename, which the asker doesn't appear to have.
    – dhag
    Commented Feb 22, 2017 at 21:20
  • 1
    Is there a good reference to explain what these commands mean? Like what the + sign does. This is close, except I guess the real format for my filenames is Device1_Ex_1.jpg Where this syntax would turn that into Device01_Ex_1.jpg and not Device1_Ex_01.jpg
    – toombzie
    Commented Feb 22, 2017 at 21:21
  • 1
    @dhag The asker definitely has a Perl-based version, both because they aren't running Linux so they don't have the util-linux rename, and because the output of rename -v explicitly mentions Perl. Commented Feb 22, 2017 at 22:24
  • 1
    Yes, OK, my wording was terrible. I should have said "the version that accepts a Perl expression as one of its parameters". This still looks like a wrong answer, doesn't it?
    – dhag
    Commented Feb 22, 2017 at 22:44
  • Interestingly, while this appears to contradict the help text which, if I understand correctly, requires -e before the Perl code, this call actually works with the rename utility that comes with macOS. I retract my initial comment.
    – dhag
    Commented Feb 23, 2017 at 14:04
6

With the version of rename that you seem to be using, the following expression should do the transformation you request (example with a directory containing files named File_Ex_{1..11}.jpg):

$ rename -n -e 's/_(\d\.)/_0$1/g' -- *.jpg
'File_Ex_1.jpg' would be renamed to 'File_Ex_01.jpg'
'File_Ex_2.jpg' would be renamed to 'File_Ex_02.jpg'
'File_Ex_3.jpg' would be renamed to 'File_Ex_03.jpg'
'File_Ex_4.jpg' would be renamed to 'File_Ex_04.jpg'
'File_Ex_5.jpg' would be renamed to 'File_Ex_05.jpg'
'File_Ex_6.jpg' would be renamed to 'File_Ex_06.jpg'
'File_Ex_7.jpg' would be renamed to 'File_Ex_07.jpg'
'File_Ex_8.jpg' would be renamed to 'File_Ex_08.jpg'
'File_Ex_9.jpg' would be renamed to 'File_Ex_09.jpg'

(Remove flag -n to actually do the renaming.)

The argument to -e is a Perl search-and-replace expression; where _(\d\.) matches am underscore followed by a single digit and then a dot, and replaces the underscore with _0, thus inserting a leading zero. $1 is a back-reference to the group within parentheses, the digit and dot, and leaves it unchanged in the new file name.

2

While it can be done with prename command, I would like to provide a python alternative to that:

$ ls
File_Ex_1.jpg  File_Ex_2.jpg  File_Ex_3.jpg  File_Ex_4.jpg

$ ./pad_zeros.py *                                                                                                       

$ ls
File_Ex_01.jpg  File_Ex_02.jpg  File_Ex_03.jpg  File_Ex_04.jpg  pad_zeros.py*

The pad_zeros.py script is simple:

#!/usr/bin/env python
import sys,os,re

for file in sys.argv[1:]:
    if __file__ in file: continue
    words = re.split('_|\.',file)
    words[-2] = words[-2].zfill(2)
    new_name = "_".join(words[:-1]) + "." + words[-1]
    os.rename(file,new_name)

It takes arguments on command-line, iterates over them, and pads zeros using .zfill() command(in this particular case to two characters). Since we're operating on all files in the same folder, glob-star can be used.

5
  • This is the best answer adjustable to however many files need to be renamed. Commented Nov 9, 2019 at 18:23
  • Although I am getting an Anomalous backslash in string: '\g'. String constant might be missing an r prefix. (anomalous-backslash-in-string) error Commented Nov 9, 2019 at 18:39
  • could not run it so I chmod 755'd it. and now getting a boat load of errors:import-im6.q16: not authorized sys' @ error/constitute.c/WriteImage/1037. import-im6.q16: not authorized os' @ error/constitute.c/WriteImage/1037. import-im6.q16: not authorized re' @ error/constitute.c/WriteImage/1037. ./renameLeadZero.py: line 6: syntax error near unexpected token if' ./renameLeadZero.py: line 6: if file in file: continue' ` I am using a conda env so probably have to designate the interpreter on the shebang differently? Commented Nov 9, 2019 at 18:52
  • 1
    @mLstudent33 That's peculiar. The error in your second comment probably stems from a backslash in one of the filenames. Python regex thinks it's a special backslash escape. We probably can get around that by adding repr() around file in re.split() line, or string formatting ( stackoverflow.com/q/18707338/3701431 ) but we'll need to take into account whether it's Python3 or Python2.7 we have on the system - their string formatting is different just a bit. For the error in the third comment, I cannot confirm nor deny - never worked with Conda. Commented Nov 9, 2019 at 19:49
  • Thanks for the reply. Commented Nov 9, 2019 at 19:52
0

I have done with ls + awk + sh for the sake of challenge:

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

-F_ ............... field separator
%s ................ string to "mv "$0" "$1"_"$2"_"
%02d .............. our numeric field with leading zeros

To just see the command with no modifications remove the | sh

2
  • In general, providing commands in plain text to sh, after processing unknown filenames, is vulnerable to code injection. Think of a file named 1 2;rm -rf ~;_Ex_1.jpg.
    – thanasisp
    Commented Nov 15, 2020 at 14:34
  • 1
    For sure thanasisp, but as I have said we can see the command before runing it simply by removing the | sh. Commented Nov 15, 2020 at 15:18
0

One answer above gives:

rename -e 's/\d+/sprintf("%02d",$&)/e' -- *.jpg

However, that gives incorrect results for me, since *.jpg sorts the files in strict numerical order. Running rename with the -n flag for a dry run gives me:

rename(9999.jpg, frames_0000039996.jpg) 
rename(999.jpg, frames_0000039997.jpg) 
rename(99.jpg, frames_0000039998.jpg) 
rename(9.jpg, frames_0000039999.jpg)

Correct results can be had with:

rename 's/.+/our $i; sprintf("frames_%010d.jpg", 0+$i++)/e' $(ls -1 | sort -V)

which gives

rename(9999.jpg, frames_0000039996.jpg)
rename(999.jpg, frames_0000039997.jpg)
rename(99.jpg, frames_0000039998.jpg)
rename(9.jpg, frames_0000039999.jpg)

with the -n flag.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .