mjb4
mjb4

Reputation: 987

awk reparse piped input without getline

I wrote an awk script that will parse piped input and turn it into a well spaced table. To achieve this I needed to parse the input stream twice. First to parse the actual column size for each column. And then for printing the table itself.

#!/bin/gawk -f
# with changes from ooga

BEGIN {
    FS=" "
    buffer = "mktemp" | getline result
    # Initialize Vars
}

{
    # Count Columns...
}

END{
    close(buffer)

    while((getline < buffer) > 0){
        # Print formated table
    }
}

So this is working but it uses getline and all manuals pointed out that there are very few cases where you really need getline. Thought the only other option I found was using files instead of pipes.

Is there another option in gawk that will parse piped input twice?

Upvotes: 1

Views: 692

Answers (2)

ooga
ooga

Reputation: 15501

There's not really a better way to do it (EDIT: actually, it's probably better to use an array as Ed Morton has said; see his post and my alternate example at the end of this post), but it's not a very "awkish" program since it doesn't use the pattern{action} paradigm. The only advantage of awk for this program is the automatic field-splitting.

Some tips:

  • FS defaults to a single space (which has the special meaning that fields are separated by runs of whitespace and that leading and trailing whitespace is ignored.) So there's no need to explicitly set it to a space.

  • |& opens a coprocess, but you only need a regular pipe so just ust |.

  • You should explicitly close the pipe.

  • The function seems an unecessary complication.

  • You should delete the temporary file after you're finished with it.

This yields:

#!/bin/gawk -f

BEGIN {
    "mktemp" | getline tmpfile
    close("mktemp")
}

{
    # process and save piped data to tmpfile
}

END {
    close(tmpfile)
    while((getline < tmpfile) > 0) {
        # process data from tmpfile
    }
    system("rm " tmpfile)
}

Here's an example of using an array instead of a temporary file:

#!/bin/awk -f

{
    line[NR] = $0
    if (NF > nf)
        nf = NF;
    for (i=1; i<=NF; ++i)
        if (length($i) > flen[i])
            flen[i] = length($i)
}

END {
    for (r=1; r<=NR; ++r) {
        for (f=1; f<=nf; ++f) {
            split(line[r], fields)
            printf("| %-*s ", flen[f], fields[f])
        }
        print "|"
    }
}

Output:

$ cat file
one two three
four five six
seven eight nine
$ cat file | ./columnize.awk
| one   | two   | three |
| four  | five  | six   |
| seven | eight | nine  |
$

Upvotes: 1

Ed Morton
Ed Morton

Reputation: 203924

Just store the input in an array and then print that. You didn't post any sample input and expected output so there's nothing we can test against but something like this might be what you want:

awk '
{
    line[NR] = $0
    curLength = length($0)
    if (curLength > maxLength)
        maxLength = curLength
}
END {
    for (i=1; i<=NR; i++) {
        printf "| %*s |\n", maxLength, line[i]
    }
}
'

Upvotes: 2

Related Questions