I am using the following to search a directory recursively for specific string and replace it with another:
grep -rl oldstr path | xargs sed -i 's/oldstr/newstr/g'
This works okay. The only problem is that if the string doesn't exist then sed
fails because it doesn't get any arguments. This is a problem for me since i'm running this automatically with ANT and the build fails since sed
fails.
Is there a way to make it fail-proof in case the string is not found?
I'm interested in a one line simple solution I can use (not necessarily with grep
or sed
but with common unix commands like these).
You can use find
and -exec
directly into sed
rather than first locating oldstr
with grep
. It's maybe a bit less efficient, but that might not be important. This way, the sed
replacement is executed over all files listed by find
, but if oldstr
isn't there it obviously won't operate on it.
find /path -type f -exec sed -i 's/oldstr/newstr/g' {} \;
Your solution is ok. only try it in this way:
files=$(grep -rl oldstr path) && echo $files | xargs sed....
so execute the xargs
only when grep return 0
, e.g. when found the string in some files.
Standard xargs
has no good way to do it; you're better off using find -exec
as someone else suggested, or wrap the sed
in a script which does nothing if there are no arguments. GNU xargs
has the --no-run-if-empty
option, and BSD / OS X xargs
has the -L
option which looks like it should do something similar.
I have taken Vlad's idea and changed it a little bit. Instead of
grep -rl oldstr path | xargs sed -i 's/oldstr/newstr/g' /dev/null
Which yields
sed: couldn't edit /dev/null: not a regular file
I'm doing in 3 different connections to the remote server
touch deleteme
grep -rl oldstr path | xargs sed -i 's/oldstr/newstr/g' ./deleteme
rm deleteme
Although this is less elegant and requires 2 more connections to the server (maybe there's a way to do it all in one line) it does the job efficiently as well
I think that without using -exec
you can simply provide /dev/null
as at least one argument in case nothing is found:
grep -rl oldstr path | xargs sed -i 's/oldstr/newstr/g' /dev/null
My use case was I wanted to replace
foo:/Drive_Letter
with foo:/bar/baz/xyz
In my case I was able to do it with the following code.
I was in the same directory location where there were bulk of files.
find . -name "*.library" -print0 | xargs -0 sed -i '' -e 's/foo:\/Drive_Letter:/foo:\/bar\/baz\/xyz/g'
hope that helped.
If you are to replace a fixed string or some pattern, I would also like to add the bash builtin pattern string replacement variable substitution construct. Instead of describing it myself, I am quoting the section from the bash manual:
${parameter/pattern/string}
The pattern is expanded to produce a pattern just as in pathname
expansion. parameter is expanded and the longest match of pattern against its value is replaced with string. If pattern
begins with /
, all matches of pattern are replaced with string.
Normally only the first match is replaced. If pattern begins
with #
, it must match at the beginning of the expanded value of
parameter. If pattern begins with %
, it must match at the end
of the expanded value of parameter. If string is null, matches
of pattern are deleted and the /
following pattern may be omitted. If parameter is @
or *
, the substitution operation is
applied to each positional parameter in turn, and the expansion
is the resultant list. If parameter is an array variable subscripted with @
or *
, the substitution operation is applied to
each member of the array in turn, and the expansion is the
resultant list.