A. Salas
A. Salas

Reputation: 97

How can I send the output of an AWK script to a file?

Within an AWK script, I'm needing to send the output of the script to a file while also printing it to the terminal. Is there a nice and tidy way I can do this without having a copy of every print redirect to the file?

I'm not particularly good at making SSCCE examples but here's my attempt at demonstrating my problem;

BEGIN{
    print "This is an awk script"
    
    # I don't want to have to do this for every print
    print "This is an awk script" > thisiswhack.out
}

{
    # data manip. stuff here
    # ...
    print "%s %s %s" blah, blah blah
    
    # I don't want to have to do this for every print again
    print "%s %s %s" blah blah blah >> thisiswhack.out
}

END{
    print "Yay we're done!"
    
    # Seriously, there has to be a better way to do this within the script
    print "Yay we're done!" >> thisiswhack.out
}

Surely there must be a way to send the entire output of the script to an output file within the script itself, right?

Upvotes: 1

Views: 2169

Answers (3)

Ed Morton
Ed Morton

Reputation: 203665

awk '
    function prtf(str) {
        printf "%s", str > "thisiswhack.out"
        printf "%s", str
        fflush()
    }
    function prt(str) {
        prtf( str ORS )
    }
    {
        # to print adding a newline at the end:
        prt( "foo" )

        # to print as-is without adding a newline:
        prtf( sprintf("%s, %s, %d", $2, "bar", 17) )
    }
' file

In the above we are not spawning a subshell to call any other command so it's efficient, and we're using fflush() after every print to ensure both output streams (stdout and the extra file) don't get out of sync with respect to each other (e.g. stdout displays less text than the file or vice-versa if the command is killed).

The above always overwrites the contents of "thisiswhack.out" with whatever the script outputs. If you want to append instead then change > to >>. If you want the option of doing both, introduce a variable (which I've named prtappend below) to control it which you can set on the command line, e.g. change:

    printf "%s", str > "thisiswhack.out"

to:

    printf "%s", str >> "thisiswhack.out"

and add:

    BEGIN {
        if ( !prtappend ) {
            printf "" > "thisiswhack.out"
        }
    }

then if you do awk -v prtappend=1 '...' it'll append to thisiswhack.out instead of overwriting it.

Of course, the better approach if you're on a Unix system is to have your awk script called from a shell script with it's output piped to tee, e.g.:

#!/usr/bin/env bash

awk '
    {
        print "foo"

        printf"%s, %s, %d", $2, "bar", 17 
    }
' "${@:--}" |
tee 'thisiswhack.out'

Note that this is one more example of why you should not call awk from a shebang.

Upvotes: 2

dan
dan

Reputation: 5231

The command to duplicate streams is tee, and we can use it inside awk:

awk '
    BEGIN {tee = "tee out.txt"}
    {print | tee}' in.txt
  • This invokes tee with the file argument out.txt, and opens a stream to this command.

  • The stream (and therefore tee) remains open until awk exits, or close(tee) is called.

  • Every time print | tee is used, the data is printed to that stream. tee then appends this data both to the file out.txt, and stdout.

  • The | command feature is POSIX awk. Also the tee variable isn't compulsory (you can use the string).

  • Of course, we can use tee outside awk too: awk ... | tee out.txt.

Upvotes: 2

Daweo
Daweo

Reputation: 36520

GNU AWK's Redirection allows sending output to command, rather than file, therefore I suggest following exploit of said feature:

awk 'BEGIN{command="tee output.txt"}{print tolower($0) | command}' input.txt

Note: I use tolower($0) for demonstration purposes. I redirect print into tee command, which does output to mentioned file and standard output, thus you should get lowercase version of input.txt written to output.txt and standard output.

If you are not confined to single awk usage then you might alternatively use tee outside, like so

awk '{print tolower($0)}' input.txt | tee output.txt

Upvotes: 1

Related Questions