awk script file command line options

Being new to awk I have a really basic question. It just has to be in the archives but it didn't bite me when I went looking for it.

I've written an awk script, placed it in a file, added the "#!/usr/bin/awk -f" at the top of the script and away I go. "% myAwk <inputfile>" gives me exactly what I expect. Now I want to add a command line option to my script to alter its behavior. Something like "% myAwk -opt1 <inputfile>" but awk is interpreting my option as just another input file rather than some internal variable (like: argv[1] ...)

Is what I'm trying to do even possible? If so, what would the awk script look like internally?

  • Tom

This gets a bit tricky, especially if you want the awk programme to process a file other than stdin.

Simple case, awk programme will process standard input:

#!/bin/awk    --exec
BEGIN {
       for( i = 0; i < ARGC; i++ )
           printf( "argv[%d] = (%s)\n", i, ARGV );
       ARGC=1;
    }
    { print; } 

This programme reads and prints the command line arguments, and then prints all of the lines from stdin. All you have to do is interpret the stuff in ARGV. When executing with a file on stdin that has two input lines, this is the output:

spot; t16a  -c bar foo <t16a.data
argv[0] = (awk)
argv[1] = (-c)
argv[2] = (bar)
argv[3] = (foo)
line 1 in test
Last line in test

The magic here is setting ARGC to 1 in the BEGIN block which causes awk to ignore the rest of the command line. To complicate things, if you want to supply the input file name(s) on the command line, then you'll have to process your parameters and reset ARGV and ARGC accordingly. This is a simple example that accepts a -f value and/or -v parameter(s), and then shifts the file name(s) down:

#!/bin/awk    --exec
BEGIN {
        for( i = 1; i <= ARGC; i++ )
        {
            if( substr( ARGV, 1, 1 ) != "-" )        # assume first non -x is a file name
                break;

            if( ARGV == "-v" )     # example option with no trailing data
            {
                verbose = 1;
                continue;                  # loop to avoid error trap
            }

            if( ARGV == "-f" )      # example option with trailing data
            {
                foo = ARGV[i+1];    # need to validate i+1 isn't out of range
                i++;                # bad form, but it works
                continue;           # loop to avoid error catch
            }

            # suss out other desired options like above

            printf( "unrecognised option: %s\n", ARGV ) >"/dev/stderr";
            exit(1);
        }

        j = 1;
        c = 1;
        for( i; i < ARGC; i++ )     # copy input file names down in argv
        {
            ARGV[j++] = ARGV;
            c++;                    # new setting for ARGC
        }

        ARGC = c;    # number of file names shifted + 1 for argv[0] value
    }

    { print; }

The command line would be something like:

script-name -v -f "some value" intput-file1 input-file2

Personally, I prefer to wrap my awk with a shell script and let it do all of the command line parsing and other error checking. The script then invokes awk with one or more -v var=value options to pass in the desired data.

Hope this gets you going again.

1 Like

Wow! Thanks for the great explanation! It was easy to follow and exactly what I needed. As you point out, it's not as concise as you could do with a shell script but your implementation is still clean and self-contained.
The only problem I had was with "--exec". Though the awk documentation indicates this is exactly what I need for some reason it didn't like that option and kept displaying the usage message. I changed this to "-f" and used the "--" characters to terminate the awk options ... good enough though not ideal.
Thanks again! :wink: