4

I've this sed & find command

find /home/www/ -name footer.php -exec sed -i 's/<\/body>/testmoe\n<\/body>/'

what I need to replace testmoe with data in file.txt so its gona be ,

find /home/www/ -name footer.php -exec sed -i 's/<\/body>/file.txt\n<\/body>/'

any suggestion to correct my command

3
  • Your question isn't clear. Do you want to add testmo\n before </body> and then at some later date change testmo to file.txt?
    – user9517
    May 7, 2011 at 8:24
  • i want to add text from file.txt before </body>
    – user47556
    May 7, 2011 at 8:39
  • file.txt include like 4 lines
    – user47556
    May 7, 2011 at 8:39

3 Answers 3

11

To improve a little on @Iain's answer, I think you can do it in just one pass like this:

#!/bin/sh
sed -i '/<\/body/ {
    r /path/to/file
    a </body>
    d
}' $1

This searches for </body>, inserts the file into the buffer, appends </body> to the buffer, and then deletes the original </body>, which as Iain correctly pointed out can't be moved.

3
  • The sed-fu is stronger in this one!
    – user9517
    May 7, 2011 at 23:13
  • 1
    Haha! Definitely not, I only came to this after reading your answer. May 7, 2011 at 23:15
  • I've been scouring the toobs for this info, ++ Aug 4, 2011 at 4:14
4

With sed you have to make multiple passes as the /RE/r <filename> command inserts the contents of <filename> into the output after the /RE/. The easiest way to do this with sed is to

  • Insert 'marker text' before the /RE/ in pass one
  • Insert the contents of the file after the 'marker text' in pass two
  • Delete the 'marker text' in pass three

This is most easily achieved with a script which you can call from find

#!/bin/bash

sed -i '/<\/body>/i xyzzy' $1
sed -i '/xyzzy/r /path/to/file.txt' $1
sed -i  '/xyzzy/d' $1

then

find /home/www -name footer.php -exec /path/to/script {} \;
2
  • 1
    Ooooh, cunning! The sed-fu is strong in this one.
    – MadHatter
    May 7, 2011 at 21:59
  • 1
    This is quite nice. I think you can do it without the marker text though, I posted that variation as a separate answer. May 7, 2011 at 23:05
1

I know it's a bit like the old West Country joke ("Do you know how to get to Yeovil?" "If I wanted to get to Yeovil, I wouldn't start from here, sir.") but I wouldn't use sed.

Try

perl -ne '$temp = $_ ; if ( $_ =~ /<\/body>/ ) { open FD2, "file.txt" ; while (<FD2>) { print  $_ ; } } ; print $temp;' < foo > bar

perl one-liners can be a bit, well, cryptic, so here's a breakdown of what's happening. The perl script:

  1. perl -n loops through STDIN line-by-line; for each line -e invokes the script in single-quotes, in which:
  2. $temp = $_ stores the line in a temporary buffer
  3. if ( $_ =~ /<\/body>/ ) looks for a line containing </body>
  4. open FD2, "file.txt" ; while (<FD2>) { print $_ ; }: when </body> has been found, opens a file descriptor to file.txt and in a subloop reads through each line in that file and writes it to STDOUT; the re-use of $_ in this loop is why we had to temporarily store it in step 2
  5. print $temp; writes the line originally read to STDOUT regardless of whether a match was found in step 3

The practical upshot is that each line of STDIN is written to STDOUT, but if any line happens to contain </body>, the entire contents of file.txt are inserted before that line is written out.

Is that what you wanted? If so, wrapping it in find, redirecting STDIN and STDOUT, and renaming STDOUT on top of STDIN after execution, is left as an exercise for you. You should also note that if you happen to have more than one occurrence of </body> in your file, you'll get the contents of file.txt intercalated each time it occurs.

1
  • 2
    +1 for creative excuse to use perl oneliner.
    – Caleb
    May 7, 2011 at 13:18

You must log in to answer this question.

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