2

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?

Haru Suzuki
  • 142
  • 10

1 Answers1

1

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:
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • `7p is executed patten space will be empty` printing does not empty pattern sapce, it's still there. || Pattern space is _copied_ to hold space with `h` _on 6th line_. There are lines 6,5,4,3,2,1 in hold space. Then we read 7th line. We grab `G` from hold space, so we have `7,6,5,4,3,2,1` lines in pattern space. And they are printed. – KamilCuk Jan 30 '22 at 12:26
  • `7p alone means print 7th line` `p` comamnd always means to print pattern space. On beginning, the current line is inside pattern space. `-n` disables "automatic printing" (without `-n` it's as-if command `p` is always on the end of the script). So `sed -n 7p` causes only 7th line to print, because the line is in pattern space and all other lines are not printed.. – KamilCuk Jan 30 '22 at 12:29
  • Thanks. Means 7p actually means when pattern space contains 7th line of input print the entire pattern space whatever it contains – Haru Suzuki Jan 30 '22 at 12:31
  • Edited. There was a sed visualizer/debugger I saw on reddit, but I can't find. See also https://unix.stackexchange.com/questions/492871/any-way-to-have-a-verbose-mode-or-debug-mode-with-sed – KamilCuk Jan 30 '22 at 12:34