0

I use sed to replace strings in all files.

I have this, but it doens't work:

#!/bin/bash
OLD="oldtext"
NEW="newtext"

grep -rli '$OLD' * | xargs -i@ sed -i 's/$OLD/$NEW/g' @

But this

grep -rli 'oldtext' * | xargs -i@ sed -i 's/oldtext/newtext/g' @

works! How can I fix my command?

Benjamin W.
  • 33,075
  • 16
  • 78
  • 86
J. Dawton
  • 9
  • 1
  • 2
    Variables do not expand under single quotes. Use double quotes, and be careful you don't use some of the regex metacharacters in place of `oldtext` or `newtext`. – randomir Nov 04 '17 at 15:51
  • Tip: [shellcheck](http://shellcheck.net) automatically detects this and other common problems – that other guy Nov 04 '17 at 20:12

1 Answers1

0

As pointed out in randomir's comment and the question he links to, you have to use double quotes so variables are expanded:

old="oldtext"
new="newtext"
grep -rZli "$old" | xargs -0 sed -i "s/$old/$new/gi"

I changed a few other things as well:

  • Lowercase variable names to avoid clashes with names reserved for environment variables
  • grep -Z option to separate filenames with a NUL byte to deal with special characters such as newlines in filenames
  • Skipped * after the sed pattern, as it defaults to the working directory anyway
  • -0 option for xargs to expect NUL byte separated filenames
  • Skipped the (deprecated) -i option as the filenames go to the end of the command anyway
  • i flag for the substitution to match the case insensitive matching in the grep command

Some of these require GNU grep, xargs and sed.

If you don't look at a huge number of files to justify grepping them first, you can just run a single sed process over them all, using a generalized **/* glob:

shopt -s globstar
sed -i "s/$old/$new/gi" **/*

This will complain about sed trying to run on directories, but it doesn't hurt; if all your files have a common extension like .txt, you could use **/*.txt to avoid that.

If, on the other hand, you have tons of files and you want to accelerate the task, you could use the -P option of xargs to parallelize:

grep -rZli "$old" | xargs -0 -P 4 sed -i "s/$old/$new/gi"

for four parallel processes.

Benjamin W.
  • 33,075
  • 16
  • 78
  • 86
  • Hi, thanks for you answer! I use this grep -rZli "$old" | xargs -0 -P 4 sed -i "s/$old/$new/gi" and i have error sed: -e expression #1, char 10: unknown option to `s' keen you help me? – J. Dawton Nov 05 '17 at 12:16
  • @J.Dawton That happens if your `old` or `new` variables contain a slash. You have to choose a different delimiter for the `s` command in that case, like `"s@$old@$new@"` - anything that is neither in `old` or `new` works. – Benjamin W. Nov 05 '17 at 19:46
  • It works! :D very very THX!! – J. Dawton Nov 06 '17 at 21:09