2

In bash, I often run awk programs with many preset variables. Most of the times I use the same variable names in both bash and awk, so I call awk like the following:

awk -v var1="$var1" -v var2="$var2" -v var3="$var3" ....... '
    # awk code
' 

This gets a little tedious when I have many variable names to type. Ideally, I would like to be able to run something like the following:

$(awkline var1 var2 var3 ......) '
   # awk code
'

where the output of the command "awkline" is the correct format of the awk command. For example, the "awkline" program could look like:

#!/bin/bash
line="awk"
for i in $*; do line="$line -v $i="'"$'"$i"'"'; done
echo $line

If I then run "awkline a b c", I get what I expect:

awk -v a="$a" -v b="$b" -v c="$c"

However, if I try to run the command like $(awkline a b c), the value of the variable a inside awk becomes (literally including the quotes) "$a", and not the value of a, as I would like. How do I substitute all occurences of e.g. $a with the value of the variable a, so that I can run the awkline command?

  • This is often a bad idea, but have you considered eval? From the bash man page: eval [arg ...] - The args are read and concatenated together into a single command. This command is then read and executed by the shell, and its exit status is returned as the value of eval. – a CVn Dec 13 '13 at 15:18
  • 1
    awk has getenv function – Basilevs Dec 13 '13 at 15:18
2

Don't make awkline a separate script: that means you'll have to export all the variables you want pass to awk. You can make it a function that you add to your .bashrc. That way it runs in the current shell process and has access to your shell variables.

Handling the parameters will be tricky: You can either:

  1. pass the variables in as a space delimited string and let there be a variable number of file arguments:

    awkline "var1 var2 var3" "awk body" file ...
    
  2. or have the function always read from stdin and allow a variable number of variable names:

    cat file1 file2 ... | awkline var1 var2 ... "awk body"
    

To code the first:

awkline () {
    local variables=$1
    local body=$2
    shift 2
    local files=("$@")

    set -- $variables   # no quotes here, now the positional params are the var names
    local awk_cmd=(awk)
    for var; do
        awk_cmd+=(-v "$var=${!var}")
    done

    "${awk_cmd[@]}" "$body" "${files[@]}"
}     

Given the usage in your comment, this can be simplified:

awkline () {
    local awk_vars=()
    for var in $1; do                   #  no quotes here
        awk_vars+=(-v "$var=${!var}")
    done
    shift
    awk "${awk_vars[@]}" "$@"
}     
  • Nice one. You could throw local IFS=" ," in there to allow a comma-separated list too. – mr.spuratic Dec 16 '13 at 18:57
  • This actually (perhaps luckily?) works better than expected. It is possible to run: awkline "a b c" -v y="manually set" 'BEGIN {print a,y}' and get the expected output! Thanks! – Matthew Dec 17 '13 at 14:06

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.