I have the following files


I want to rename the files as

  • 1
    If you only have five files, you can always open whatever graphical file manager you have, and use it. Besides, you're giving 5 times the same file name... – gniourf_gniourf May 20 '14 at 17:37
  • Yeah it will be a little hard to do this if the source files are all named the same thing. – Bratchley May 20 '14 at 17:42
  • Do you want to copy the same file 6 times with 6 different names or do you actually have 6 different input files? – terdon May 20 '14 at 17:48
  • I have following files SSt12.precip.374.sub.climatology.ctl, SSt12.precip.2874.sub.climatology.ctl, SSt12.precip.3764.sub.climatology.ctl, SSt12.precip.6774.sub.climatology.ctl I want to rename as SSt12.precip.1.sub.climatology.ctl, SSt12.precip.2.sub.climatology.ctl, SSt12.precip.3.sub.climatology.ctl, SSt12.precip.4.sub.climatology.ctl please help me with unix script – sourabh bal May 20 '14 at 19:18
  • 1
    Welcome to Unix & Linux Stack Exchange! Please edit your question to add extra info, it is hard to read and easy to miss in the comments. You already have two answers that do what you want. What else do you need? – terdon May 20 '14 at 19:37

With zsh:

$ autoload zmv
$ n=0; zmv -n '(*.)<->(*.ctl)(#qn)' '$1$[++n]$2'
mv -- SSt12.precip.374.sub.climatology.ctl SSt12.precip.1.sub.climatology.ctl
mv -- SSt12.precip.2874.sub.climatology.ctl SSt12.precip.2.sub.climatology.ctl
mv -- SSt12.precip.3764.sub.climatology.ctl SSt12.precip.3.sub.climatology.ctl
mv -- SSt12.precip.6774.sub.climatology.ctl SSt12.precip.4.sub.climatology.ctl

(do it again without the -n to actually perform the renaming).

With GNU tools and assuming filenames don't contain newline characters, you could do:

ls -v | awk -F. -vOFS=. -vORS='\0' '/\.ctl$/{print;$3=++n;print}' |
  xargs -r0n2 echo mv --

(remove the echo to actually do the renaming)

  • mv -- SSt12.precip.374.sub.climatology SSt12.precip.1.sub.climatology mv -- SSt12.precip.374.sub.climatology.ctl SSt12.precip.2.sub.climatology.ctl mv -- SSt12.precip.374.sub.climatology.gmp SSt12.precip.3.sub.climatology.gmp mv -- SSt12.precip.2083-2313.dm ..this script collects all the files. I want to rename it increasing for files*.ctl, *.gmp seperately – sourabh bal May 21 '14 at 11:36
  • @sourabhbal, see update – Stéphane Chazelas May 21 '14 at 11:39
printf 'cp SSt12.precip.374.sub.climatology.ctl \
   SSt12.precip.%d.sub.climatology.ctl\n' $(seq 5) | 
. /dev/stdin

Apparently you did mean rename - sorry for the misunderstanding.

But if it does... say 374 - 378 ...

printf 'n=%d ; mv SSt12.precip.$((373+n)).sub.climatology.ctl \
   SSt12.precip.${n}.sub.climatology.ctl\n' $(seq 5) | 
. /dev/stdin

Regarding your comment above - assuming they're in the same directory and they're the only files that will match SSt12.precip.*.sub.climatology.ctl in there:

set -- SSt12.precip.*[67]4.sub.climatology.ctl
printf 'echo mv %s SSt12.precip.$((n=n+1)).sub.climatology.ctl\n' "$@" | 
    . /dev/stdin

I've nerfed it with echo above so you don't wind up with any regrets on my account. In its current form it will just show what it wants to do. Remove echo and the files are yours to move or lose.

