You're viewing a comment by Bård and its responses.

Bård Permalink
December 19, 2013, 11:40

The loop is achieved by the //D.
We can write the expression a bit clearer (IMHO) like this:

echo "1234" |sed -r '/\n/! G; s|(.)(.*\n)|&\2\1|; /(.)(.*\n)/D; s|\n||'

I like to use the | for separating the different parts of a substitution command (to avoid the picket fence). The -r options (which might not be portable across sed versions, I think) removes the need for escaping grouping parentheses. I also added a space between commands and replaced the //D with /(.)(.*\n)/D to make things even more clear. The // just repeats the last used regular expression. And finally replaced the . in the last replace expression with \n just to make it very clear that the character we expect to find and replace is indeed a newline.
And now to the point:
The key here is the /(.)(.*\n)/D expression. As long as we are able to find any character followed by a newline, the D command will be run and the D command does the following (quoted from the sed manual)

If pattern space contains no newline, start a normal new cycle as if the d command was issued. Otherwise, delete text in the pattern space up to the first newline, and restart cycle with the resultant pattern space, without reading a new line of input.

Every time (.)(.*\n) matches we indeed do have a newline in the pattern space, so we "delete text in the pattern space up to the first newline, and restart cycle with the resultant pattern space, without reading a new line of input". Which makes this a loop. This loop will go on until we do not get a match with (.)(.*\n), which will happen eventually (when the content of the pattern space starts with a newline). We then move on to removing the leading newline with s|\n|| and we're done.

To look at the resulting pattern space after each iteration we can use the l command in sed, which (again from the sed manual):

Print the pattern space in an unambiguous form: non-printable characters (and the \ character) are printed in C-style escaped form; long lines are split, with a trailing \ character to indicate the split; the end of each line is marked with a $.

Like so:

echo "1234" |sed -r '/\n/! G; s|(.)(.*\n)|&\2\1|; l; /(.)(.*\n)/D; s|\n||'

We print the content of the pattern space after each substitution in the loop, so we see what the /(.)(.*\n)/D receives as input.


The first 5 lines here are the output from the 5 invocations of the l in the loop and the last line is what is final reversed string printed then the whole sed expression is finished.
Now we see pretty clearly what is going on in the loop and how and when the loop is terminated.

And I think we all can agree on that the person who wrote this one-liner initially is a very intelligent and knows his sed commands. :-)

Reply To This Comment

(why do I need your e-mail?)

(Your twitter handle, if you have one.)

Type the word "cdrom_95": (just to make sure you're a human)

Please preview the comment before submitting to make sure it's OK.