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.
 echo 1234 |   sed '
       /\n/ !G
       s/\(.\)\(.*\n\)/&\2\1/
       //D
       s/.//
     ' 

I'm unable to comprehend the above sed code.

my understanding is:

PATTERN SPACE=1234
first operation /\n/ !G ---> 1234\n(if \n is  not found it is appended at th end)
                s/\(.\)\(.*\n\)/&\2\1/ ----> 1234\n234\n1
                //D ----> \n234\n1(deletes upto newline and commands
                                   from the beginning are applied to the 
                                   existing pattern space)
PATTERN SPACE=\n234\n1
second operation /\n/ !G ---> skipped since  \n234\n1 has newline now
                 s/\(.\)\(.*\n\)/&\2\1/ ----> \n234\n1234\n
                 //D ----> \n234\n1234\n

It seems I'm doing something wrong.

share|improve this question
1  
You can write it a little more legibly and efficiently as sed -e 'G;:1' -e 's/\(.\)\(.*\n\)/\2\1/;t1' -e 's/.//' –  Stéphane Chazelas Oct 31 '13 at 9:48

1 Answer 1

up vote 6 down vote accepted

There is a loop in this code that get's confusing to see. This bit:

   s/\(.\)\(.*\n\)/&\2\1/
   //D

Keeps looping, shifting the characters 234\n1 to 34\n21 until we're left with \n4321, where it then drops out of the loop.

The description by catonmat is spot on so I'm including it here: 37. Reverse a line (emulates "rev" Unix command)..

excerpt

The first line "/\n/ !G" appends a newline to the end of the pattern space if there was none.

The second line "s/\(.\)\(.*\n\)/&\2\1/" is a simple s/// expression which groups the first character as \1 and all the others as \2. Then it replaces the whole matched string with "&\2\1", where "&" is the whole matched text ("\1\2"). For example, if the input string is "1234" then after the s/// expression, it becomes "1234\n234\n1".

The third line is "//D". This statement is the key in this one-liner. An empty pattern // matches the last existing regex, so it's exactly the same as: /\(.\)\(.*\n\)/D. The "D" command deletes from the start of the input till the first newline and then resumes editing with first command in script. It creates a loop. As long as /\(.\)\(.*\n\)/ is satisfied, sed will resume all previous operations. After several loops, the text in the pattern space becomes "\n4321". Then /\(.\)\(.*\n\)/ fails and sed goes to the next command.

The fourth line "s/.//" removes the first character in the pattern space which is the newline char. The contents in pattern space becomes "4321" -- reverse of "1234".

There you have it, a line has been reversed.

share|improve this answer

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.