奇怪的 bash 脚本行为 - 生成的命令在复制粘贴时有效,但在脚本中无效 [英] Weird bash-script behavior - generated command works when copy-pasted, but not from script
问题描述
出于安全原因,我编写了一个简短的 bash 脚本来包装 ansible playbook 命令.没有什么复杂的,大部分脚本在这里都无关紧要.最后,我将根据脚本参数生成的变量中的 ansible 命令放在一起,如下所示:
I have written a short bash-script to wrap an ansible playbook command for safety reasons. It's nothing much complicated, and most of the script is irrelevant here. At the end, I put together the ansible command from the variables that were generated from the arguments for the script, like so:
ansible-playbook -k $user -i hosts-staging $limit $tags $additionalargs site.yml
生成的命令在大多数情况下都有效,只是 $additionalargs 似乎没有任何效果.所以我做了一个回声,看看脚本实际生成了什么:
The generated command works for the most part, except that $additionalargs doesn't seem to have any effect. So I do an echo and look at what the script actually generated:
echo ansible-playbook -k $user -i hosts-staging $limit $tags $additionalargs site.yml
output: ansible-playbook -k -u pi -i hosts-staging -l tcameras --tags fulllaunch --extra-vars "agraphite_force_restart=true" site.yml
这看起来完全符合预期,但 --extra-vars 参数似乎没有通过.因此,我复制了 echo 生成的行,将其粘贴到控制台中并按 Enter,这完全有效,包括 --extra-vars 参数.
This looks exactly as intended, but the --extra-vars parameter seems to not get passed. So I copied the line produced by the echo, pasted it in the console and hit enter, and this worked fully, including the --extra-vars argument.
所以脚本生成了我想要的,如果我手动提供脚本生成的命令,它会起作用……但是当脚本本身执行命令时它不起作用,或者至少不完全.
So the script generates what I intend, and if I manually supply the command generated by the script it works... but it doesn't work when the script itself executes the command, or at least not completely.
任何人都可以给我提示可能会出错的地方吗?下面是完整的脚本供参考,尽管由于输出符合预期,我不希望问题出在这里,而是有一些我不知道的 bash 特性.
Can anybody give me a hint as to what might go wrong? below is the full script for reference, although since the output is as expected I don't expect the problem to be located here, but rather that there's some bash pecularity I just don't know about.
#!/bin/bash
# brief Makes graphite deployment via Ansible saver by wrapping it into a script that only needs the most common variables, does not apply to the whole inventory by default, and uses the current staging inventory.
# args (3) Expects 3 arguments: what to do, who to do it with, and if it should do it with all of them or just with one individual instance
# arg $1 What to do. Valid values are "deploy", "launch" or "restart".
# arg $2 Who to do it with. Valid values are "cameras", "servers" or "all".
# arg $3 Optional: Pass an IP from the inventory to apply the action to.
# error codes:
# 1: Unexpected number of arguments
# 2: Invalid first argument
# 3: Invalid second argument
# 4: Invalid third argument (optional)
# 5: Optional third argument not compatible with second argument
additionalargs=""
if [ $# -lt 2 ]; then
echo "Unexpected number of arguments (2 expected). Please pass what (\"deploy\", \"launch\" or \"restart\") and who (\"cameras\", \"servers\" or \"all\")"
exit 1
fi
if [ $1 == "launch" ]; then
tags="--tags fulllaunch"
elif [ $1 == "restart" ]; then
tags="--tags fulllaunch"
additionalargs='--extra-vars "agraphite_force_restart=true"'
elif [ $1 != "deploy" ]; then
echo "Invalid argument: "$1"! Must be either \"launch\", \"deploy\" or \"restart\"!"
exit 2
else
tags=""
fi
if [ $2 == "cameras" ]; then
limit="-l tcameras"
user="-u pi"
elif [ $2 == "servers" ]; then
limit="-l tservers"
user="-u ubuntu"
elif [ $2 == "all" ]; then
limit=""
user="both"
else
echo "Invalid argument: "$2"! Must be either \"cameras\", \"servers\", <ip> or \"all\"!"
exit 3
fi
if [ $# -eq 3 ]; then
if [[ $3 == *.*.*.* ]]; then #quite a superficial check for the validity of an ip address, but it serves to prevent dangerous input
limit="-l "$3
else
echo "Error: optional third parameter must be a valid ip-address!"
exit 4
fi
if [ $user == "both" ]; then
echo "Error: specific ip cannot be used with parameter \"all\"."
exit 5
fi
fi
if [[ $user == "both" ]]; then
#do actions for both cameras and servers
ansible-playbook -k -u ubuntu -i hosts-staging -l tcameras $tags $additionalargs site.yml
ansible-playbook -k -u pi -i hosts-staging -l tservers $tags $additionalargs site.yml
else
echo ansible-playbook -k $user -i hosts-staging $limit $tags $additionalargs site.yml
ansible-playbook -k $user -i hosts-staging $limit $tags $additionalargs site.yml
fi
推荐答案
我在编写另一个涉及相当复杂 curl 命令的脚本时找到了解决方案.首先我发现你可以使用
I found the solution to this one while writing another script involving rather complex curl commands. First I found out that you can use
set -x
在脚本中让它显示扩展的命令,我对引用扩展产生的混乱感到相当震惊.它在回声中很好地扩展了,但是作为参数传递了整个事情,构建了一个神秘的引号迷宫.我也尝试过引用变量,但 Ansible 不太喜欢这样.
In the script to make it show the expanded commands, and I was rather horrified at the mess the quote expansion produced. It expanded nicely in an echo, but passing the whole thing as arguments built a mystic labyrinth of quotes. I also experimented around with quoting the variables, but Ansible doesn't like that too much.
我发现对于带有许多参数且本身可能包含引号的长命令,最好将整个命令写在双引号内的字符串中并转义参数中的任何引号,然后将整个内容传递给 eval.
I found that for long commands with many arguments that themselves may contain quotes, it is best to write the whole command in a string within double quotes and escape any quotes inside the arguments, and then pass the whole thing to eval.
在这种特殊情况下,实际运行的命令如下所示:
In this particular case, the command that actually works looks like this:
eval "ansible-playbook -k $user -i hosts-staging $limit $tags $additionalargs site.yml"
这篇关于奇怪的 bash 脚本行为 - 生成的命令在复制粘贴时有效,但在脚本中无效的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!