在 bash 扩展中使用感叹号

在 bash 扩展中使用感叹号

在 bash 中,我将 svn 命令构建为字符串,其中动态添加了用户名和密码选项。某些用户的密码中可能包含感叹号,这可能会导致以下情况:

svncmd='svn update --username=user --password password\! --non-interactive'
$($svncmd)

但是这会扩大我的感叹号,这不是我想要的。我错过了什么?

编辑:

啊,我想我知道发生了什么。使用此命令,我可以像 $svncmd 而不是 $($svncmd) 一样运行它。发生的事情是,当 bash 尝试运行“At revision xxxxx”作为命令时,它抱怨 bash: At: command not found...。哎呀。

答案1

将命令存储在变量中然后按预期执行它很棘手,因为 shell 解析命令行的顺序是这样的。它解析(并应用和删除)引号和转义符,然后替换变量,然后进行分词和通配符扩展,但不会返回并解释替换变量值中的转义符和引号。因此,如果存储在变量中的命令有引号或转义符,它们将不会产生预期的效果。例如,假设用户的密码是“foo * bar”,如果您将命令设置为svncmd='svn update --username=user --password foo * bar --non-interactive',然后将其作为执行$svncmd,它将以“foo”作为密码,然后获取当前目录中的文件列表,后跟“bar”作为单独的参数。另一方面,如果您将命令存储为svncmd='svn update --username=user --password "foo * bar" --non-interactive',它将以“foo”作为密码,然后获取文件列表,后跟“bar”......

在 bash 中处理此问题的最佳方法通常是将命令存储为一个数组(其中命令的每个“单词”都是一个数组元素),然后将其作为"${svncmd[@]}"--[@]调用,使其将每个元素视为一个命令词,双引号可防止在替换数组元素后对其进行任何额外的解析。因此,您可以构建类似这样的命令:

svncmd=(svn update --username=user)
if [ $pwdfromvar ]; then
    svncmd+=(--password "$userpw")  # Double-quotes allow variable substitution, prevent parsing of the substituted value
elif [ $defaultpw ]; then
    svncmd+=(--password 'password!')  # Single-quotes prevent the ! from being interpreted
else
    IFS= read -rs -p "Password:"  # Raw read without echo
    echo
    svncmd+=(--password "$REPLY")
fi
svncmd+=(--non-interactive)

"${svncmd[@]}"

相关内容