0

I would like to rename several files picked by find in some directory, then use xargs and mv to rename the files, with parameter expansion. However, it did not work...

example:

mkdir test
touch abc.txt
touch def.txt

find . -type f -print0 | \
xargs -I {} -n 1 -0 mv {} "${{}/.txt/.tx}"

Result:

bad substitution
[1]    134 broken pipe  find . -type f -print0

Working Solution:

for i in ./*.txt ; do mv "$i" "${i/.txt/.tx}" ; done

Although I finally got a way to fix the problem, I still want to know why the first find + xargs way doesn't work, since I don't think the second way is very general for similar tasks.

Thanks!

Oliver
  • 147
  • 9

1 Answers1

1

Remember that shell variable substitution happens before your command runs. So when you run:

find . -type f -print0 | \
xargs -I {} -n 1 -0 mv {} "${{}/.txt/.tx}"

The shell tries to expan that ${...} construct before xargs even runs...and since that contents of that expression aren't a valid shell variable reference, you get an error. A better solution would be to use the rename command:

find . -type f -print0 | \
xargs -I {} -0 rename .txt .tx {}

And since rename can operate on multiple files, you can simplify that to:

find . -type f -print0 | \
xargs -0 rename .txt .tx
larsks
  • 277,717
  • 41
  • 399
  • 399
  • 'shell variable substitution happens before your command runs', that's a golden word, thx a lot! I am not familiar with `rename`, so it leads to another question: if I want to substitute some middle part of file name, not only prefix or suffix, how could I do that with `rename`? – Oliver Nov 12 '18 at 19:26
  • Rename actually operates on the entire filename, so for example `rename foo bar *` would rename `this_foo_example` to `this_bar_example`. Take a look through the `rename` man page and experiment a bit. If it doesn't work the way you want, consider posting a new question. – larsks Nov 12 '18 at 20:20