I am trying to rename files named in the form of splitfile[a-z] to splitfile[1-26].csv, that is I want to rename splitfilea to splitfile1.csv and so on.
I tried the following command:

mv splitfile[a-z] splitfile[1-26].csv 

but it doesn't work.Kindly help me, meanwhile I am trying with for loop.

  • 2
    You may want to consider padding with leading zeros, like splitfile01.csv. The way you suggest, ordinary sorting will put them in the order splitfile1, splitfile10, splitfile11, ... splitfile19, splitfile2, splitfile20, ... which is often inconvenient. In particular, doing cat splitfile*.csv will no longer combine them back into the proper order. – Nate Eldredge Jan 4 '16 at 18:31

With new bash you can use brace expansion of the form {a..z}:

i=1; for f in file{a..z}; do mv "$f.csv" "${f%?}$((i++)).csv"; done
  • in file[a-z] is work too but unsorted, so it can be tricked by $(echo file[a-z] | sort) – Costas Jan 4 '16 at 14:21
  • What about using file* instead of file{a..z}? I think it should work... – Mohammad Jan 4 '16 at 16:32
  • And please note that do mv "$f.csv" should be do mv "$f" according to the question. – Mohammad Jan 4 '16 at 16:34
  • @mohammad.k It cannot be file* because that would match file_something_not_a_single_letter too. Also it has to be mv "$f.csv" instead of mv "$f", because file{a..z} matches filea, wiithout suffix .csv. – jimmij Jan 4 '16 at 16:40
  • 2
    @Costas - globs are sorted by locale by default. – mikeserv Jan 4 '16 at 17:58

If you have the perl utility rename (sometimes called prename) you can use it to rename your files

rename -n 's/([a-z])$/ord($1)-96/e and $_ .= ".csv"' splitfile?

What this does is to take the last letter of your filenames matching splitfile?, convert the character to its ASCII ordinal equivalent (i.e. a=97, b=98, ...), subtract 96 and replace the matched letter with the result. For all filenames where this substitution was applied successfully the string ".csv" is appended.

The command above won't change your actual files (that's what the -n flag does), it will only print what it would do. Run it once, check the output and, if it is correct, run it again without the -n.


With zsh one doesn't need to use a loop, just a zmv function:

autoload -U zmv
i=1 zmv -v '(file)[a-z].csv' '$1$((i++)).csv'

Or if not all the file[a-z].csv may exist:

zmv -v '(file)([a-z]).csv' '$1$letters[(i)$2].csv'

This sh script correctly renames files even if files are missing from the a-z sequence:

for n in $(seq 26)
    f=$(printf $(printf 'file\%03o.csv' $((n+96))))
    [ -e $f ] && echo mv $f file$n.csv

This is a way to do it with a POSIX shell, assuming all files exist in the a-z sequence:

for f in file[a-z].csv
    echo mv $f file$i.csv
  • 2
    not exactly... consider the cases of matches on a and c but not b. – mikeserv Jan 4 '16 at 14:52
  • @mikeserv, yea, so far only the perl script seems to account for that. – RobertL Jan 4 '16 at 16:42

