chepner's helpful answer proposes the simpler and more efficient use of find
's -exec
action instead of piping to xargs
.
Unless special xargs
features are needed, this change is always worth making, and maps to xargs
features as follows:
find ... -exec ... {} \;
is equivalent to find ... -print0 | xargs -0 -n 1 ...
find ... -exec ... {} +
is equivalent to find ... -print0 | xargs -0 ...
In other words:
the \;
terminator invokes the target command once for each matching file/folder.
the +
terminator invokes the target command once overall, supplying all matching file/folder paths as a single list of arguments.
- Multiple calls happen only if the resulting command line becomes too long, which is rare, especially on Linux, where
getconf ARG_MAX
, the max. command-line length, is large.
Troubleshooting the OP's command:
Since the OP's xargs
command passes all matching file paths at once - and per xargs
defaults at the end of the command line, the resulting command will effectively look something like this:
sed -i 's/previousword/newword/g' /myprojects/file1.cpp /myprojects/file2.cpp ...
This can easily be verified by prepending echo
to sed
- though (conceptual) quoting of arguments that need it (paths with, e.g., embedded spaces) will not show (note the echo
):
find /myprojects -type f -name '*.cpp' -print0 |
xargs -0 echo sed -i 's/previousword/newword/g'
Next, after running the actual command, check whether the last-modified date of the files has changed using stat
:
- If they have, yet the contents haven't changed, the implication is that
sed
has processed the files, but the regex in the s
function call didn't match anything.
It is conceivable that older GNU sed
versions don't work properly when combining -i
(in-place editing) with multiple file operands (though I couldn't find anything in the GNU sed
release notes).
To rule that out, invoke sed
once for each file:
If you still want to use xargs
, add -n 1
:
find /myprojects -type f -name '*.cpp' -print0 |
xargs -0 -n 1 sed -i 's/previousword/newword/g'
To use find
's -exec
action, see chepner's answer.
With a GNU sed
version that does support updating of multiple files with the -i
option - which is the case as of at least v4.2.2 - the best formulation of your command is (note the quoted *.cpp
argument to prevent premature expansion by the shell, and the use of terminator +
to only invoke sed
once):
find /myprojects -type f -name '*.cpp' -exec sed -i 's/previousword/newword/g' '{}' +