如何(不)将空引号变量作为命令参数传递

12

我在一个bash脚本中有一个函数,它看起来像这样(简化版):

# Usage: f URL [PARAMETER]...
f() {
    local URL="$1"; shift

    local PARAMS
    for arg in "$@"; do
        PARAMS="${PARAMS}&${arg}"
    done
    PARAMS="${PARAMS#'&'}"

    local DATA_OPTION
    [ -z "${PARAMS}" ] || DATA_OPTION='--data'

    curl -o - "${DATA_OPTION}" "${PARAMS}" "${URL}"
}

可以像这样调用f http://example.com/resourcef http://example.com/resource p1=v1 p2=v2。问题在于当DATA_OPTIONPARAMS为空时。在这种情况下,Bash会向curl传递两个空参数,然后被curl识别为URL,并产生以下丑陋的消息:

curl: (3) <url> malformed
curl: (3) <url> malformed

我使用if/else暂时解决了问题,以便DATA_OPTIONPARAMS完全不被传递:

我用 if/else 暂时解决了这个问题,这样就完全不需要传递 DATA_OPTIONPARAMS

    [..]

    if [ -z "${PARAMS}" ]; then
        curl -o - --data "${PARAMS}" "${URL}"
    else
        curl -o - "${URL}"
    fi
}

但我觉得这样很丑陋。有没有更优雅的解决方案?请注意,PARAMS周围的引号是必需的,因为一些参数值可能包含空格。

3个回答

22
您可以使用参数展开中的 "使用替代值" 选项(:+)来干净地解决此问题:
curl -o - ${PARAMS:+"--data" "$PARAMS"} "${URL}"

如果PARAMS为空或未定义,则整个${PARAMS:+"--data" "$PARAMS"}会被解析为空字符串,因为它没有双引号,因此单词拆分将其完全删除。另一方面,如果PARAMS非空,它将有效地被替换为"--data" "$PARAMS",这正是你想要的。
[编辑] 这在大多数 POSIX-ish shell 中都可以工作,但在 zsh 中不行,因为即使未加引号,zsh 也不会对扩展进行单词拆分。如果您希望在 zsh(以及 bash、dash、ksh 等)中使用此方法,则需要将选项标签作为一个单独的条件项。
curl -o - ${PARAMS:+"--data"} ${PARAMS:+"$PARAMS"} "${URL}"

1
为什么要在那里引用 --data,而不是简单地使用 ${PARAMS:+--data "$PARAMS"} - janos
4
@janos: 两种都可以,但我更喜欢带引号的版本,有2个原因:首先,在编写shell脚本时,习惯性地加上引号比试图记住什么情况下可以不用引号更安全。其次,当阅读这种脚本时,如果在参数扩展选项(:+)和参数(--data)之间有一个引号,就很容易知道它们之间的分界点(空格也可以,但参见第一个原因)。 - Gordon Davisson

1

我认为这是懒惰和优雅的完美结合:

curl -o - --data "&$PARAMS" "$URL"

没错,这里有一个无用的&。但问题是它不会影响任何人,而且非常简短,无论你的PARAMS是否为空,它都应该能够正常工作。


0

使用 bash 数组 构建命令

我经常使用 数组来实现这个功能!

注意,使用 IFS='&' 来合并带有 & 的参数更快更简单!

你的函数将变成:

f() {
    local cmdArgs=(-o -) Url="$1" IFS='&'
    shift
    [[ $* ]] && cmdArgs+=(--data "$*")
    curl "${cmdArgs[@]}" $Url
}

注意:避免使用大写的变量名!优先选择驼峰式或小写形式!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接