6

I have this file:

table_01 (id, field01, field02, field03, field04)
record_01
record_02
record_03
table_02 (id, field01, field02, field03)
record_01
table_03 (id, field01, field02, field03, field04)
record_01
record_02
table_04 (id, field01, field02, field03, field04, field04)
record_01
table_05 (id, field01, field02, field03, field04)
record_01
record_02
record_03
record_04

I want to have a script to display the lines with the occurrence of the word “table” and display the number of lines between them and the lines after the last occurrence.

So the output would be:

table_01 (id, field01, field02, field03, field04)
3
table_02 (id, field01, field02, field03)
1
table_03 (id, field01, field02, field03, field04)
2
table_04 (id, field01, field02, field03, field04, field04)
1
table_05 (id, field01, field02, field03, field04)
4

So far I have this script:

awk '$0 ~ /table/ {if (n) print NR-1-n; n=NR}' file

Its output is:

3
1
2
1

But this script doesn't display the lines with the occurrence of “table”, and doesn't display the lines after the last occurrence.  How can I modify it to display what is missing?

3
  • 2
    I removed the awk tag since the question isn't about awk itself and you also clarified that you're open to non-awk solutions anyway.
    – terdon
    Apr 12 at 20:03
  • Hello, what would you like returned if two consecutive table lines are encountered? And...are all files guaranteed to start with a table line, or might there be stray records at the beginning? Thx. Apr 15 at 15:44
  • 1
    @jubilatious1 There is not consecutive lines starting with table, there is always at least one line starting with record after table line. There is not stray records at the beginning. Apr 15 at 18:12

4 Answers 4

5

Clearly, you’re 90% of the way there:

awk '/table/ {if (n) print NR-1-n; n=NR; print}
     END     {if (n) print NR-1-n}'             file

You don’t need $0 ~; that’s implied.

4

I put together a Perl solution:.

perl -Mfeature=say -e '
    while (<>) {
        if (/^table/) {
            $c && say $c;
            print;
            $c = 0;
            next;
        }
        $c++;
    }
    say $c;
' <input
user@server ~/[REDACTED] (git)-[REDACTED] % perl -Mfeature=say -e '
    while (<>) {
        if (/^table/) {
            $c && say $c;
            print;
            $c = 0;
            next;
        }
        $c++;
    }
    say $c;
' <input
table_01 (id, field01, field02, field03, field04)
3
table_02 (id, field01, field02, field03)
1
table_03 (id, field01, field02, field03, field04)
2
table_04 (id, field01, field02, field03, field04, field04)
1
table_05 (id, field01, field02, field03, field04)
4
3
  • 1
    It's fine, the solution is not limited to only awk Apr 12 at 18:46
  • 1
    For what it's worth, you can always use -l which auto-chomps input and adds a trailing \n to all print calls, so you don't need to bring in say, you can do perl -lne 'if (/^table/) { $c && print $c; print; $c = 0; next} $c++; }{ print $c' file.
    – terdon
    Apr 12 at 20:02
  • @terdon Right, I mostly use perl in scripts, I've forgot everything about those switches... Thank you for the reminder! Maybe I'll refactor this later for the sake of golf
    – kos
    Apr 12 at 21:30
2

My answer assumes a scenario in which there can be empty tables ("empty table" lines / matching lines) and a generalized scenario in which there might be extra (non-table / non-matching) lines prepended to the input file.

In this scenario, to display table lines (matching lines) and to count occurrences of their following record lines (non-matching lines), using awk, if desired pattern is ^table:

awk '
  /^$/ {next}
  /^table/ {
    if (precedingmatch)
      {print 0}
    else if (n)
      {print n}
    
    print; n=0; precedingmatch=1; matchesfound=1
  }
  !/^table/ {
    if (matchesfound) {n++}
    
    precedingmatch=0
  }
  END {if (matchesfound) {print n} else {print 0} }
' file.txt
  • precedingmatch is used to print 0 for when two subsequent lines are table lines.
  • matchesfound is used to ignore printing the count of non-table lines found before any first table line.

Sample file.txt with some "empty tables", a newline, and some randomly prepended lines:

randomline_01
randomline_02
table_01 (id, field01, field02, field03, field04)
table_02 (id, field01, field02, field03)
record_01
record_02

table_03 (id, field01, field02, field03, field04)
record_01
record_02
record_03
table_04 (id, field01, field02, field03, field04, field05)
table_05 (id, field01, field02, field03, field04)

Output:

table_01 (id, field01, field02, field03, field04)
0
table_02 (id, field01, field02, field03)
2
table_03 (id, field01, field02, field03, field04)
3
table_04 (id, field01, field02, field03, field04, field05)
0
table_05 (id, field01, field02, field03, field04)
0

A file with no "table" lines, an empty file, or a file filled with newlines outputs 0.

0
1

Using Raku (formerly known as Perl_6)

~$ raku -ne 'BEGIN my $c = 0;  if /^table/ { $c && put $c; .put; $c = 0; next}; $c++;'  file

Raku is a programming language in the Perl-family that features high-level support for Unicode. This Raku solution follows the general outline (in Perl) posted by @kos and @terdon.

Sample Input (extra table lines at end, although OP says these won't be encountered):

table_01 (id, field01, field02, field03, field04)
record_01
record_02
record_03
table_02 (id, field01, field02, field03)
record_01
table_03 (id, field01, field02, field03, field04)
record_01
record_02
table_04 (id, field01, field02, field03, field04, field04)
record_01
table_05 (id, field01, field02, field03, field04)
record_01
record_02
record_03
record_04
table_06 (id, field01, field02, field03, field04)
table_07 (id, field01, field02, field03, field04)

Sample Output:

table_01 (id, field01, field02, field03, field04)
3
table_02 (id, field01, field02, field03)
1
table_03 (id, field01, field02, field03, field04)
2
table_04 (id, field01, field02, field03, field04, field04)
1
table_05 (id, field01, field02, field03, field04)
4
table_06 (id, field01, field02, field03, field04)
table_07 (id, field01, field02, field03, field04)

Above gives the same answer as the Perl answers by @kos and @terdon. To be more explicit, the first statement inside the block can be written $c.Bool && put $c; or even $c.so && put $c;, but the code above suffices.


INSERT ZEROS IF NO INTERVENING "NON-TABLE" RECORDS:

Here's code to return 0 when no record lines follow the table header (similar to the answer by @Aeronautix):

~$ raku -ne 'BEGIN my $c = 0;  if /^table/ { $c && put($c-1); .put; $c = 0}; $c++; END put($c-1);'  file
table_01 (id, field01, field02, field03, field04)
3
table_02 (id, field01, field02, field03)
1
table_03 (id, field01, field02, field03, field04)
2
table_04 (id, field01, field02, field03, field04, field04)
1
table_05 (id, field01, field02, field03, field04)
4
table_06 (id, field01, field02, field03, field04)
0
table_07 (id, field01, field02, field03, field04)
0

Note: for all answers above the code assumes the first line starts with table, and OP confirms no stray lines will occur before the first table line.

However (for other users/data-sources), if a stray record line is encountered at the top of the file, the first answer will return the number of lines before the first table line. The second answer will return "one-minus" the number of lines before the first table line.

https://raku.org

You must log in to answer this question.

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