Question

I have found several similar questions that have solutions, except they don't involve variables.

I have a particular pattern in a tree of files and directories - the pattern is the word TEMPLATE. I want a script file to rename all of the files and directories by replacing the word TEMPLATE with some other name that is contained in the variable ${newName}

If I knew that the value of ${newName} was say "Fred lives here", then the command

find . -name '*TEMPLATE*' -exec bash -c 'mv "$0" "${0/TEMPLATE/Fred lives here}"' {} \;

will do the job

However, if my script is:

newName="Fred lives here"
find . -name '*TEMPLATE*' -exec bash -c 'mv "$0" "${0/TEMPLATE/${newName}}"' {} \;

then the word TEMPLATE is replaced by null rather than "Fred lives here"

I need the "" around $0 because there are spaces in the path name, so I can't do something like:

find . -name '*TEMPLATE*' -exec bash -c 'mv "$0" "${0/TEMPLATE/"${newName}"}"' {} \;

Can anyone help me get this script to work so that all files and directories that contain the word TEMPLATE have TEMPLATE replaced by whatever the value of ${newName} is

eg, if newName="A different name" and a I had directory of

/foo/bar/some TEMPLATE directory/with files then the directory would be renamed to

/foo/bar/some A different name directory/with files

and a file called some TEMPLATE file would be renamed to

some A different name file

Was it helpful?

Solution 2

You have two options.

1) The easiest solution is export newName. If you don't export the variable, then it's not available in subshells, and bash -c is a subshell. That's why you're getting TEMPLATE replaced by nothing.

2) Alternatively, you can try to construct a correctly quoted command line containing the replacement of $newName. If you knew that $newName were reasonably well-behaved (no double quotes or dollar signs, for example), then it's easy:

find . -name '*TEMPLATE*' \
       -exec bash -c 'mv "$0" "${0/TEMPLATE/'"${newName}"'}"' {} \;

(Note: bash quoting is full of subtleties. The following has been edited several times, but I think it is now correct.)

But since you can't count on that, probably, you need to construct the command line by substituting both the filename and the substitution as command line parameters. But before we do that, let's fix the $0. You shouldn't be using $0 as a parameter. The correct syntax is:

bash -c '...$1...$1...' bash "argument"

Note the extra bash (many people prefer to use _); it's there to provide a sensible name for the subprocess.

So with that in mind:

find . -name '*TEMPLATE*' \
       -exec bash -c 'mv "$1" "${1/TEMPLATE/$2}"' bash {} "$newName" \;

OTHER TIPS

You an get around having to use quotes with IFS=$'\n' and since bash -c is a subshell an export of any variable is required. This works:

#!/bin/bash
IFS=$'\n'
export newName="Fred lives here"
find . -name '*TEMPLATE*' -exec bash -c 'mv "$0" "${0/TEMPLATE/${newName}}"' {} \;

If you do not mind two more lines and would like a script that is easier to read (no export required):

#!/bin/bash
IFS=$'\n'
newName="Fred lives here"
for file in $(find . -name '*TEMPLATE*'); do
    mv ${file} ${file/TEMPLATE/${newName}}
done
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top