Take the 2-minute tour ×
Ask Ubuntu is a question and answer site for Ubuntu users and developers. It's 100% free, no registration required.
$ (echo hello; echo there) | sed ':a;$!N;s/\n/string/;ta'
hellostringthere

Above sed command replaces new line character with the string "string". But I don't know the meaning of :a;$!N;s/\n/string/;ta within the single quotes. I know the middle part s/\n/string/. But I don't know the function of first (:a;$!N;) and last (ta) part.

share|improve this question
2  
please look at stackoverflow.com/questions/1251999/…. –  xiaodongjie May 5 at 16:05
    
What about the last part? –  Avinash Raj May 5 at 16:15
    
The "t" command branches to a named label if the last substitute command modified pattern space. –  xiaodongjie May 5 at 16:18

2 Answers 2

up vote 5 down vote accepted

These are the, admittedly cryptic, sed commands. Specifically (from man sed):

: label
         Label for b and t commands.

t label
         If a s/// has done a successful substitution since the last input line was read and since the last t or T command, then branch to label; if label is omitted, branch to end of script.

n N         Read/append the next line of input into the pattern space.

So, the script you posted can be broken down into (spaces added for readbility):

sed ':a;  $!N;  s/\n/string/;  ta'
     ---  ----  -------------  --
      |     |        |          |--> go back (`t`) to `a`
      |     |        |-------------> substitute newlines with `string`
      |     |----------------------> If this is not the last line (`$!`), append the 
      |                              next line to the pattern space.
      |----------------------------> Create the label `a`.

Basically, what this is doing could be written in pseudocode as

while (not end of line){
    append current line to this one and replace \n with 'string'
}

You can understand this a bit better with a more complex input example:

$ printf "line1\nline2\nline3\nline4\nline5\n" | sed ':a;$!N;s/\n/string/;ta'
line1stringline2stringline3stringline4stringline5

I am not really sure why the !$ is needed. As far as I can tell, you can get the same output with

printf "line1\nline2\nline3\nline4\nline5\n" | sed ':a;N;s/\n/string/;ta'
share|improve this answer
    
The !$ is to don't match the last newline, IMO. –  Braiam May 5 at 16:09
    
@Braiam not too sure about that, it's $! not !$. However, it might also be !N and not $!. –  terdon May 5 at 16:19
    
I was trying to parse the texinfo page but didn't found references to neither !N or $!. So, I still keep my thinking that is looking if the last line is newline or EOF. –  Braiam May 5 at 16:23
2  
I try to think of $! as an address 'range' with a postfix complement operator - so $!N (do N everywhere except for address $) is really the same syntax as something like m,n!d (delete everything except lines m to n). –  steeldriver May 5 at 17:31
    
@steeldriver ah, I see, thanks. –  terdon May 5 at 18:45

There's no "man" reference for these commands (well, actually just a "reminder") so you should consult the info pages, specifically "3.7 Commands for 'sed' gurus" section. There's also a warning that you should instead consider awk or Perl up to this point, and they are rightly so:

  • : LABEL [No addresses allowed.]

    Specify the location of LABEL for branch commands. In all other respects, a no-op.

    in this specific example, it just create a label, is no operative. The a can be changed to whatever you like as long as it's the same as t label.

  • ; Commands within a SCRIPT or SCRIPT-FILE can be separated by semicolons (`;') or newlines (ASCII 10).

    In this case, is to separate different commands, they can be easily replaced by new lines.

  • $!N Check if it's not the last line.

  • s/\n/string/ your regex expression. There's nothing important here.
  • t LABEL Branch to LABEL only if there has been a successful substitution since the last input line was read or conditional branch was taken. The LABEL may be omitted, in which case the next cycle is started.

    This verifies where the command substitution (the regex expresion) took place, and executes whatever is in the label, in this case the label is a.

If we have to explain the entire command with plain english it would be equal to:

Create and store a check for the last line in label a, substitute all "new lines" for string and stop when the check for label a is true.

share|improve this answer
    
Steeldriver clarified in a comment to my A. The $!N means do N on all lines except the last one. –  terdon May 5 at 18:46

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.