Query related to working flow of sed: Print command act as range when part of multicommand but act as pattern when used individually

Viewed 30

I was having query regarding a command posted in this question

As I understand the sed flow

sed -n '1!G;h;$p'

Sed flow occurs left to right in loop on every single line. Means every single line in input it will try to execute every single commands specified by semicolon. In above example, --sed reads first line into pattern space ---Now it has three sets of commands it will try to execute each on line ---> It reads first command that is 1!G it will try to execute it on line but since the line currently read is first and negation is supplied it will skip to second command, then it will try to execute third command which is $p but as third command is to print last line it will be skipped for all consecutive lines until the last line.

If I am right about my above understanding, then for below command

sed -n '1!G;h;7p;8p' 

When 8th line is read it should print 7th and 8th line, printing command should not be applied to any other line.

But it was printing 15 lines in reverse order. It was printing 1-8 lines and then again 1-7 lines. Can anyone help clarify.

As per my undetstanding and sed documentation sed operates line by line in left to right order *but above command seems to process entite file again

What was occuring was instead of printing 1to 8 line it was storing 7 lines in patten space printing them then reading 8th linr and printing pattern space again.

Based on above observation sed acts on pattern space for each line read.

Then For below command

sed -n '1!G;h;7p'

Since commands are executed in order, Print command should be executed only when 7th line is read and it should printed but it was printing 1-7 line of patterm space

Hence 7p should literally mean print 7th line But Here it act as range, Means if pattern space has 7th line of input then print the 1-7.

sed '7p' -n ---> Prints 7th line

sed -n '1!G;h;7p'---> Prints 1-7line.

sed -n '1!G;h;1,7p' ---> Prints 1, 1-2, 1-3, 1-4, 1-5, 1-6, 1-7 lines

Can someone clarify why it was occuring?

1 Answers

Means every single line in input it will try to execute every single commands specified by semicolon.

Commands in sed are separated by newlines and semicolons.

is supplied it will skip to second command, then it will try to execute third

Yes, it will skip G, but not h. It will skip the first command. Pattern space will be added to hold space.

1!G   # when not on first line do `G`
h     # do `h`, always
$p    # on last line print

it was printing 1-7 line of patterm space

Sure, holds each line in hold space, and Grabs it each time. So on 7th line there are 6 lines in hold space, they are Grabbed, added to pattern space, then hold and then 7p printed.

Because of how G + h works, they are in reverse order - h puts first line in hold space, then on second line G appends the line in pattern space, so there is first line followed by second line, then h puts it in hold space - so they will be in reverse order.

Events:

> seq 10 | sed -n '1!G;h;3p'

- read first line to pattern space
        - pattern space is `1`
   - `1!G` - ignored for first line
   - `h` - `1` is put into hold space
         - hold space is `1`
   - `3p` - ignored, not third line

- read second line to pattern space
        - pattern space is `2`
   - `1!G` - `G` grabs hold space to pattern space with a newline
        - pattern space is `2\n1`
   - `h`
        - hold space is `2\n1`
   - `3p` - ignored, not third line

- read third line to pattern space
        - pattern space is `3`
   - `1!G` - `G` grabs hold space to pattern space with a newline
        - pattern space is `3\n2\n1`
   - `h`
        - hold space is `3\n2\n1`
   - `3p` - third line, so we print pattern space with a newline
        - print `3\n2\n1\n` - so you see the output in reverse

why it was occuring?

Because G and h commands are executing.


--debug looks nice:

$ printf "%s\n" one two three four | sed --debug -n '1!G;h;3p'
SED PROGRAM:
  1! G
  h
  3 p
INPUT:   'STDIN' line 1
PATTERN: one
COMMAND: 1! G
COMMAND: h
HOLD:    one
COMMAND: 3 p
END-OF-CYCLE:
INPUT:   'STDIN' line 2
PATTERN: two
COMMAND: 1! G
PATTERN: two\none
COMMAND: h
HOLD:    two\none
COMMAND: 3 p
END-OF-CYCLE:
INPUT:   'STDIN' line 3
PATTERN: three
COMMAND: 1! G
PATTERN: three\ntwo\none
COMMAND: h
HOLD:    three\ntwo\none
COMMAND: 3 p
three
two
one
END-OF-CYCLE:
INPUT:   'STDIN' line 4
PATTERN: four
COMMAND: 1! G
PATTERN: four\nthree\ntwo\none
COMMAND: h
HOLD:    four\nthree\ntwo\none
COMMAND: 3 p
END-OF-CYCLE:
Related