0

I need to:

Print text between "hi" and "hello" patterns.

Patterns may be repeated and there may be multiple occurrences of hi..hello.

Source file:

hi aa bb cc
dd ee ff
hello xxxxxx
aaaa dddd cccc hi aaa bbb
ccc hello cccc fff

Expected output.

hi aa bb cc
dd ee ff
hello
hi aaa bbb
ccc hello

I tried with awk and sed -n commands whereas it displays all the line between the first occurrence of hi and the last occurrence of hello.

2
  • You need to post bigger example of input file and expected output including multi-blocks.
    – jimmij
    Oct 9, 2018 at 7:01
  • 1
    Also show how a file with multiple hi/hello (on the same line and on consecutive lines) should be treated.
    – Kusalananda
    Oct 9, 2018 at 7:04

3 Answers 3

2

From the first hi to the first hello that follows.

grep

Using (GNU) grep and tr:

$ <infile grep -oPz "(?s)hi.*?hello" | tr '\0' '\n'
hi aa bb cc
dd ee ff
hello
hi aaa bbb
ccc hello

Descripttion:

  • <infile Source file.
  • grep -oPz Call grep to:
    • (-P) match a PCRE (Perl Compatible Regular Expression)
    • (-o) only print the matching part.
    • (-z) use a zero byte (a.k.a. NUL and a.k.a. \0) as line delimiter.
  • "(?s) Make the PCRE dot (.) match also newlines.
  • hi Starting with the string hi.
  • .*? Match all characters that follow (non-greedy because of ?).
  • hello" Up until the string hello is matched.
  • | tr '\0' '\n' Convert the NULs (\0) bytes (from grep -z) to newlines.

sed

GNU sed:

<infile sed 's/hi/\n&/;s/[^\n]*\n//;s/\(hello\).*/\1/;/hi/,/hello/!d'

Or, for BSD sed, which doesn't allow \n on the right side of s///, you need to define a newline variable nl:

$ eval "$(printf "nl='\n'")"

And, then:

<infile sed 's/hi/\'"$nl"'&/;s/[^\n]*\n//;s/\(hello\).*/\1/;/hi/,/hello/!d'

Or; if you could write an explicit newline:

<infile sed 's/hi/\
&/;s/[^\n]*\n//;s/\(hello\).*/\1/;/hi/,/hello/!d'
7
  • Thanks for your time and suggestion, i tried with below command and it worked, but its adding letter n before hi, also its taking the complete line of the start pattern, below is the o/p i got
    – user314870
    Oct 9, 2018 at 11:20
  • nhi aa bb cc dd ee ff hello aaaa dddd cccc nhi aaa bbb ccc hello
    – user314870
    Oct 9, 2018 at 11:20
  • command i tried infile sed 's/hi/\n&/;s/[^\n]*\n//;s/(hello).*/\1/;/hi/,/hello/!d'
    – user314870
    Oct 9, 2018 at 11:21
  • Then, you have a BSD sed, which doesn't allow a \n on the Right side of s///. Use the BSD version(s). @user314870
    – user232326
    Oct 9, 2018 at 20:30
  • You don't need the tr part. Grep isn't replacing \n with \0 because of -z (try it!), it's just looking for lines separated by \0 instead of \n, which in most cases allows to look at the whole file as a single line. The \n characters remain untouched, though. Oct 10, 2018 at 21:51
0
grep -oPz "(?s)hi.*?hello" fileName

Explanation below.

cat tmp

Output ->

hi aa bb cc
dd ee ff
hello xxxxxx
aaaa dddd cccc hi aaa bbb
ccc hello cccc fff
00000000000
hi ff 
djd h
sdkf hello
dfj 

Solution:

grep -oPz "(?s)hi.*?hello" tmp

Output ->

hi aa bb cc
dd ee ff
hello
hi aaa bbb
ccc hello
hi ff 
djd h
sdkf hello

Params:

  • -z this option tells grep to treat newlines as ordinary text characters, and look for null bytes to separate records.  In a text file with no null bytes, grep -z will treat the entire file as one line.

  • (?s) activate PCRE_DOTALL, which means that '.' finds any character or newline.

  • o Only print the matched part

  • -P Interpret the pattern as a Perl-compatible regular expression (PCRE). This is highly experimental, particularly when combined with the -z (--null-data) option, and ‘grep -P’ may warn of unimplemented features.

  • .*? For non-greedy match. Start searching for next match of hi when you find first instance of hello

    Source: https://www.gnu.org/software/grep/manual/grep.html

1
  • The grep -z output will include some \0 (not newlines). Those have to be changed.
    – user232326
    Oct 9, 2018 at 20:32
0

awk for giggles

#!/usr/bin/awk -f
{
    n=split($0, col, FS)
    for(i=1;i<=n;i++){
        (i==n)? sp="" : sp=FS
        if (col[i] ~ /hi/) p=1
        if (p == 1  && col[i] ~ /hello/) h=1
        if (p == 1) printf("%s%s",col[i],sp)
        if (h==1) p=0;h=0
    }
    printf "\n"
}



kapu@jake:$ cat SourceFile
hi aa bb cc
dd ee ff
hello xxxxxx
aaaa dddd cccc hi aaa bbb
ccc hello cccc fff
kapu@jake:$ script.awk SourceFile
hi aa bb cc
dd ee ff
hello
hi aaa bbb
ccc hello

You must log in to answer this question.

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