2

I have a awk script where i want to be able to pass N arguments into it and also read from stdin. I would like to be able to do something like

tail -f logfile | my_cool_awk_scipt var1 var2 var3 ... varN

And then use these variables inside the script.

#!/bin/awk -f

BEGIN { 
print "AWK Script Starting" 
print ARGV[1]
}                                                                              
{
    if ($0 < ARGV[1])
        print $0
    else if ($0 < ARGV[2])
        print $0 + ARGV[2]             
}

If i try to pass the variables as it stands it print ARGV[1] and then hits

awk: ./my_cool_awk_script:4: fatal: cannot open file `var1' for reading (No such file or directory)

I can do,

tail -f logfile | my_cool_awk_scipt -v var1=var1 -v var2=var2 -v var3=var3 ... varN=varN

but this is a bit limiting and verbose. I know i can also wrap this in a shell script but am unsure a clean way to embed what i have into something like that.

Thanks in advanced.

0

The moment awk hits the main body of the script, after BEGIN, it's going to want to read the filenames specified in ARGV[x]. So just nuke 'em.

$ cat a.awk
#!/bin/awk -f
BEGIN {
print "AWK Script Starting"
ZARGV[1]=ARGV[1]
ZARGV[2]=ARGV[2]
ARGV[1]=""
ARGV[2]=""
}
{
    if ($0 < ZARGV[1])
        print $0
    else if ($0 < ZARGV[2])
        print $0 + ZARGV[2]
}
$

Example:

$ cat logfile
1
2
3
4
5
$ ./a.awk 3 4 <logfile
AWK Script Starting
1
2
7
$
  • 1
    While the other Answers were good this was able to solve my problem in the most straight forward manner. Cheers – AdamR Aug 3 '18 at 16:30
  • 1
    maybe something like BEGIN{for(i in ARGV) ZARGV[i]=ARGV[i]; delete ARGV} to handle an arbitrary number of arguments? – ilkkachu Aug 3 '18 at 16:52
1

Just for the fun of it (and this is certainly NOT the recommended way to do it): As awk doesn't know about "positional parameters" (PP) but only variable assignments and input filenames, we need to dissect the PP out and tell them from the other two. This could be done by either separating the PP with a fixed token, e.g. -- (which is used in other context as well), or by knowing the PP count, either fixed or conveyed in e.g. ARGV[1]). Try

    awk '
    BEGIN   {while (ARGV[++MXPP] != "--")   PP[MXPP]     = ARGV[MXPP]
             for (j=MXPP+1; j<ARGC; j++)    ARGV[j-MXPP] = ARGV[j]
             ARGC -= --MXPP
            }

            {if ($0 < ARGV[1])
             print $0
             else if ($0 < ARGV[2])
             print $0 + ARGV[2]             
            }
    ' VAR1 VAR2 -- file[12]

If you use stdin in lieu of input files by piping sth in, you could omit the token and fetch the PP until the end of the list (i.e. set token to "")

0

You already know about -v variable=value. The other way is to pass variables through the environment and read them from the ENVIRON array:

$ var1=hello var2=world awk 'BEGIN { print ENVIRON["var1"], ENVIRON["var2"] }'
hello world

This sets the environment variables var1 and var2 in awk's environment only.

Or,

$ export var1=hello var2=world
$ awk 'BEGIN { print ENVIRON["var1"], ENVIRON["var2"] }'
hello world

This sets the variables in the calling environment before calling awk.

The ARGV array contains only the filenames that the awk program will read from in sequence, but it may also contain variable names set on the command line, as in

awk '...' var1=value1 var2=value2 filename

This is generally not a recommended way of passing variables into awk though (these variables would not be available in a BEGIN block for instance).

0

You could build a script like this:

#!/bin/bash   
vars=()
i=1
for arg in "$@"; do
    vars+=(-v "var$i=$arg")
    i=$((i+1))
done

awk "${vars[@]}" -f/dev/fd/3 3<< EOF
BEGIN {
    printf "awk var1: %s\n", var1;
    printf "awk var2: %s\n", var2;
}
1
EOF

and then run it:

$ echo some input | ./awk.sh foo bar doo
awk var1: foo
awk var2: bar
some input

The shell script will build a command line of those -v var1=... arguments, and pass those to awk, with the the actual awk program through a here-doc (of course you could have the awk script in a separate file instead). You can't pass any names of the input files this way, though, you're forced to have the awk script read from stdin.

At least GNU awk explicitly documents that ARGV[n] are used as the input files too (https://www.gnu.org/software/gawk/manual/html_node/ARGC-and-ARGV.html), which is why you get the "file not found" errors.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.