This is probably a duplicate of "How can we run a command stored in a variable?", but I'll answer it for your particular case since it has multiple parts to it:
- Storing a command in a variable and running it using the variable.
- Passing data into
awk
.
The "run command in variable" bit
Here's a suggestion for an alternative implementation of your script:
#!/bin/bash
wrk=( awk -v a="$3" -v b="$2"
'BEGIN { printf "%s and %s variable example\n", a, b }' )
awk -v a="$1" -v b="$2" 'BEGIN { printf "%s and %s silly example\n", a, b }'
printf 'wrk is: %s\n' "${wrk[*]}"
"${wrk[@]}"
Running this:
$ ./blue.sh "red" "blue" "yellow"
red and blue silly example
wrk is: awk -v a=yellow -v b=blue BEGIN { printf "%s and %s variable example\n", a, b }
yellow and blue variable example
What I do is that I store the awk
command in an array instead of in a string. This ensures that each separate element of the array may be properly quoted while still remaining a separate element, separate from the other parts of the array. For example, the awk
word at the start is one word separate from the last entry in the array which is the entire awk
code.
When using "${wrk[@]}"
as a command, each element of the array is used as individual command line arguments (the first element being the command). The quoting of the "${wrk[@]}"
expansion ensures that each element is individually quoted (using ${wrk[@]}
would not work).
That's how you store and run a command in a variable, i.e. you use an array and make sure to properly quote the array elements and the expansion of the array.
For the printf
call in the shell script, I use "${wrk[*]}"
to generate a single string from the wrk
array (all elements separated by spaces by default). You will notice that this does not preserve any quoting of the individual elements, which is to be expected since the shell removed these when creating the array (just like the quotes are removed when doing things like a='hello world'
).
To see each separate element of wrk
, use something like printf 'element: "%s"\n' "${wrk[@]}"
instead. This would output
element: "awk"
element: "-v"
element: "a=yellow"
element: "-v"
element: "b=blue"
element: "BEGIN { printf "%s and %s variable example\n", a, b }"
In this particular case, and depending on your needs, you may also be served by using a shell function:
#!/bin/bash
wrk () {
awk -v a="$1" -v b="$2" 'BEGIN { printf "%s and %s variable example\n", a, b }'
}
awk -v a="$1" -v b="$2" 'BEGIN { printf "%s and %s silly example\n", a, b }'
wrk "$3" "$2"
Note that $1
and $2
in the function refers to the function's first and second argument, not the script's.
Running this:
$ ./blue.sh "red" "blue" "yellow"
red and blue silly example
yellow and blue variable example
The awk
-specific bit
To pass data into awk
I'm using -v
to initialise awk
variables on the awk
command line. This is preferable to injecting variables into the awk
code via shell expansions since doing that would potentially allow a user to inject executable code rather than just data (by simply making $3
contain a ;
and some awk
code, for example). See "Command line argument in awk" and similar question on this site for more about that.
You may also pass data into awk
using environment variables:
a=$3 b=$2 awk 'BEGIN { printf "%s and %s variable example\n", ENVIRON["a"], ENVIRON["b"] }' )
or,
export a="$3"
export b="$2"
awk 'BEGIN { printf "%s and %s variable example\n", ENVIRON["a"], ENVIRON["b"] }' )
I'm also executing the awk
code in a single BEGIN
block since there is no input to read on standard input.
a
andb
. The goal fo rme is to be processing lines from STDIN through a what is effectively a template of:$3 " and " $2 " variable example". Use of a
BEGIN{ }closure is a different use case, also at I need to update the question, the
$1,
$2,
$3variables are the
awk` input fields mea culpa on for that. – will Mar 05 '20 at 21:50