5

I have A LOT of files named "bar_foo.txt" in a directory, and I'd like to rename them to "foo_bar.txt" instead.

In sed and vim the regex to to this would be something like 's/\(bar\)_\(foo\)/\2_\1/g', backreferencing the search in the replacement.

  1. Is there a way to do this with rename? I've seen some hacks piping ls to sed to bash, but that's obviously not an amazing way to do it.

  2. Are there another tools that does this?

  3. Is there a name for the "sed and vim flavour" of regex?

lindhe
  • 4,236
  • 3
  • 21
  • 35

3 Answers3

5

If you have the rename implementation with Perl regexes (as on Debian/Ubuntu/…, or prename on Arch Linux), you need $1 instead of \1. Also, no backslashes on capturing parentheses:

rename 's/(.*)_(.*)/$2_$1/' *_*

If not, you have to implement it yourself.

#! /bin/bash
for file in *_* ; do
    left=${file%_*}
    right=${file##*_}
    mv "$file" "$right"_"$left"
done

Note: As written, both commands rename a_b_c to c_a_b. To get b_c_a, change the first capture group to .*? in the first case, or % to %% and ## to # in the second one.

choroba
  • 47,233
0

The standard tool that does this is pax - though it works better with hardlinks and copies than with direct moves. Of course, all a move is, essentially, is creating a new link and removing an old one.

I prefer to disengage these steps - for a brief while I have two copies of the same tree and can compare and contrast. This is especially important when renaming files dynamically - it is possible that the result of an edit operation on a filename might be identical to an existing linkname, and in that case which file gets the name?

And so to do the thing as asked POSIXly you can do:

pax -rwls'|^\(bar\)_\(foo\).txt$|\2_\1.txt|' -s'|.*||' *_*.txt .

The results will be separate links to the files in question when you are through. Verify the results are as you like and remove the old ones.

And the sed, vim regex style is standard BRE

mikeserv
  • 58,310
0
  1. With the util-linux rename, no, this one only does basic string replacement. With the Perl-based rename, yes, see choroba's answer.

  2. With zsh's zmv:

    autoload -U zmv      # put this in your ~/.zshrc
    zmv '(*)_(*).(*)' '${2}_$1.$3'
    zmv -w '*_*.*' '${2}_$1.$3'
    

    The two zmv invocations above are equivalent. To act in subdirectories as well, you can use zmv -w '**/*_*.*' '$1/${3}_$2.$4'

  3. Vi(m) and sed use (extensions of) basic regular expressions. See also Why does my regular expression work in X but not in Y?