Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I've noticed that, if I add \n to a patter for a substitute using sed, it does not match. Example:

$ cat > alpha.txt
This is
a test
Please do not
be alarmed

$ sed -i'.original' 's/a test\nPlease do not/not a test\nBe/' alpha.txt

$ diff alpha.txt{,.original}

$ # No differences printed out

How can I get this to work?

share|improve this question
add comment

4 Answers 4

up vote 16 down vote accepted

In the simplest calling of sed, it has one line of text in the pattern space, ie. 1 line of \n delimited text from the input. The single line in the pattern space has no \n... That's why your regex is not finding anything.

You can read multiple lines into the pattern-space and manipulate things surprisingly well, but with a more than normal effort.. Sed has a set of commands which allow this type of thing... Here is a link to a Command Summary for sed. It is the best one I've found, and got me rolling.

However forget the "one-liner" idea once you start using sed's micro-commands. It is useful to lay it out like a structured program until you get the feel of it... It is surprisingly simple, and equally unusual. You could think of it as the "assembler language" of text editing.

Summary: Use sed for simple things, and maybe a bit more, but in general, when it gets beyond working with a single line, most people prefer something else...
I'll let someone else suggest something else.. I'm really not sure what the best choice would be (I'd use sed, but that's because I don't know perl well enough.)


sed '/^a test$/{
       $!{ N        # append the next line when not on the last line
         s/^a test\nPlease do not$/not a test\nBe/
                    # now test for a successful substitution, otherwise
                    #+  unpaired "a test" lines would be mis-handled
         t sub-yes  # branch_on_substitute (goto label :sub-yes)
         :sub-not   # a label (not essential; here to self document)
                    # if no substituion, print only the first line
         P          # pattern_first_line_print
         D          # pattern_ltrunc(line+nl)_top/cycle
         :sub-yes   # a label (the goto target of the 't' branch)
                    # fall through to final auto-pattern_print (2 lines)
       }    
     }' alpha.txt  

Here it is the same script, condensed into what is obviously harder to read and work with, but some would dubiously call a one-liner

sed '/^a test$/{$!{N;s/^a test\nPlease do not$/not a test\nBe/;ty;P;D;:y}}' alpha.txt

Here is my command "cheat-sheet"

:  # label
=  # line_number
a  # append_text_to_stdout_after_flush
b  # branch_unconditional             
c  # range_change                     
d  # pattern_delete_top/cycle          
D  # pattern_ltrunc(line+nl)_top/cycle 
g  # pattern=hold                      
G  # pattern+=nl+hold                  
h  # hold=pattern                      
H  # hold+=nl+pattern                  
i  # insert_text_to_stdout_now         
l  # pattern_list                       
n  # pattern_flush=nextline_continue   
N  # pattern+=nl+nextline              
p  # pattern_print                     
P  # pattern_first_line_print          
q  # flush_quit                        
r  # append_file_to_stdout_after_flush 
s  # substitute                                          
t  # branch_on_substitute              
w  # append_pattern_to_file_now         
x  # swap_pattern_and_hold             
y  # transform_chars                   
share|improve this answer
    
Shoot me now. Worst syntax ever! –  Gili May 20 at 19:51
1  
This is a fantastic explanation, but I'm inclined to agree with @Gili. –  gatoatigrado May 21 at 20:44
    
Your cheat-sheet has it all. –  konsolebox Jul 19 at 12:30
add comment

Use perl instead of sed:

$ perl -0777 -i.original -pe 's/a test\nPlease do not/not a test\nBe/igs' alpha.txt
$ diff alpha.txt{,.original}
2,3c2,3
< not a test
< Be
---
> a test
> Please do not

-pie is your standard "replace in place" command-line sequence, and -0777 causes perl to slurp files whole. See perldoc perlrun to find out more about it.

share|improve this answer
    
Thanks! For multiline work, perl wins hands down! I ended up using ` $ perl -pi -e 's/bar/baz/' fileA` to change the file in-place. –  Nicholas Tolley Cottrell Feb 4 '13 at 14:36
add comment

sed has three commands to manage multi-line operations: N, D and P (compare them to normal n, d and p).

In this case, you can match the first line of your pattern, use N to append the second line to hold space and then use s to do your substitution.

Something like:

/a test$/{
  N
  s/a test\nPlease do not/not a test\nBe/
}
share|improve this answer
add comment

You can but it's difficult. I recommend switching to a different tool. If there's a regular expression that never matches any part of the text you want to replace, you can use it as an awk record separator in GNU awk.

awk -v RS='a' '{gsub(/hello/, "world"); print}'

If you there are never two consecutive newlines in your search string, you canuse awk's “paragraph mode” (one or more blank lines separate records).

awk -v RS='' '{gsub(/hello/, "world"); print}'

An easy solution is to use Perl and load the file fully into memory.

perl -0777 -e 's/hello/world/g'
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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