Håkon Hægland
Håkon Hægland

Reputation: 40758

Passing arguments with spaces contained in variable in bash

Consider passing some set of arguments to a function, e.g.:

awk -vv1="Hello there" -vv2=Bye 'BEGIN {print v1,v2}'

Note that the first argument contains a space. The output is:

Hello there Bye

Now, I try to store the arguments in a variable. Consider first the case without a space in the first argument:

arg="-vv1=Hello -vv2=Bye"
awk $arg 'BEGIN {print v1,v2}'

This works fine. Now insert the space in the variable:

arg="-vv1='Hello there' -vv2=Bye"
awk $arg 'BEGIN {print v1,v2}'

which gives the error message:

awk: there'
awk:      ^ invalid char ''' in expression

What is happening here?


Update

Based on the comments so far, I would like to refine my question: Consider the case where arg is the output of another shell script. For instance, assume that genArg is a shell script that prints the string:

"-vv1='Hello there' -vv2=Bye"

to the terminal. Then arg is collected (in a script) like

arg=$(genArg)

and next I would like to call awk with the given arguments:

awk $arg 'BEGIN {print v1,v2}'

Upvotes: 1

Views: 2788

Answers (3)

Ed Morton
Ed Morton

Reputation: 203597

In response to your updated question and comments:

$ cat tst.sh
function genArg() {
    printf "Hello there\n"
    printf "Bye\n"
}

IFS=$'\n' rslts=( $(genArg) )

awk -v v1="${rslts[0]}" -v v2="${rslts[1]}" 'BEGIN{
    printf "v1=\"%s\"\n", v1
    printf "v2=\"%s\"\n", v2
}'
$
$ ./tst.sh
v1="Hello there"
v2="Bye"

Here's a, IMHO, better alternative:

$ cat tst.sh
function genArg() {
    printf "Hello there\n"
    printf "Bye\n"
}

awk -v rslts="$(genArg)" 'BEGIN{
    split(rslts,v,/\n/)
    for (i=1;i in v;i++)
        printf "v[%d]=\"%s\"\n", i, v[i]
}'
$
$ ./tst.sh
v[1]="Hello there"
v[2]="Bye"

If you want to use named instead of numbered variables:

$ cat ./tst.sh
function genArg() {
    printf "greeting=Hello there\n"
    printf "farewell=Bye\n"
}

awk -v rslts="$(genArg)" 'BEGIN{
    split(rslts,tmp,/\n/)
    for (i=1;i in tmp;i++) {
        var = val = tmp[i]
        sub(/=[^=]+/,"",var)
        sub(/[^=]+=/,"",val)
        v[var] = val
    }

    for (var in v)
        printf "v[%s]=\"%s\"\n", var, v[var]

    greeting = v["greeting"]
    farewell = v["farewell"]

    print "greeting=\"" greeting "\""
    print "farewell=\"" farewell "\""
}'
$
$ ./tst.sh
v[greeting]="Hello there"
v[farewell]="Bye"
greeting="Hello there"
farewell="Bye"

Us the array v[] indexed by key strings as-is or map it's values to variables specifically named based on the keys strings. Either way...

Upvotes: 3

chepner
chepner

Reputation: 531185

Use an array:

arg=(-vv1="Hello there" -vv2=Bye)
awk "${arg[@]}" 'BEGIN {print v1,v2}'

Upvotes: 9

John Zwinck
John Zwinck

Reputation: 249153

Try this:

arg1="-vv1=Hello there"
arg2="-vv2=Bye"
awk "$arg1" "$arg2" 'BEGIN {print v1,v2}'

The idea is to make sure that awk sees exactly three string arguments: vv1, vv2, and the actual awk script text. You can do it this way with two variables, or if you really want to, you could probably also do it using a Bash array.

Upvotes: 0

Related Questions