2

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

rednectar
  • 101
  • 2
  • 12
  • You don't want `$0` in those expressions. The value of `$0` is the program being run, which for `bash -c` scripts is `"bash"`. What type of system are you running this on? I would look for the `rename` command and take advantage of it. – Mark Reed Nov 05 '13 at 03:56
  • @Mark Reed. I didn't think the _rename_ command could do directories. At least I couldn't get it to work - I did try that. And in spite of *$0* being not correct, it works, so I'll stick with it. – rednectar Nov 07 '13 at 03:54

2 Answers2

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" \;
rici
  • 234,347
  • 28
  • 237
  • 341
  • The export suggestion was good, but that didn't tell me how to do it. (Thanks to cforbish who did though). I did try both options: The first resulted in the new names with embeded quote - like /foo/bar/some "Fred lives here" directory/with files The second option embedded backslash characters \ in the name, so I ended up with names like: /foo/bar/some Fred\ lives\ here directory/with files – rednectar Nov 07 '13 at 03:34
  • @rednectar: yup, sorry. There were extra quotes in both command lines; I removed them. (`export newName` *is* how you export newName; you can do that before, during -- as in cforbish's example -- or after you set its value. Once you've marked a variable as exported, it stays that way for the rest of the session, even if you reassign the value of the variable. Sorry for assuming that was obvious.) – rici Nov 07 '13 at 04:37
  • The corrected first option now works (the one that uses `$0`). The `printf` version doesn't rename anything. – rednectar Nov 08 '13 at 04:10
  • @rednectar: Indeed. Bash quoting is tricky in contexts like this, but I think I finally got it :( – rici Nov 08 '13 at 16:05
  • I got round to testing your modified second attempt (I notice you are not using 'printf' anymore) and it seems to work perfectly. What I don't understand is why I shouldn't use $0 as in @cforbish's answer (which also works just fine). Why is it better to do it your way? But perhaps that's a discussion for another time. – rednectar Nov 11 '13 at 20:15
2

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
cforbish
  • 8,567
  • 3
  • 28
  • 32
  • The export option was the missing link - thanks for that, that one worked. But the for loop didn't work - the variable file did not pick up the whole path/filename, only as far as the first space in the path. [Can someone with enough karma vote this answer up?] – rednectar Nov 07 '13 at 03:46
  • Did the export `find` method work for you? If you modify the `mv` command in the last script to `echo`, what does it say? – cforbish Nov 07 '13 at 04:05
  • If put an `echo` in front of the `mv` I get output like: `mv ./foo/bar/some TEMPLATE directory/with files /foo/bar/some Fred lives here directory/with files` - I guess I'd need quotes around it to get rid of the spaces, or put it through an extra process to escape the spaces. Anyway - the `export newName="Fred lives here"` worked like a charm, so problem solved (in spite of others saying I shouldn't use $0). – rednectar Nov 07 '13 at 04:31