13

There's a script (let's call it echoer) that prints to screen a bunch of information. I'd like to be able to only see lines after a pattern is found.

I imagine the usage of a solution to look something like

echoer | solution_command <pattern>

Ideally pattern would be a regular expression, but hard value strings would be enough for me.

3
  • So pattern can be multiple strings? – Inian Mar 28 '19 at 9:41
  • A glob? Do you mean a regular expression? Globs only make sense for file name expansions. – terdon Mar 28 '19 at 9:47
  • @Inian I'm not sure I understand the question. Do you mean to ask if I want this to work with disjunctions? – user23146 Mar 28 '19 at 10:03
21

AWK can do this with pattern ranges, which allows the use of any regular expression:

echoer | awk '/pattern/,0'

will print echoer’s output starting with the first line matching pattern.

AWK is pattern-based, and is typically used with a “if this pattern matches, do this” type of approach. “This pattern” can be a range of patterns, defined as “when this pattern matches, start doing this, until this other pattern matches”; this is specified by writing two patterns separated by a comma, as above. Patterns can be text matches, as in /pattern/, where the current line is checked against the pattern, interpreted as a regular expression; they can also be general expressions, evaluated for every line, and considered to match if their result is non-zero or non-empty.

In AWK, the default action is to print the current line.

Putting all this together, awk '/pattern/,0' looks for lines matching pattern, and once it finds one, applies the default action to all lines until the 0 condition matches (is non-zero). awk '/pattern/,""' would work too.

The Gawk manual goes into much more detail.

3
  • 3
    I was not aware of the range semantics with zero as the end of the range. Thanks! – Kusalananda Mar 28 '19 at 10:06
  • @StephenKitt This is great! What if I wanted to print until it matched 0? Would it be /pattern/,/0/? What would the answer look like explicitly writing out the default action? – user23146 Mar 28 '19 at 10:32
  • @user23146 yes, /pattern/,/0/ would print until it found a line (record) containing “0”. Writing the snippet in the answer with an explicit action gives /pattern/,0 { print $0 }, or equivalently /pattern/,0 { print }. – Stephen Kitt Mar 28 '19 at 10:46
7

The obligatory sed equivalent of @StephenKitt's awk one:

sed '/pattern/,$!d'

pattern there is interpreted as a Basic Regular Expression like in grep (as opposed to Extended Regular Expression in awk/egrep/grep -E). Some sed implementations have a -E (BSD, ast, recent GNU/busybox, soon POSIX) or -r (GNU, ssed, busybox, some recent BSD) option to make it Extended Regular Expressions instead and some have -P (ast) or -R (ssed) to make it a perl-like regular expression.

With perl:

perl -ne 'print if /pattern/ .. undef'
4

with GNU and *BSD grep:

grep -A1000000000 pattern file

Unless your file has more than 1M lines, that's it.

0

If you're using a pager such as less to view output from the command

less +pattern
0

awk for lines after (but not including) the first pattern match

If the line containing the trigger pattern is the equivalent of "CUT HERE", you can omit it from the printed output:

echoer | awk 'flag ; /pattern/ { flag=1 }'

Each line of input runs through two components in the awk code. The first component is flag, which awk interprets as "print the line if the variable flag is nonzero". Since awk variables are 0 by default, this will initially not print anything.

The second component, /pattern/ { flag=1 }, sets the flag to 1 as soon as it detects the pattern, and the flag keeps that value for the rest of the run.

By the time that the pattern is first detected, the opportunity to print that line of input has passed. Any subsequent lines (including additional lines containing the pattern) will print.

0

Bash

A bit clunky, but it works.

#!/bin/bash
found=false
while IFS= read -r; do
    if $found || [[ $REPLY =~ pattern ]]; then
        found=true
        printf '%s\n' "$REPLY"
    fi
done

This version relies on cat, but it's easier to understand.

#!/bin/bash
while IFS= read -r; do
    if [[ $REPLY =~ pattern ]]; then
        printf '%s\n' "$REPLY"
        break
    fi
done
cat

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.