0

I have several files whose names aren't correct:

$ ls
devoirNote1_1_2.R  devoirNote1_1_5.R  devoirNote1_4_1.R
devoirNote1_1_3.R  devoirNote1_1.R    devoirNote1.R
devoirNote1_1_4.R  devoirNote1_2_1.R  example140.R

I want to change all devoirNote1_i_j.R with the i and j integers except devoirNote1_4_1.R to devoirNote1_{i+1}_j.R (only devoirNote1_4_1.R is left unchanged).

I thought of using the command mvlike mv devoirNote1_*_2.R devoirNote1_2_2.R but when several files match (for example if both devoirNote1_1_2.R and devoirNote1_3_2.R are present), it creates an issue.

Therefore, how to create a script or a command line that renames my files, by incrementing one of the variables in the filenames, but not all of them?

lgeorget
  • 13,914
  • What do you mean with all devoirNote1_1_2.R ? – ilkkachu Mar 25 '17 at 15:33
  • @ilkkachu you're right, that was unclear, I updated – Revolucion for Monica Mar 25 '17 at 15:45
  • Still unclear: there is only one devoirNote1_1_2.R with a 1 in the middle. Same for devoirNote1_2_1.R. Do you mean devoirNote1_1_* and devoirNote1_2_* respectively? Please clarify your question. – xhienne Mar 25 '17 at 17:02
  • @xhienne I updated with a variable i – Revolucion for Monica Mar 25 '17 at 17:13
  • There is only one file whose name matches devoirNote1_i_2.R. The way your problem is stated, all you have to do is mv devoirNote1_1_2.R devoirNote1_2_2.R. Only one command. Why is it a problem? Why do you need a script for this? Please clarify and provide us with the expected ls output after the move(s). – xhienne Mar 25 '17 at 17:30
  • @xhienne I understand my mistake : I provided a second variable that should make this clearer – Revolucion for Monica Mar 25 '17 at 17:32
  • @Marine1 I took the liberty of rephrasing your post. Let us know if this is what you meant. If it's not the case, please feel free to rollback the change. – lgeorget Mar 25 '17 at 22:29
  • By the way how do you decide whch filenames do not need change? Is only devoirNote1_4_1.R in this case? – lgeorget Mar 25 '17 at 22:30

3 Answers3

0

Perl's rename utility(*) can be used for this. It applies a given Perl command to each given filename, and renames the file to whatever it is the commands changed the name to.

So this would change _1_ to _2_ on all files the glob matches.

rename 's/_1_/_2_/' devoirNote1_*_2.R

Incrementing numerically can be done with the /e modified to the substitution command, making the replacement part a Perl snippet instead of a literal.

rename -v 's/_(\d+)_/"_" . ($1 + 1) . "_"/e' devoirNote1_*_2.R

This would pick a part of the filename with digits between underscores, capturing the digits to $1, and replace that with the number increased one, with the underscores added back. -v for verbose mode. With the list of files in your example, this would print devoirNote1_1_2.R renamed as devoirNote1_2_2.R as expected.

The affected files are whatever the glob in the end of the command line matches.

Though note that if you have, say 1_1_2 and 1_2_2, renaming the first one will fail, since it would collide with the next one. This would require either giving the file names in reverse order, or changing them to some non-colliding form first and then back, say by adding an extra character.

(* not the util-linux rename tool. See here and here)

ilkkachu
  • 138,973
0

A script would be overkill for two (batch) moves. Use the mmv utility:

mmv devoirNote1_2_"*" devoirNote1_3_#1
mmv devoirNote1_1_"*" devoirNote1_2_#1

In that order of course. If there are much more than two moves, then a script would be preferable indeed.

xhienne
  • 17,793
  • 2
  • 53
  • 69
0

Here we make make use of negative lookaheads to filter out files *Note1_3_2.R and *Note1_4_2.R from the search list.

The OP wanted to leave the *Note1_4_2.R untouched => we can't touch one previous file as well, viz., the *Note1_3_2.R, since we have nowhere to mv this.

The for loop logic is there to perform a reverse numeric sort on the index number in between the 1 and 2 so that we can perform a nondestructive mv operation.

perl -e '
   /(.*)_(\d+)_(.*)/ and
      rename $_, join "_", $1, $2+1, $3 or warn "Could not mv the file <<$_>>\n"
      for
         map  {                  $_->[0]                   }
         sort { $b->[2] <=> $a->[2] || $b->[1] <=> $a->[1] }
         map  {             [$_, /_(\d+)_(\d+)/]           }
         grep {     /^devoirNote1_(?![34]_1.R)\d+_\d+.R/   }
         <*.R>
'