Stephen Smith
Stephen Smith

Reputation: 377

Move around the characters in a filename

I've got a folder full of files with the names ab1234, abc5678, etc., and I want to switch them to abc3412, abc7856, etc. – just swap the last two characters out with the second-to-last two characters. The filenames are all in this format, no surprises. What's the easiest way to do this with a regex?

Upvotes: 0

Views: 91

Answers (2)

mklement0
mklement0

Reputation: 438093

Depending on your platform, you may have a rename utility that can directly do what you want. For instance, anishsane's answer shows an elegant option using a Perl-based renaming utility.

Here's a POSIX-compliant way to do it:

printf '"%s"\n' * | sed 'p; s/\(..\)\(..\)"$/\2\1"/' | xargs -L 2 mv
  • printf '"%s"\n' * prints all files in the current folder line by line (if there are subdirs., you'd have to exclude them), enclosed in literal double-quotes.
  • sed 'p; s/\(..\)\(..\)"$/\2\1"/' produces 2 output lines:
    • p prints the input line as-is.
    • s/\(..\)\(..\)"$/\2\1"/' matches the last 2 character pairs (before the closing ") on the input lines and swaps them.
    • The net effect is that each input line produces a pair of output lines: the original filename, and the target filename, each enclosed in double-quotes.
  • xargs -L 2 mv then reads pairs of input lines (-L 2) and invokes the mv utility with each line as its own argument, which results in the desired renaming. Having each line enclosed in double-quotes ensures that xargs treats them as a single argument, even if they should contain whitespace.

Tip of the hat to anishsane for the enclose-in-literal-double-quotes approach, which makes the solution robust.

Note: If you're willing to use non-POSIX features, you can simplify the command as follows, to bypass the need for extra quoting:

  • GNU xargs:

    printf '%s\n' * | sed 'p; s/\(..\)\(..\)$/\2\1/' | xargs -d '\n' -L 2 mv
    

    Nonstandard option -d '\n' tells xargs to not perform word splitting on lines and treat each line as a single argument.

  • BSD xargs (also works with GNU xargs):

    printf '%s\n' * | sed 'p; s/\(..\)\(..\)$/\2\1/' | tr '\n' '\0' | xargs -0 -L 2 mv
    

    tr '\n' '\0' replaces newlines with \0 (NUL) chars, which is then specified as the input-line separator char. for xargs with the nonstandard -0 option, again ensuring that each input line is treated as a single argument.

Upvotes: 2

anishsane
anishsane

Reputation: 20980

Use perl rename?

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

Upvotes: 2

Related Questions