find doesn't magically expand all {}
instances after it starts, just the places it sees them in its own command line. If you want to use bash redirection within your command, you need to use it within a bash shell command like so:
find . -name "*.php" -exec bash -c 'expand -t 4 {} > {}' \;
The way you wrote it, the output of find . -name "*.php" -exec expand -t 4 {}
is sent to the file {}
.
And with the second example you gave, the find command prints a list of files, which are then given to the xargs is completely unassociated with the find command. xargs
works by running a command once for every item input: find /tmp -name core -type f -print0 | xargs -0 /bin/rm -f
is a good example of how it should be used. Finally, the last section of that command pipeline outputs the results to the file {}
.
The thing to realize is that every element of a command pipeline is completely separate from all others. If you want to use a string repeatedly in a pipeline you should place it in a shell variable to store it. For instance, you could use the bash for loop as follows (the extra complexity is to properly handle files with spaces in their names, like find -0
does automatically):
#!/bin/bash
shopt -s globstar
for f in **; do
if ! echo $f | grep -i -q .php ; then
continue
fi
expand -t 4 $f > ${f}.ex
mv ${f}.ex $f
done
You should also use temporary files rather than in-place editing. And you might want to do this in two steps (i.e., not do the mv
initially) in case you made a mistake. It'd be unfortunate to accidentally destroy all your php files.
Lastly, you can use the xargs -I stringtoreplace
option. If you use -i
with no argument, it's the same as -I {}
, but harder to read. In this case your second method would be
find . -name "*.php" -print0 | xargs -0 -I [file] expand -t 4 [file] > [file]
You may want to use -iname
instead of -name
or grep -i
instead of grep
, in case you have .PHP
files.