20

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/'
1
  • Make a copy of the data, run your command, inspect the result.
    – Kusalananda
    May 31, 2019 at 14:27

2 Answers 2

15

Use sed with 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 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.

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

7
  • 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, 2014 at 20:45
  • @user251482 - "Just list the lines", as in "viewing a diff between the old and new file"?
    – grebneke
    Feb 5, 2014 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, 2014 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 Feb 5, 2014 at 21:57
  • 1
    @grebneke, it's sed -i '' -e s/.../ in BSD sed (no optional options there) Feb 5, 2014 at 22:52

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .