6

I have files such as

a
bb
ccc
abc
emrls

I would like to rename them so that, after sorting them (for example, lexicographically), each file gets the following name:

00001
00002
00003
etc

where the # of digits for the 0 padding is specified a priori, e.g. 6 in the case above (assuming that we know how many digits are needed).

Since the shell I am most comfortable with is zsh, I'm interested in solutions that benefit from features on zsh (maybe using zsh's zmv?). I would also be interested in solutions that are compatible with Bash.

10

You can use the l parameter expansion flag to pad a number on the left.

i=0; for x in *; do ((++i)); mv -- $x new/${(l:6::0:)i}; done

There is a relatively simple way to do this with only POSIX features: start numbering at 1000001 (for 6 digits) instead of 1, and strip off the leading 1. It is less straightforward but a few characters shorter.

i=1000000; for x in *; do i=$((i+1)); mv -- "$x" new/${i#1}; done

If you want to take advantage of zmv, you can use an arithmetic expression that increments i inside the replacement text.

i=0; zmv '*' '${(l:6::0:)$((++i))}'
i=1000000; zmv '*' '${$((++i))#1}'

Add the o glob qualifier if you need to sort the files in a different order. With zmv, you need to pass the -Q flag when the pattern contains glob qualifiers.

  • Wow.. @Gilles, this is better! :-) – Nikhil Mulley Dec 9 '11 at 18:16
  • Thanks Gilles, this is great. By the way, what is the purpose of -- in mv? – Amelio Vazquez-Reina Dec 19 '11 at 22:35
  • 1
    @intrpc The -- is in case one of the file names begins with -. It tells mv that what follows is not an option. – Gilles 'SO- stop being evil' Dec 19 '11 at 22:39
  • This seems to work: i=0; zmv '*' '$((++i)) but I am getting an error with i=0; zmv '*' '${(l:6::0:)$((++i))}' (the first of the two options suggested for zsh). I – Amelio Vazquez-Reina Feb 5 '13 at 22:37
  • Ops. I think it is because I typed the letter l as the number 1 (I didn't copy+paste). It's working again. Thanks! – Amelio Vazquez-Reina Feb 5 '13 at 22:40
2

Use this bash snippet.

[centos@centos new]$ touch a bb ccc dddd eee f gh i
[centos@centos new]$ touch abc emrls cdg sf
[centos@centos new]$ touch ABC A BB CCC DD GI KLM kmna kabc mas nas san fin zoo
[centos@centos new]$ \ls -1
a
A
abc
ABC
bb
BB
ccc
CCC
cdg
DD
dddd
eee
emrls
f
fin
gh
GI
i
kabc
KLM
kmna
mas
nas
san
sf
zoo
[centos@centos new]$ a=0; for i in *; do a=$(($a+1));  b=`printf "%06d" $a`; mv -v ${i} ${b};  done
`a' -> `000001'
`A' -> `000002'
`abc' -> `000003'
`ABC' -> `000004'
`bb' -> `000005'
`BB' -> `000006'
`ccc' -> `000007'
`CCC' -> `000008'
`cdg' -> `000009'
`DD' -> `000010'
`dddd' -> `000011'
`eee' -> `000012'
`emrls' -> `000013'
`f' -> `000014'
`fin' -> `000015'
`gh' -> `000016'
`GI' -> `000017'
`i' -> `000018'
`kabc' -> `000019'
`KLM' -> `000020'
`kmna' -> `000021'
`mas' -> `000022'
`nas' -> `000023'
`san' -> `000024'
`sf' -> `000025'
`zoo' -> `000026'
[centos@centos new]$ 
  • I am assuming ls -1| sort would do your lexicographical sort, however if you need any other type of sort, just change that command but the zero padding and moving can be same. – Nikhil Mulley Dec 9 '11 at 18:08
  • 1
    Ugh, don't parse the output of ls! $(ls -1 | sort) is the same thing as $(ls) anyway, and your code fails with file names containing whitespace or globbing characters (you'd need to set -f and set IFS to a newline, and then your code would still fail with filenames containing nonprintable characters or newlines). for i in *; do – Gilles 'SO- stop being evil' Dec 9 '11 at 18:16
  • 1
    Done. Just updated the snippet. Thanks @Gilles – Nikhil Mulley Dec 9 '11 at 18:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.