12

Is there any way to run a trial recursive search and replace using sed, before actually running it? I just want to print out the results before actually doing the search and replace. Something like echoing the results of,

grep -rl term1 . |xargs sed -i -e 's/term1/term2/'
10

You could run sed without -i and step through output with less

grep -rl --null term1 . | xargs -0 sed -e 's/term1/term2/' | less

Then run sed with -i.bak to create backups which you can diff afterwards

grep -rl --null term1 . | xargs -0 sed -i.bak -e 's/term1/term2/'
diff somefile.bak somefile
# verify changes were correct

Edit: As suggested in comment, use grep --null | xargs -0. This causes filenames to be terminated by the null-byte, which makes it safe for filenames with unusual characters like newline. Yes, \n is a valid character in a unix filename. The only forbidden characters are slash / and the nul character \0

  • Interesting, I tried finding the meaning of -i but it wasn't listed under man sed. Your solution worked. What would be the best way in your opinion to just list the lines, not the entire file? Instead of piping into less I piped | grep term2, which sort of worked but I got some output with strange characters as well. – user251482 Feb 5 '14 at 20:45
  • @user251482 - "Just list the lines", as in "viewing a diff between the old and new file"? – grebneke Feb 5 '14 at 20:48
  • 1
    @StephaneChazelas Yes, or even better grep -rl --null since OP did not specify OS, and -Z means something different on BSD/OSX (decompress). --null is the same on both GNU and BSD – grebneke Feb 5 '14 at 21:01
  • 1
    @grebneke. Good point, but note that the OP is using the GNU sed syntax, so we can assume he's not on BSDs. Note that grep is (a fork of) the GNU grep on FreeBSD and NetBSD. On NetBSD, -Z still means --null (according to the man page), but I agree --null is more portable. Also note that neither --null nor -Z work on OpenBSD – Stéphane Chazelas Feb 5 '14 at 21:57
  • 1
    @grebneke, it's sed -i '' -e s/.../ in BSD sed (no optional options there) – Stéphane Chazelas Feb 5 '14 at 22:52
4

Use find instead of grep

First of all, I would employ find rather than grep, and this for three good reasons:

  1. find allows for more precise file selection. For example, grep -r string *.txt will yield files only in the current directory; not with those in subdirectories.
  2. find comes with the powerful -exec option which eliminates the need for the whole --null … |xargs 0 construct.
  3. The -readable and -writable options of find will prevent wasting time on files that cannot be accessed.

Capture test

That said, grep does lend itself for a first test to see what would be captured:

$ find . -exec grep term1 {} \;

or more specifically:

$ find . -type f -name '*.txt' -readable -writable -exec grep term1 {} \;

Dry run

Now, proceed with a sed dry run. The sed option -n is a synonym for --quiet and the p at the very end of the sed expression will print the current pattern space.

$ find . -exec sed -n 's/term1/term2/gp' {} \;

or more specifically:

$ find . -type f -name '*.txt' -readable -writable -exec sed -n 's/term1/term2/gp' {} \;

Execution

If everything looks fine, issue the definitive command by replacing sed option -n by -i for "in place" and by removing the p at the end.

$ find . -exec sed -i 's/term1/term2/g' {} \;

or more specifically:

$ find . -type f -name '*.txt' -readable -writable -exec sed -i 's/term1/term2/g' {} \;

More find examples

More find examples can be found here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.