防止Bash破坏带引号的字符串

时间:2019-01-31 17:00:31

标签: bash

我写了一个shell脚本agit,我用它来循环遍历子目录(使用pushd / popd),并对每个目录执行git命令。当我尝试使用-m开关进行提交时,遇到了麻烦。如果我输入

 agit --debug -u commit -a -m "This is a test commit"

--debug导致set -x就在git调用之前,而-u仅对未提交更改的子目录执行调用。

我回来了

> git commit -a -m "This is a test commit"

+ git commit -a -m '"This' is a test 'commit"'
fatal: Paths with -a does not make sense.

第一行只是一个echo命令,显示了我期望发生的事情,第二行是Bash实际处理的命令。我认为错误是因为仅将-m之后的第一个单词视为一条消息,而将其余单词视为路径。

作为命令解析的一部分,我通读了每个参数,找到了所有包含空格的地方,并将它们用引号引起来。

while [[ ${#@} > 0 ]]; do
    _1=${1}
    if [[ "${1}" == *" "* ]]; then
        _1="\"${1}\""
    fi
    _AT="$(append "${_AT}" "${_1}")"
    shift
done

在遍历每个子目录时,我只使用

git ${_AT}

我尝试使用sed来转义空格,改用单引号,并且完全不更改参数,所有这些都具有相同的结果。

我能做些什么来防止bash破坏包含空格的参数吗?

您可以在pastebin上看到完整的脚本。

1 个答案:

答案 0 :(得分:1)

使用printf "%q"包装参数。就是为了这个(如果您必须这样做...):

rick() {
    local idx=1 i
    for i; do
        echo "\$$idx=\"$i\""
        ((idx++))
    done
}

morty_pass() {
    eval rick $(printf "%q " "$@")
}

morty_pass "this is a test commit" "second arg"

将输出:

$1="this is a test commit"
$2="second arg"

根据您的命令:

agit --debug -u commit -a -m "This is a test commit"

您可能只使用了两个班次。然后,您可以将"$@"传递给git commit。

agit() { 
     arg="$1" # --debug
     # parse debug
     shift;
     arg="-u"
     shift
     git "$@" # will run as expected
}

如果需要,请使用bash数组:

 args=()
 while (($#)); do
      args+=("$1")
      shift
 done 
 git "${args[@]}"

但是实际上这仅仅是:

args=("$@")
git "${args[@]}"

请注意,${#@}甚至可以使我感到惊讶。看起来像个有趣的bash怪癖。仅将$#用于参数计数。