I am trying to rename a list of files (from $old
to $new
), all present in $homedir
or in subdirectories in $homedir
.
In the command line this line works to rename files in the subfolders:
find ${homedir}/ -name ${old} -exec rename "s/${old}/${new}/" */${old} ';'
However, when I want to implement this line in a simple bash script getting the $old
and $new
filenames from input.txt
, it doesn't work anymore...
input.txt
looks like this:
name_old name_new
name_old2 name_new2
etc...
the script looks like this:
#!/bin/bash
homedir='/path/to/dir'
cat input.txt | while read old new;
do
echo 'replacing' ${old} 'by' ${new}
find ${homedir}/ -name ${old} -exec rename "s/${old}/${new}/" */${old} ';'
done
After running the script, the text line from echo with $old
and $new
filenames being replaced is printed for the entire loop, but no files are renamed. No error is printed either. What am I missing? Your help would be greatly appreaciated!
I checked whether the $old
and $new
variables were correctly passed to the find -exec rename
command, but because they are printed by echo
that doesn't seem to be the issue.
CodePudding user response:
If you add an echo
, like -exec echo rename ...
, you'll see what actually gets executed. I'd say that both the path to $old
is wrong (you're not using the result of find
in the -exec
clause), and */$old
isn't quoted and might be expanded by the shell before find
ever gets to see it.
You're also having most other expansions unquoted, which can lead to all sorts of trouble.
You could do it in pure Bash (drop echo
when output looks good):
shopt -s globstar
for f in **/"$old"; do echo mv "$f" "${f/%*/$new}"; done
Or with rename
directly, though this would run into trouble if too many files match (drop -n
when output looks good):
rename -n "s/$old\$/$new/" **/"$old"
Or with GNU find, using -execdir
to run in the same directory as the matching file (drop echo
when output looks good):
find -type f -name "$old" -execdir echo mv "$old" "$new" \;
And finally, a version with find
that spawns just a single subshell (drop echo
when output looks right):
find -type f -name "$old" -exec bash -c '
new=$1
shift
for f; do
echo mv "$f" "${f/%*/$new}"
done
' bash "$new" {}
CodePudding user response:
The argument to rename
should be the file itself, not */${old}
. You also have a number of quoting errors, and a useless cat
).
#!/bin/bash
while read -r old new;
do
echo "replacing ${old} by ${new}" >&2
find /path/to/dir -name "$old" -exec rename "s/${old}/${new}/" {} ';'
done <input.txt
Running find
multiple times on the same directory is hugely inefficient, though. Probably a better solution is to find all files in one go, and abort if it's not one of the files on the list.
find /path/to/dir -type f -exec sh -c '
for f in "$@"; do
awk -v f="$f" "f==\$1 { print \"s/\" \$1 \"/\" \$2 \"/\" }" "$0" |
xargs -I {} -r rename {} "$f"
done' input.txt {}
(Untested; probably try with echo
before you run this live.)