Grant Bowman
Grant Bowman

Reputation: 327

awk "if" statement

I have a file

foo
--
bar

I only want the lines above the separator. I've struggled with this for too long and tried a number of variants. My one liner is:

echo -e "foo\n-- \nbar" | gawk -v x=0 -- '/^--\ / { x++ } ; IF (x==0) {print} '

This should print only "foo" but I get the whole file output. If I change to print x I get

0
1
1

I can't seem to make make awk conditionally print a line based on the value of x. I know I am missing something simple.

Upvotes: 4

Views: 4535

Answers (4)

Ed Morton
Ed Morton

Reputation: 203139

In GNU awk you could just set the record separator to be a line that only contains "--" and then just print the first record:

$ gawk -v RS='\n--\n' 'NR==1' file
foo

or if performance is a concern:

$ gawk -v RS='\n--\n' 'NR==1{print;exit}' file
foo

that way you can enhance the script later to print any other record you might want:

$ cat file
the
quick
--
brown
fox
--
jumped
$
$ gawk -v RS='\n--\n' 'NR==1' file
the
quick
$ gawk -v RS='\n--\n' 'NR==2' file
brown
fox
$ gawk -v RS='\n--\n' 'NR==3' file
jumped

Upvotes: 1

gniourf_gniourf
gniourf_gniourf

Reputation: 46813

If you like sed better:

sed -n '/^--$/q;p' file.txt

Explanation: sed reads the file line by line. If sed finds the pattern ^--$ (i.e., a line that contains exactly --) it quits (that's the q command), otherwise, sed prints out the content of that line (with the p command). Note that sed was launched with the -n option, i.e., doesn't output anything unless explicitly told to with the p command. Since sed quits when the separator -- is found (i.e., before the p command), this separator will not be printed.

The good thing about sed is that it's faster than awk for this task.

Edit. As glenn jackman points out in a comment, with GNU sed, you can use:

sed '/^--$/Q' file.txt

(I wasn't on a computer that had a sed with the Q command when I answered). Thanks glenn.

Upvotes: 2

Gilles Quénot
Gilles Quénot

Reputation: 184965

Try doing this :

echo -e "foo\n-- \nbar" | awk '/^--/{exit}1'

EXPLANATIONS

  • /^--/ is a regex to match the string at the beginning of the current line
  • {} part is executed if the condition is true (the previous regex)
  • 1 is like {print} : by default, awk print on STDOUT if a condition is true. While 1 is true for awk, it print the current line.

Decomposition of the command :

echo -e "foo\n-- \nbar" | awk '
    {
        if (/^--/) {
            exit
        }
        else {
            print
        }
    }
'

Alternative decomposition:

echo -e "foo\n-- \nbar" |
awk '(/^--/) { exit }
             { print }'

This emphasizes that there are two pattern-action rules; one with an explicit pattern and an exit action; the other with implicit pattern and print action.

Upvotes: 5

Jonathan Leffler
Jonathan Leffler

Reputation: 753465

Your original script was on the right lines but too complex:

echo -e "foo\n-- \nbar" | gawk '/^--\ / { x++ } { if (x==0) print}'

Variables are automatically created and zeroed by awk (so you don't need -v x=0 etc). The 'spot the double dash' code was fine. The semi-colon is unnecessary (at best). The IF (x == 0) {print} is plain weird. The awk on Mac OS X 10.7.5 accepts it, but I'm not sure what it was doing. The replacement action is for every line, and tests whether x is zero before printing.

Personally, I'd probably use sed for this:

echo -e "foo\n-- \nbar" | sed '/^--/q'

Fixing my sed command as gniourf_gniourf suggests:

echo -e "foo\n-- \nbar" | sed -n '/^--/q;p'

You can mimic that in awk with the command shown by sputnick in his answer.

echo -e "foo\n-- \nbar" | awk '/^--/ {exit} 1'

The 1 matches every line (it is always true) and triggers the default action, which is 'print $0'. You could also write:

echo -e "foo\n-- \nbar" | awk '/^--/ {exit} {print}'

Upvotes: 1

Related Questions