1
foo
I love
Stack Exchange
bar
Some junk
lines
foo
Welcome to the
world of
Bash
bar
foo
Elon Musk
rocks

Yes, there is no bar complementing foo at the end of the file

The required output is

I love Stack Exchange
Welcome to the world of Bash
Elon Musk rocks

So, remove all newline characters, between ^foo and ^bar or between ^foo and the end of file where there is no bar

2

With awk:

awk '
  $0 == "foo" {if (sep) print ""; sep = ""; inside = 1; next}
  $0 == "bar" {inside = 0; next}
  inside {printf "%s", sep $0; sep = " "}
  END {if (sep) print ""}'

To match on lines with foo as the first word, replace $0 == "foo" with $1 == "foo"; to match on lines starting with foo, replace with /^foo/ (short for $0 ~ /^foo/).

2

Perl to the rescue!

perl -ne '
    if ($e = /^foo$/ .. /^bar$/) {
        if ($e =~ /E/) { print "\n" }
        else {
            chomp;
            print " " if $e > 2;
            print     if $e > 1;
        }
}' -- input.txt
  • -n reads the input line by line
  • .. is the range operator, it returns the relative line number for each line in a block. The last line has E0 appended to it.
  • Why are >2 and >1 used here and what are they doing basically? – GypsyCosmonaut Sep 17 '18 at 1:13
  • It's numerical comparison. $e > 2 excludes the first two lines of each block, $e > 1 excludes only the first (foo) one. – choroba Sep 17 '18 at 1:30
  • 1
    Yes, you can move the $e > 1 one level up: perl -ne 'if ($e = /^foo$/ .. /^bar$/) { if ($e =~ /E/) { print "\n" } elsif ($e > 1) { chomp; print " " if $e > 2; print; } }' -- file – choroba Sep 17 '18 at 1:49
2

You could do this way also:

a) This is POSIX-sed compliant code wherein we store the mid-range lines (!/foo/ and !/bar/) in the hold area.

sed -e '
    /foo/,/bar/!d
    /foo/d
    /bar/!{H;$!d;}
    s/.*//;x;s/^\n//;y/\n/ /
' input-file.txt

b) and with Perl as shown

perl -lne '
   next unless /foo/ ... /bar/;
   push(@A, $_),next if !/foo/ && !/bar/ && !eof;
   push(@A, $_)      if !/foo/ && !/bar/ &&  eof;
   print join " ", splice @A if /bar/ || !/foo/ && eof;
' input-file.txt

Explanation:

  • skip non-interesting lines or lines that fall out of range. A range is defined as beginning with a line containing /foo/ and ending with /bar/, both occurring on separate lines.
  • Once we are in the range, then perform separate actions based on what line we are in.
  • For lines that are not /foo/, neither /bar/, and neither eof : store the line in the array @A. After storing, go back for reading the next record provided not eof.
  • Only for the end of range, i.e., /bar/ or eof, we take the action of printing what's in @A and also emptying it, in preparation for the next gathering cycle.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.