2

Does anybody know how to grep multiple lines from a text file? I googled many results, but still not fully working.

Text file example:

x
x
x
---
start
a
b
c
d
---
x
x
x

And I want to get only this from the file:

---
start
a
b
c
d
---

It can be shorter (a b) or longer (a b c d e f g), but always starting with:

---
start

and ending with:

---

Thanks a lot

4
  • What operating system are you using? Is this Linux or, if not, can you use GNU grep?
    – terdon
    Feb 11, 2022 at 9:41
  • Do you have other sections that start with --- but that does not continue with a line saying start? Will your sections ever overlap (share --- lines)?
    – Kusalananda
    Feb 11, 2022 at 9:41
  • Yes it's Linux RH8 and some lines have a blank line between --- and start, but the rest is same
    – Nicco
    Feb 11, 2022 at 11:12
  • You should include a - in the "start" block in your example (e.g. change b to b-k) or you're likely to get solutions that'll fail given that input. Also be sure to test cases where there's a start that isn't preceded by --- and where ---\nstart exists but no --- afterwards, etc.
    – Ed Morton
    Feb 13, 2022 at 16:06

5 Answers 5

5

You can use the -z option of grep:

-z, --null-data a data line ends in 0 byte, not newline

$ grep -zo -- '---.start[^-]*---' file
---
start
a
b
c
d
---
0
3

If you are sure that in your text, as shown in the example, there are no "trick" sections where lines --- are always followed by start, you can reduce section heading to ---, and use :

sed -n '/---/,//p' text

If you are not sure about the above statement :

sed -n '/---/{n;/start/{:a H;n;/---/!ba;x;G;s/^/---/p;s/.*/\n---/;D}}' test


sed : /bin/sed executable
-n : sed option to avoid auto line printing
/---/ : Match a pattern of 3 "-"
n: Get the next line of input
/start/: Match a line "start"
:a : Build a label called "a" (For the loop)
H: Happend the line to the HOLD space (Save it)
n: Get the next line
/---/!: Test if the current line **is not** equal to: "---"
ba: Jump to the label 'a' if the test succede
x: Swap the Hold space and the Pattern space.
G: Get the line from the Hold space and append it to the Pattern space
s/^/---/p: Append to the start of the string a sequence of "---" and print the line
s/.*/\n---/: Replace the current line with a new line and an : "---"
D: Delete character in the current line (Pattern space) up to the  first new line character and start the next cycle with the remaining line

awk in paragraph mode :

awk -v h="---" -v h2="start" '                     
    f == 2
    $0 == h {f=1}
    f == 1 && h2 == $0 {print h;print;f++}
' test
2

Based on the answer by @schrodigerscatcuriosity, you can do like

grep -zoP -- '(?s)\n---\nstart\n.*?\n---\n' file

-P for PCRE extension and (?s) fot PCRE_DOTALL

And for the optional blank lines between --- and start you mentioned,

grep -zoP -- '(?s)\n---\n[\n\s]*start\n.*?\n---\n' file
1

With sed sed -n '/^---$/{N;/---\nstart/{:loop;p;n;/^---$/{p;d};b loop};D;}' txtfile If not close the section with --- => also print.... :(

Correct version: sed -n '/^---$/{N;/---\nstart/{:loop;N;/\n---$/{p;d};b loop};D;}' txtfile

Txtfile:

x
x
x
---
---
start
a
b
c
d
---
x
x
x
---
start
a-
b
c
d
--
---
DDD

Output:

start
a
b
c
d
---
---
start
a-
b
c
d
--
---
1

With GNU awk for multi-char RS and RT and assuming ---\n only appears in the input as a record separator (e.g. you can't have something like b---\n mid-record):

$ awk -v RS='---\n' -v ORS= '/^start/ && RT{print RT $0 RT}' file
---
start
a
b
c
d
---

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .