1

I'm trying to run this command from a bash script and pass the extra-vars from the bash arguments.

#!/bin/bash
ansible-playbook /path/to/playbook.yml --extra-vars "var1=1 $@" > /path/to/log/file

I run the script like below and get this error:

> ./test.bs var2=2 var3=3
ERROR! the playbook: var3=3 could not be found

I even tried escaping the quotes but it didn't work. How can I get this to run?

1
  • 1
    "$@" expands to multiple separate arguments. Thus, "var1=1 var2=2" "var3=3" Commented May 23, 2017 at 22:08

5 Answers 5

1

Or, you can change your script to use $*:

#!/bin/bash
ansible-playbook /path/to/playbook.yml --extra-vars "var1=1 $*" > /path/to/log/file
Sign up to request clarification or add additional context in comments.

3 Comments

While rarely true, this is actually a case where $* is might actually be the Right Thing, at least if the values are guaranteed to not get too interesting.
I chose this answer because of the simplicity. Can you explain why this works though ?
Sure. "$@" puts quotes around each command-line argument, and $* does not. So "$@" comes out to '"var2=2" "var3=3"', and "$*" comes out to '"var2=2 var3=3"'. Since ansible-playbook is expecting one and only one argument to --extra-args, so it all has to be in a single pair of quotes: "var1=1 var2=2 var3=3". "$@" will put in quotes you don't want.
1

As long as you're on Ansible 1.2 or newer, the most robust thing to do is use jq to generate JSON. The following will do this for an arbitrary number of key=value arguments:

#!/bin/bash
jq_args=( )
jq_text='{}'

i=0
for arg in var1=1 "$@"; do
  [[ $arg = *=* ]] || continue # require arguments to be in key=value form
  key=${arg%%=*}
  value=${arg#*=}
  jq_args+=( --arg "key$i" "$key" --arg "value$i" "$value" )
  jq_text+=" | .[\$key$i]=\$value$i"
  ((i++))
done

json_final=$(jq "${jq_args[@]}" "$jq_text" <<<"$json") # || exit

ansible-playbook /path/to/playbook.yml --extra-vars "$json_final"

Comments

0

You are passing one long argument to --extra-vars since it's all within the same " marks. You are building a single string of var1=1 arg1 arg2 arg3.

Your intention was likely to instead pass three arguments, each one a string that look like var1=1 var2=2 var3=3

With that said, try this.

ansible-playbook /path/to/playbook.yml --extra-vars "var1=1" "$@" > /path/to/log/file

This way you are passing the first arg as the string "var1=1" and then bash expands "$@" into the individual arguments that you've passed to the program.

So with the above snippet ./test.bs var2=2 var3=3 will properly execute. For sanity though it's good practice to wrap your args in " though in this case its not necessary. It's good to get in the habit though since one day you'll run ./test.bs var2=* and get some crazy output. So the final, most sane, call would be...

./test.bs "var2=2" "var3=3"

1 Comment

"Passing one long argument"? Not true; "$@" causes splits between distinct arguments to be preserved. Try running set -- one two three; printf '<%s>\n' "start$@end", and you'll see startone as the first argument, two as the second, and threeend as the last. Compare to the same but with "start$*end" -- in that case everything's passed as just one argument.
0

Run it like this:

> ./test.bs 'var2=2 var3=3'

Comments

-1

This seems correct and should work.

Quoting and not quoting var1=1 $@ will make a difference only for the way the arguments are passed in. Not quoting it will allow arguments to be passed quoted (i.e. "var2=2 var3=3") or unquoted (i.e. var2=2 var3=3) as it will expand space separated arguments into separate arguments. If $@ is quoted, the arguments must be passed not quoted or individually quotted (i.e. "var2=2" "var3=3") so they are accepted as individual arguments.

One option that would work for sure is using eval, however, that is usually avoided as it allows passing unsafe data. Bascially just put eval before the command:

eval ansible-playbook /path/to/playbook.yml --extra-vars "var1=1 $@" \
    > /path/to/log/file

It will go through the command, parsing it and removing all first level quotes, making the command ansib ... --extra-vars var1=1 var2=2 var3=3 > /path....

3 Comments

That's not the correct form for --extra-vars. It expects a single argument to follow -- ie. from docs.ansible.com/ansible/…, see the example with ansible-playbook release.yml --extra-vars "version=1.23.45 other_variable=foo"
and frankly, if you did want to pass one argument per variable, why would you use eval rather than make it --extra-vars "var1=1" "$@"?
(The use of eval also exposes your data to parsing as code -- if $@ had an argument originally specified as var4='$(rm -rf ~)', someone would have a very bad day).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.