Neil Masson
Neil Masson

Reputation: 2689

How do I use parameter expansion in find?

I can use the for command to change .dat files to .txt files like this

for f in *.dat
do
  mv $f ${f/.dat/.txt}
done

where ${f/.dat/.txt} modifies the .dat in each filename to .txt. But how can I do this with find? An attempt to use {} as a variable fails.

find . -type f -exec echo ${} \;
-bash: ${}: bad substitution 

I can get further by assigning {} to a parameter like this

find . -type f -name '*.dat'  -exec echo `f={}; echo $f` \;

but an attempt at substitution fails

find . -type f -name '*.dat'  -exec echo `f={}; echo $(echo ${f/.dat/.txt})` \;

prints

./file1.dat
./file2.dat

not

./file1.txt
./file2.txt

Does anyone know how to make find behave the way I want it?

Upvotes: 1

Views: 140

Answers (3)

Neil Masson
Neil Masson

Reputation: 2689

This post from @celada about systemd points to the solution. The -exec in find does not invoke a shell, so although some parameter manipulation is supported, parameter expansion like $f{/.dat/.txt} isn't.

So the trick is to exec a shell and then escape the dollar signs like this

find . -type f -name '*.dat'  -exec sh -c "f={}; mv \$f \${f/dat/txt}" \; 

Upvotes: 2

miken32
miken32

Reputation: 42710

You could just skip the exec parameter and echo the output to sed instead:

find . -type f -name '*.dat' | sed 's/.dat/.txt/'

Upvotes: 0

shellter
shellter

Reputation: 37298

You can use awk to modify filename and generate cmds that you pass to bash, i.e.

cat fixer.awk
#!/bin/awk -f
{
        outFile=$0
        sub(/\.txt/, ".dat", outFile) ; print "mv "$0 " " outFile
}

chmod 755 fixer.awk

find . -type f -name '*.dat' | /path/to/fixer.awk

output

mv 34274841/file1.txt 34274841/file1.dat
mv 34274841/file2.txt 34274841/file2.dat

When you're happy with that output, and certain there are no unforeseen consequences ;-) , then execute

 find . -type f -name '*.dat' | /path/to/fixer.awk | bash

IHTH

Upvotes: 1

Related Questions