4

I found this question and it solved 99% of my issue but I can't figure out to match the end pattern with ^. sed - Print lines matched by a pattern range if one line matches a condition

Here are my data, it requires me matching the closing bracket at beginning of the line.


(
Device=B
Data=asdfasdf(qwfw)
Lorem=Ipsum
)
(
Device=A
Data=asdfasdf(aewf)
Lorem=Ipsum
)
(
Device=B
Data=asdfasdf(wef)
)
(
Device=A
Data=asdfasdf(jalks)
)
(
Device=B
Data=asdfasdf(asf)
)
(
Device=C
Data=asdfasdf(asfew)
Lorem=Ipsum
)

I'm trying this command, but it fails to match anything.

sed -n '/(/{:a;N;/^)/!ba;/Device=A/p}' file

If I remove ^, it returns this

-> % sed -n '/(/{:a;N;/)/!ba;/Device=A/p}'  test 
(
Device=A
Data=asdfasdf(aewf)
(
Device=A
Data=asdfasdf(jalks)

Does anyone know what am I missing here?

sed version

sed (GNU sed) 4.5
Copyright (C) 2018 Free Software Foundation, Inc.
  • 1
    sed is not a good tool for this. Grep is the regex tool meant for pattern matching and Awk is the tool for pattern matching with complex logic. For a multi-line match like this it is probably MUCH easier to do it using awk. – davolfman Feb 15 '23 at 23:54

4 Answers4

3

When you are accumulating lines in the pattern space you can no longer use ^ to match the beginning of subsequent lines and have to use \n (embedded newline) instead:

sed -n '/(/{:a;N;/\n)/!ba; /Device=A/p}' infile

although I very much prefer Costas' solution over there (working with the hold buffer is usually easier/better):

sed 'H;/^(/h;/^)/!d;x;/Device=A/!d' infile

In this case the ^ anchor is needed for both patterns.

don_crissti
  • 82,805
0

When trying to do this with sed you're using arcane constructs that became obsolete literally almost half a century ago when awk was invented. Using GNU awk for multi-char RS and RT:

$ awk -v RS='\n)\n' '/Device=A/{ORS=RT; print}' file
(
Device=A
Data=asdfasdf(aewf)
Lorem=Ipsum
)
(
Device=A
Data=asdfasdf(jalks)
)

or using any awk:

$ awk '{r=r $0 ORS} /^)/{if (r ~ /Device=A/) printf "%s", r; r=""}' file
(
Device=A
Data=asdfasdf(aewf)
Lorem=Ipsum
)
(
Device=A
Data=asdfasdf(jalks)
)
Ed Morton
  • 31,617
0

Using Raku (formerly known as Perl_6

~$ raku -e '.put for slurp.comb( /^^ \( \n "Device=A" \n [\N+ \n]+? \) $$ / );'  file

#OR

~$ raku -e '.put for slurp.comb: /^^ ( \n "Device=A" \n [\N+ \n]+? ) $$ / ;' file

Briefly, Raku has a function comb which is quite the opposite of split. You can give comb a Regex-matcher and it will pull out matching elements.

The code above slurps the file in (reads-in all lines at once), without auto-chomping each line. To pull out Device=A elements, simply list the matching lines you desire (including \n newlines, which are still present).

Note: the grouping [\N+ \n]+? is a non-greedy sequence of one-or-more \N non-newline characters followed by a \n newline, this [...] grouped-pattern appearing + one-or more times, ? non-greedily.

Sample Output:

(
Device=A
Data=asdfasdf(aewf)
Lorem=Ipsum
)
(
Device=A
Data=asdfasdf(jalks)
)

https://raku.org

jubilatious1
  • 3,195
  • 8
  • 17
0

Here is a sed solution:

$ sed -n '/=A/,/^)/p' infile | sed '/=A/i ('

( Device=A Data=asdfasdf(aewf) Lorem=Ipsum ) ( Device=A Data=asdfasdf(jalks) )

dhm
  • 361