Renaming a bunch of files

This is possibly a FAQ, but I was unable to find an answer: let's say you have two files named "hello.txt" and "goodbye.txt" and you want them to be "hi.txt" and "seeyou.txt". The typical regular expressions renamer apps do not apply, as you want different new names for each one of the files. The first logical step is creating a text file with the new names:

hi.txt
seeyou.txt

Then, using the command paste I got that:

#!/bin/bash
# Usage: rename_from_file.sh newfilenames.txt hello.txt goodbye.txt
set -e
NEWFILENAMES=$1
shift
for FILENAME in "$@"; do 
    echo "$FILENAME" 
done | paste - $NEWFILENAMES | tr '\n' '\t' | xargs -rt -d"\t" -n2 mv

Seeking a one-liner solution I came across the interesting "process substitution" bash feature, and this is what I got (still using paste):

#!/bin/bash
# Usage: rename_from_file.sh hello.txt goodbye.txt <newfilenames.txt
paste <(for FILE in "$@"; do echo "$FILE"; done) - | tr '\n' '\t' | xargs -rt -d"\t" -n2 mv

I cannot say I am proud of it, so... any better ideas?

regards

Why not just specify "from to" pairs?

while read from to; do
  mv "$from" "$to"
done <<HERE
  hello.txt hi.txt
  goodbye.txt seeyou.txt
HERE

Thanks for your reply. I see two problems, though:

  • You build by hand the pairs to rename, and this should be automatic. That's why I use paste, to simulate a zip function found in some scripting languages.

  • read will fail if the filename contains spaces. This is easily solvable setting IFS to tab.

Using both paste and the while read, we could write:

paste <(for FILE in "$@"; do echo "$FILE"; done) - | \
    while IFS=$(echo -e "\t") read FROM TO; do mv "$FROM" "$TO"; done