我正在制作一个bash脚本,它将打印并将复杂的参数传递给另一个外部程序。
./script -m root@hostname,root@hostname -o -q -- 'uptime ; uname -a'
如何打印原始参数:
-m root@hostname,root@hostname -o -q -- 'uptime ; uname -a'
使用$@
和$*
删除uptime ; uname -a
周围的单引号,这可能会导致意外结果。我的脚本不需要解析每个参数。我只需要打印/记录参数字符串并将它们传递给另一个程序,确切地说它们是如何给出的。
我知道我可以使用类似"'uptime ; uname -a'"
的内容来逃避引号,但我无法保证用户会这样做。
答案 0 :(得分:14)
在将参数传递给脚本之前删除了引号,因此保留它们为时已晚。你可以做的是在将参数传递给内部命令时保留它们的效果,并重建用于打印的参数的等效引用/转义版本。
为了将参数传递给内部命令"$@"
- 使用双引号,$ @保留原始单词分隔符,这意味着内部命令接收与脚本完全相同的参数列表。
对于打印,您可以在bash的printf命令中使用%q格式来重建引用。请注意,这并不总是重建原始引用,但会构造一个等效的引用/转义字符串。例如,如果您传递参数'uptime ; uname -a'
,则可能会打印uptime\ \;\ uname\ -a
或"uptime ; uname -a"
或任何其他等效项(请参阅@William Pursell对类似示例的回答)。
以下是使用这些的示例:
printf "Running command:"
printf " %q" innercmd "$@" # note the space before %q -- this inserts spaces between arguments
printf "\n"
innercmd "$@"
答案 1 :(得分:4)
如果要打印参数列表尽可能接近用户可能输入的内容:
#!/bin/bash
chars='[ !"#$&()*,;<>?\^`{|}]'
for arg
do
if [[ $arg == *\'* ]]
then
arg=\""$arg"\"
elif [[ $arg == *$chars* ]]
then
arg="'$arg'"
fi
allargs+=("$arg") # ${allargs[@]} is to be used only for printing
done
printf '%s\n' "${allargs[*]}"
这不完美。像''\''"'
这样的论点比合理的更难以容纳。
答案 2 :(得分:3)
如果用户调用您的命令:
./script 'foo'
给脚本的第一个参数是不带引号的字符串foo
。您的脚本无法区分该脚本以及可以将foo
作为参数的任何其他方法(例如./script $(echo foo)
或./script foo
或./script "foo"
或./script \f\o""''""o
)。
答案 3 :(得分:1)
正如其他人已经提到的那样,当您访问脚本内部的参数时,现在知道调用哪些参数被引用为时已晚。但是,您可以重新引用包含空格或其他特殊字符的参数,这些参数需要引号作为参数传递。
这是一个基于Python's shlex.quote(s)
的Bash实现,它执行以下操作:
function quote() {
declare -a params
for param; do
if [[ -z "${param}" || "${param}" =~ [^A-Za-z0-9_@%+=:,./-] ]]; then
params+=("'${param//\'/\'\"\'\"\'}'")
else
params+=("${param}")
fi
done
echo "${params[*]}"
}
您的示例略有更改,以显示空参数:
$ quote -m root@hostname,root@hostname -o -q -- 'uptime ; uname -a' ''
-m root@hostname,root@hostname -o -q -- 'uptime ; uname -a' ''
答案 4 :(得分:0)
eval "./script -m root@hostname,root@hostname -o -q -- 'uptime ; uname -a'"
答案 5 :(得分:-1)
使用$ {@@ Q}作为简单的解决方案。要进行测试,请在脚本bigQ中放置以下行。
CngKey key;
bool keyExists = CngKey.Exists(KEY_NAME);
if (!keyExists)
{
CngKeyCreationParameters parameters = new CngKeyCreationParameters()
{
ExportPolicy = CngExportPolicies.AllowPlaintextExport,
Provider = CngProvider.MicrosoftSoftwareKeyStorageProvider,
KeyUsage = CngKeyUsages.AllUsages
};
CngProperty property = new CngProperty("Length", BitConverter.GetBytes(RSA_KEY_SIZE), CngPropertyOptions.None);
parameters.Parameters.Add(property);
key = CngKey.Create(CngAlgorithm.Rsa, KEY_NAME, parameters);
}
答案 6 :(得分:-1)
您不需要保留引号,而只需要保留参数分隔。
对于传递参数为:"${@}"
如果您需要从函数返回参数,请使用 for 循环:
for arg in "${args[@]}"; do
echo "${arg}"
done
并在接收器中重建为数组:
readarray -t args < <(function "${@}")
如果这还不够,将每个单独的论点引用为:"${@@Q}"