Another fairly portable solution...

    eval    set "'' $(dc -e'123[1-ddP32P97<a]salax')"  ### reverse-order lowercase ascii
    while   "${2+shift}" 2>&-                          ### only iterate while we should
    do      for f   in ./*["$1"].ext                   ### glob* is fine - sorting's done
            do      [ "${f%%*\]????}" ] &&             ### *null glob* kind of
            echo    mv "$f" "${f%?.*}${?%${10+?}}$#.${f##*.}"  ### echo doesn't mv much
    done;   done                                       ### $? zero-pads to two decimals

After doing the set line above, I then generated a test set for this solution like...

    eval '  for A   in  '"$(printf %1.s%s%1.s "$@" "" ";do for a in$@")"';
            do      echo  "$A$a" > "${A}file$a.ext";        done;   done'

That iteratively skips every two letters for every two letters... if that makes sense. The results were:

ls -m

bfilec.ext, bfilef.ext, bfilei.ext, bfilel.ext, bfileo.ext, bfiler.ext,
bfileu.ext, bfilex.ext, efilec.ext, efilef.ext, efilei.ext, efilel.ext,
efileo.ext, efiler.ext, efileu.ext, efilex.ext, hfilec.ext, hfilef.ext,
hfilei.ext, hfilel.ext, hfileo.ext, hfiler.ext, hfileu.ext, hfilex.ext,
kfilec.ext, kfilef.ext, kfilei.ext, kfilel.ext, kfileo.ext, kfiler.ext,
kfileu.ext, kfilex.ext, nfilec.ext, nfilef.ext, nfilei.ext, nfilel.ext,
nfileo.ext, nfiler.ext, nfileu.ext, nfilex.ext, qfilec.ext, qfilef.ext,
qfilei.ext, qfilel.ext, qfileo.ext, qfiler.ext, qfileu.ext, qfilex.ext,
tfilec.ext, tfilef.ext, tfilei.ext, tfilel.ext, tfileo.ext, tfiler.ext,
tfileu.ext, tfilex.ext, wfilec.ext, wfilef.ext, wfilei.ext, wfilel.ext,
wfileo.ext, wfiler.ext, wfileu.ext, wfilex.ext, zfilec.ext, zfilef.ext,
zfilei.ext, zfilel.ext, zfileo.ext, zfiler.ext, zfileu.ext, zfilex.ext

And then I tested it...

while ...; do ...; done | { HOME=/dev/null; paste -d', ' - ~ - ~; }

mv ./bfilex.ext ./bfile24.ext, mv ./efilex.ext ./efile24.ext,
mv ./hfilex.ext ./hfile24.ext, mv ./kfilex.ext ./kfile24.ext,
mv ./nfilex.ext ./nfile24.ext, mv ./qfilex.ext ./qfile24.ext,
mv ./tfilex.ext ./tfile24.ext, mv ./wfilex.ext ./wfile24.ext,
mv ./zfilex.ext ./zfile24.ext, mv ./bfileu.ext ./bfile21.ext,
mv ./efileu.ext ./efile21.ext, mv ./hfileu.ext ./hfile21.ext,
mv ./kfileu.ext ./kfile21.ext, mv ./nfileu.ext ./nfile21.ext,
mv ./qfileu.ext ./qfile21.ext, mv ./tfileu.ext ./tfile21.ext,
mv ./wfileu.ext ./wfile21.ext, mv ./zfileu.ext ./zfile21.ext,
mv ./bfiler.ext ./bfile18.ext, mv ./efiler.ext ./efile18.ext,
mv ./hfiler.ext ./hfile18.ext, mv ./kfiler.ext ./kfile18.ext,
mv ./nfiler.ext ./nfile18.ext, mv ./qfiler.ext ./qfile18.ext,
mv ./tfiler.ext ./tfile18.ext, mv ./wfiler.ext ./wfile18.ext,
mv ./zfiler.ext ./zfile18.ext, mv ./bfileo.ext ./bfile15.ext,
mv ./efileo.ext ./efile15.ext, mv ./hfileo.ext ./hfile15.ext,
mv ./kfileo.ext ./kfile15.ext, mv ./nfileo.ext ./nfile15.ext,
mv ./qfileo.ext ./qfile15.ext, mv ./tfileo.ext ./tfile15.ext,
mv ./wfileo.ext ./wfile15.ext, mv ./zfileo.ext ./zfile15.ext,
mv ./bfilel.ext ./bfile12.ext, mv ./efilel.ext ./efile12.ext,
mv ./hfilel.ext ./hfile12.ext, mv ./kfilel.ext ./kfile12.ext,
mv ./nfilel.ext ./nfile12.ext, mv ./qfilel.ext ./qfile12.ext,
mv ./tfilel.ext ./tfile12.ext, mv ./wfilel.ext ./wfile12.ext,
mv ./zfilel.ext ./zfile12.ext, mv ./bfilei.ext ./bfile09.ext,
mv ./efilei.ext ./efile09.ext, mv ./hfilei.ext ./hfile09.ext,
mv ./kfilei.ext ./kfile09.ext, mv ./nfilei.ext ./nfile09.ext,
mv ./qfilei.ext ./qfile09.ext, mv ./tfilei.ext ./tfile09.ext,
mv ./wfilei.ext ./wfile09.ext, mv ./zfilei.ext ./zfile09.ext,
mv ./bfilef.ext ./bfile06.ext, mv ./efilef.ext ./efile06.ext,
mv ./hfilef.ext ./hfile06.ext, mv ./kfilef.ext ./kfile06.ext,
mv ./nfilef.ext ./nfile06.ext, mv ./qfilef.ext ./qfile06.ext,
mv ./tfilef.ext ./tfile06.ext, mv ./wfilef.ext ./wfile06.ext,
mv ./zfilef.ext ./zfile06.ext, mv ./bfilec.ext ./bfile03.ext,
mv ./efilec.ext ./efile03.ext, mv ./hfilec.ext ./hfile03.ext,
mv ./kfilec.ext ./kfile03.ext, mv ./nfilec.ext ./nfile03.ext,
mv ./qfilec.ext ./qfile03.ext, mv ./tfilec.ext ./tfile03.ext,
mv ./wfilec.ext ./wfile03.ext, mv ./zfilec.ext ./zfile03.ext,

Here's an alternative suggestion, which doesn't support missing files, but I felt the need to try a more "functional" solution that works on tuples:

splitfile_renamer() {
  mv splitfile$1 splitfile$2.csv

paste <(echo -en {a..z}"\n") <(seq 26) | while read args
  do splitfile_renamer $args

The above uses paste to create the argument tuples (separated by whitespace), and passes them to a pre-made function which then takes them in as numbered arguments.

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.