I wanted to do a simple script to rename files, well parts of files. While the command works just fine from commandline:

find . -type f -name "*old*" -exec bash -c 'mv "$0" "${0/old/new}"' {} \; 2>&1 | grep -v "Permission denied" | grep -v "are the same file"

I tried to reimplement it in a shellscript:

# renamefiles.sh - type renamefiles <old> <new>
find . -type f -name "*$1*" -exec bash -c 'mv "$0" "${0/$1/$2}"' {} \; 2>&1 | grep -v "Permission denied" | grep -v "are the same file"

When user would type,

$ renamefiles old new

A failure would occur (with error message suppressing disabled):

mv: './File0_old.txt' and './File0_new.txt' are the same file
mv: './Another_old_file.txt' and './Another_new_file.txt' are the same file

Let's start with two possible solutions first, then describe why you got the error.

Solution 1::

Just "temporarily" export the two variables so that they will be available to your sub-shell

# renamefiles.sh - type renamefiles <old> <new>

export from="$1" && export to="$2" && find . -type f -name "*$1*" -exec bash -c 'mv "$0" "${0/$from/$to}"' "{}" \; 2>&1 | grep -v "Permission denied"

exit 0

So here we export $1 into variable from, and $2 into variable to. This way, the sub-shell will be able to read them.

Solution 2:: (simpler, and may be preferred by those who don't like the temporary export thingie)

# renamefiles.sh - type renamefiles <old> <new>

find . -type f -name "*$1*" -exec bash -c 'mv "$0" "${0/$1/$2}"' "{}" "$1" "$2" \; 2>&1 | grep -v "Permission denied"

exit 0

This solution consists of just passing "$1" and "$2" besides {} (the current result from find) so that the sub-shell can access them normally as if it was a script on its own (with the exception that with the -c option passed to bash, the first argument would be $0 instead of $1; this seems to be known by you, as you've already used it)

Common notices regarding the two solutions::

  1. Always include an exit command at the end of your scripts
  2. Note the quoting for every variable being passed, even for {}. This would prevent misinterpretation for variables with spaces. Do this even if you're sure you don't have values or [here] filenames with spaces, as you don't know when you'll be later using your script.

Reason behind the error::

The reason behind the error you got ("are the same file") is very simple.

In your script, the mv command replaced the given file "$0" with another file given by the result of a string substitution ("${0/$1/$2}"). So in your case, $1 and $2 were not given to the sub-shell command, so it's like you did "${0//}" which would do nothing. As a result, the old and new filenames would be the same, hence the error.

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.