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?

2
  • 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.
    – user
    Dec 13, 2013 at 15:18
  • 1
    awk has getenv function
    – Basilevs
    Dec 13, 2013 at 15:18

1 Answer 1

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[@]}" "$@"
}     
2
  • Nice one. You could throw local IFS=" ," in there to allow a comma-separated list too. Dec 16, 2013 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, 2013 at 14:06

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .