奇怪的bash脚本行为-复制粘贴时生成的命令有效,但不是从脚本生成的

奇怪的bash脚本行为-复制粘贴时生成的命令有效,但不是从脚本生成的,bash,ansible,Bash,Ansible,出于安全考虑,我编写了一个简短的bash脚本来包装ansible playbook命令。这没什么复杂的,而且大部分脚本在这里都是无关的。最后,我将ansible命令与脚本参数生成的变量组合在一起,如下所示: ansible-playbook -k $user -i hosts-staging $limit $tags $additionalargs site.yml 生成的命令在大部分情况下都有效,除了$additionalargs似乎没有任何效果。所以我做了一个回音,看看脚本实际生成了什

出于安全考虑,我编写了一个简短的bash脚本来包装ansible playbook命令。这没什么复杂的,而且大部分脚本在这里都是无关的。最后,我将ansible命令与脚本参数生成的变量组合在一起,如下所示:

ansible-playbook -k $user -i hosts-staging $limit $tags $additionalargs site.yml  
生成的命令在大部分情况下都有效,除了$additionalargs似乎没有任何效果。所以我做了一个回音,看看脚本实际生成了什么:

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参数

所以脚本生成我想要的,如果我手动提供脚本生成的命令,它就会工作。。。但是当脚本本身执行命令时,或者至少不完全执行命令时,它不起作用

有谁能给我一个提示,说明什么地方可能会出错?下面是供参考的完整脚本,尽管由于输出如预期的那样,我不希望问题出现在这里,但是我不知道bash的一些特殊性

#!/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
#/bin/bash
#brief通过Ansible saver将graphite部署到一个脚本中,该脚本只需要最常用的变量,默认情况下不应用于整个库存,并使用当前的暂存库存。
#args(3)需要3个参数:做什么,和谁一起做,是应该和所有参数一起做,还是只和一个实例一起做
#arg$1要做什么。有效值为“部署”、“启动”或“重新启动”。
#arg$2与谁一起做这件事。有效值为“摄像机”、“服务器”或“全部”。
#arg$3可选:从清单中传递一个IP以应用操作。
#错误代码:
#1:意外的参数数
#2:第一个参数无效
#3:第二个参数无效
#4:第三个参数无效(可选)
#5:可选的第三个参数与第二个参数不兼容
additionalargs=“”
如果[$#-lt 2];然后
echo“意外的参数数(应为2个)。请传递什么(\'deploy\'、\'launch\'或\'restart\')和谁(\'cameras\'、\'servers\'或\'all\')”
出口1
fi
如果[$1==“启动”];然后
tags=“--tags fulllaunch”
elif[$1==“重新启动”];然后
tags=“--tags fulllaunch”
additionalargs='--extra vars“agraphite\u force\u restart=true”
elif[$1!=“部署”];然后
echo“无效参数:$1”!必须是“启动”、“部署”或“重新启动”!”
出口2
其他的
tags=“”
fi
如果[$2==“摄像机”];然后
limit=“-l tcameras”
user=“-u pi”
elif[$2==“服务器”];然后
limit=“-l t服务器”
user=“-u ubuntu”
elif[$2==“全部”];然后
limit=“”
user=“两者”
其他的
echo“无效参数:$2”!必须是“摄像机”、“服务器”或“全部”!”
出口3
fi
如果[$#-eq 3];然后
如果[[$3==*.*]];然后#对ip地址的有效性进行相当肤浅的检查,但它可以防止危险的输入
limit=“-l”$3
其他的
echo“错误:可选的第三个参数必须是有效的ip地址!”
出口4
fi
如果[$user==“两者”];然后
echo“错误:特定ip不能与参数\“all\”一起使用”
出口5
fi
fi
如果[[$user==“两者”];然后
#对摄影机和服务器执行操作
ansible playbook-k-u ubuntu-i主机staging-l tcameras$tags$additionalargs site.yml
ansible playbook-k-u pi-i hosts staging-l tservers$tags$additionalargs site.yml
其他的
echo ansible playbook-k$user-i主机暂存$limit$tags$additionalargs site.yml
ansible playbook-k$user-i主机staging$limit$tags$additionalargs site.yml
fi

我在编写另一个涉及相当复杂的curl命令的脚本时找到了解决方案。 首先我发现你可以使用

set -x
在脚本中显示扩展的命令,我对引用扩展产生的混乱感到相当震惊。它在一个回音中很好地扩展了,但整个过程都以论点的形式传递,形成了一个神秘的引语迷宫。我也尝试过引用变量,但Ansible不太喜欢这样

我发现,对于本身可能包含引号的具有许多参数的长命令,最好将整个命令写在双引号内的字符串中,并转义参数内的任何引号,然后将整个内容传递给eval

在这种情况下,实际运行的命令如下所示:

eval "ansible-playbook -k $user -i hosts-staging $limit $tags $additionalargs site.yml"

这里是BashFAQ#50:…这里还有一堆其他bug
=
在POSIX测试中实际上不是有效的运算符——标准保证的唯一字符串比较运算符是
=
。在
[$user==“both”]
中,您引用的是不需要引用的内容(文字),但扩展了不需要引用的内容(扩展)——如果您的名称带有空格,则会导致语法错误(在某些平台上,空格是合法的;在Windows/Cygwin上,空格是完全常见的)。考虑运行你的代码。谢谢。对于那些不经常抨击的人来说,突然将变量放在引号中而不是字符串中真的很奇怪。我明白了原因,但仍然觉得这样做是完全错误的。以这种方式使用
eval
是非常非常糟糕的形式——如果您的任何变量包含,例如,
$(rm-rf$HOME)
,该代码将被扩展,而不是按字面意思传递……收集参数列表的正确方法是在为此目的而构建的数据结构中——也就是说,数组。你的意思是,像#ansible playbook${arguments[@]}一样,仍然缺少引号<代码>ansible playbook“${arguments[@]}”,如果您已将参数收集到名为arguments的数组中。(希望你也能得到照顾