5

I have files named as 0-n.jpg for n from 1 to 500, for example.

The problem is that some guy using Windows didn't use leading zeros so when I do ls I obtain

0-100.jpg
0-101.jpg
...
0-10.jpg
...
0-199.jpg
0-19.jpg
0-1.jpg

So I'd like to rename them to insert the leading zeros, so that the result of ls could be

0-001.jpg
0-002.jpg
...
0-100.jpg
...
0-499.jpg
0-500.jpg

In other words, I'd like all files with the same file name lengths.

I tried this solution but I'm getting a sequence of errors like

bash: printf: 0-99: invalid number
2
  • Well, your situation is a bit different from the one you linked to. The general approach will work, but your numbers are not separated by ., but by - and ., which will require some modifications to the script. How did you modify the script? Commented Sep 15, 2014 at 22:08
  • I'm testing it before using only echo to see the output. for f in *.jpg; do int=basename $f .jpg | cut -d '.' -f 2; echo $int; new_name=printf "e.%04i.jpg\n" $int; echo $new_name; done; Commented Sep 15, 2014 at 22:14

4 Answers 4

9

If your system has the perl-based rename command you could do something like

rename -- 's/(\d+)-(\d+)/sprintf("%d-%03d",$1,$2)/e' *.jpg

Testing it using the -v (verbose) and -n (no-op) options:

$ rename -vn -- 's/(\d+)-(\d+)/sprintf("%d-%03d",$1,$2)/e' *.jpg
0-10.jpg renamed as 0-010.jpg
0-19.jpg renamed as 0-019.jpg
0-1.jpg renamed as 0-001.jpg
2
  • Ow, perfect! It worked. So to rename the files I only need to remove the -vn options? Commented Sep 15, 2014 at 22:21
  • 1
    @Sigur, you only need to remove the -n option, which means "don't actually rename anything". The -v option prints the message about each file renamed, which you can keep or remove as you like. Commented Sep 15, 2014 at 22:31
3

If you have zsh shell, you could do something like below.

zmv '([0-9])-([0-9]##).(jpg)' '$1-${(l:3::0:)2}.$3'

Testing

touch 0-1.jpg
touch 0-23.jpg
touch 0-345.jpg
touch 0-6.jpg
touch 0-05.jpg

Change the shell to zsh and if zmv is not loaded, you could do autoload zmv.

Now, you could add -n flag to the zmv command to see what will happen if you execute the zmv command. I am running the command as,

zmv -n '([0-9])-([0-9]##).(jpg)' '$1-${(l:3::0:)2}.$3'

The output is,

zmv -n '([0-9])-([0-9]##).(jpg)' '$1-${(l:3::0:)2}.$3'
mv -- 0-05.jpg 0-005.jpg
mv -- 0-1.jpg 0-001.jpg
mv -- 0-23.jpg 0-023.jpg
mv -- 0-6.jpg 0-006.jpg

If you are satisfied the file names are renamed correctly, you could remove the -n flag.

References

Thanks to user Gnouc for clarifying my doubt on this question which I had posted as another question to get this solution and the link to Gnouc's answer is this one.

2

another solution in case you do not have "rename:

for file in *.jpg; do
   [ -f "$file" ] || continue  #skips if no jpg file present in current dir
   echo "${file}" | awk -F'[-.]' '
      {   new=sprintf("%03d",$2); 
          print "echo TESTING mv ", $0," ",$1"-"new"."$3 
      }'
done | bash

(edit: moved the | bash so it's only invoked once, and not once per file. And this quick loop is not "weird filename" friendly, but could be sanitized further (to handle files with spaces, etc))

and remove the 2 words echo TESTING to really do the mv ..., once you are sure it's doing what you want it to do.

2
  • Thanks. I'll try to follow your code. I didn't know about sprintf. Commented Sep 16, 2014 at 11:49
  • 1
    Sprintf is a printf that also outputs its result as a string (= string printf), so it can be assigned to a variable Commented Sep 16, 2014 at 12:12
2

Here's another approach. Since you only need to rename a specific subset of files and since you already know what those are, just list them and rename them:

for i in {1..99}; do mv -- 0-$i.jpg 0-$(printf '%03d\n' $i).jpg; done

That will list the numbers from 1 to 99, saving each value as $i. You don't care about the rest since those are already named correctly. You then rename 0-$i.jpg to the padded version. The printf %03d directive will print a number adding 0s to the left hand side if it is less than 3 characters in length. The result is that all your problematic files will be renamed.

4
  • What is the meaning of -- after mv? Commented Sep 16, 2014 at 15:37
  • 1
    @Sigur it's there to tell mv that there are no more command line flags to process. It is useful when you need to pass a file name that contains a - and could be interpreted as an option. Commented Sep 16, 2014 at 15:47
  • 1
    @Sigur -- tells the command that the arguments that follow it aren't options, even if they start with -. It isn't actually necessary here since the remaining arguments all start with 0, but it's a good habit to follow. Commented Sep 16, 2014 at 21:11
  • A better habit to follow would be to add ./ to each filenames (as it works with every commands, even those who don't have '--', even maybe some old rm somewhere?) (But of course my answer needs this plus many other things to handle weird cases gracefully too) Commented Sep 17, 2014 at 21:54

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.