See Stephane's comment below for a note on proper sort order - he's right.

  • That's a cool trick but as you point out it will copy, not rename the files. Why not use mv? – terdon May 20 '14 at 17:43
  • @terdon - Yeah - but... what else can you do? mv? I don't know - all 5 files are the same file as far as I can tell... – mikeserv May 20 '14 at 17:43
  • 1
    I was assuming that the OP actually has 6 different file names and the mistake was posting only one 6 times. You seem to be assuming that he/she actually wants 6 copies of the same file with different names. That is a perfectly reasonable assumption, I hadn't thought of that. – terdon May 20 '14 at 17:46
  • @terdon - maybe. Sometimes there's a language barrier - I don't know. If OP fixes it, I'll try to fix this too. It's as much as I can do with what I've got right now, though. – mikeserv May 20 '14 at 17:47
  • 2
    Note that in the question, the files are sorted numerically. So you'd need set SSt12.precip.*.sub.climatology.ctl(n) (zsh syntax) – Stéphane Chazelas May 20 '14 at 20:07

Assuming you actually have multiple files and not the same one, and that you therefore want to rename each of them, you could do this:

for i in *ctl; do echo "$i"; done | 
    sort -t'.' -nk3 | 
        awk -F'.' -v OFS='.' '{printf "mv %s ", $0; $3=NR;print}' |

The above assumes that your file names are exactly as you show in your question. They all have a number as the 3 dot-separated field and they all end with ctl.

  • 1
    Note that the middle bit in ${VAR/PATTERN/REPLACEMENT} is a shell wildcard pattern, not a regexp. You don't need the backslashes before ., and [0-9]* matches a digit followed by an arbitrary sequence of characters. This happens to work for the file names shown here. If you want regular expressions, use ksh93 extended glob syntax (shopt -s extglob in bash). – Gilles May 20 '14 at 23:25
  • @Gilles thanks, I did indeed thing they were regexes. Answer edited and needlessly complex alternative added. – terdon May 20 '14 at 23:36
  • @terdon +1 for needlessly complex – mikeserv May 21 '14 at 1:00
  • Another problem I forgot to mention earlier is that your script puts the file in the order 2874, 374, 3764, 6774 rather than in numerical order. Stephane avoids this problem thanks to zsh's n glob qualifier. – Gilles May 21 '14 at 7:26
  • The above scripts failed to rename in increment order as prescribed. e.g – sourabh bal May 21 '14 at 11:10

The first hurdle is getting the files in the right order. The wildcard pattern SSt12.precip.*.sub.climatology.ctl expands the file names in lexicographic order, not in numerical order, so it puts the files in the order 2874, 374, 3764, 6774.

In zsh, this is easily remedied thanks to the n glob qualifier (SSt12.precip.*.sub.climatology.ctl(n)). In other shells, this takes more work. You can extract the numbers and sort them. Here's a way that works as long as your file names don't contain any newline. I extract the number by stripping off the first two dot-separated segments and then all but the first dot-separated segment.

for x in SSt12.precip.*.sub.climatology.ctl; do
  number="${x#*.*.}"; number="${number%%.*}"
  printf "%d %s\n" "$number" "$x"
done | sort -k1,1n |
while IFS= read -r filename; do
  filename=${filename#* }
  mv -- "$filename" "$prefix.$number.$suffix"
  • mv: cannot stat 374': No such file or directory mv: cannot stat 2874': No such file or directory mv: cannot stat 2964': No such file or directory mv: cannot stat 6838': No such file or directory mv: cannot stat `8649': No such file or directory getting the following report – sourabh bal May 21 '14 at 11:15
  • @sourabhbal Sorry, there were a few silly errors, I've fixed them now. – Gilles May 21 '14 at 11:20
  • running bash shell I am getting following comments mv: cannot stat *': No such file or directory mv: cannot stat SSt12.precip.*.sub.climatology.ctl': No such file or directory – sourabh bal May 21 '14 at 11:21
  • 1
    Again -k1n doesn't do what you think it does. The k1 is redundant. Why not printf '%s\n' *.ctl | sort -t. -k3,3n – Stéphane Chazelas May 21 '14 at 12:33
  • 1
    @StephaneChazelas Ok, fair enough, I've changed to -k1,1n. – Gilles May 21 '14 at 19:53

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.