使用空格将参数传递给 bash -c

Passing arguments to bash -c with spaces

我将程序的命令行参数存储在 bash 数组中,其中一个参数可以包含空格。 X=(A B "C D" E)

我有另一个约束,我需要通过 bash -c 语句来执行我的程序。 (从技术上讲,它是一个 ... | xargs bash -c "./a.out ..." 语句)

当我这样做时,我最终破坏了命令中的空格。我怎样才能正确地做到这一点?

我写了一个快速的 c++ 程序来输出命令行参数来表示我的程序

#include <iostream>
int main(int argc, char** argv)
{
    for (int i = 1; i < argc; ++i)
        std::cout << '"' << argv[i] << '"' << std::endl;
    return 0;
}

我正在寻找 ./a.out "${X[@]}"

的输出
"A"
"B"
"C D"
"E"

但是当我测试这个 bash -c "./a.out ${X[@]}" 我只是得到

"A"

当我测试这个 bash -c "./a.out ${X[*]}" 它破坏了空间,我得到

"A"
"B"
"C"
"D"
"E"

我能做什么?

正确的方法

从代码带外传递数据

由于您使用的是 xargs,请考虑通过 shell 本身的参数列表传递数据,而不是在使用 -c:

传递的脚本中传递数据
xargs bash -c './a.out "$@"' _

因为 "$@" 本身在单引号中,所以它不会被父 shell 扩展,而是由 xargs 传递其他参数的子 shell 扩展。 (_ 用作 [=19=] 的占位符)。

即使在参数数量固定的情况下,这实际上也是最佳实践:它有可能通过直接替换到脚本文本中的恶意数据进行 shell 注入攻击。


使用 printf %q 创建 eval 安全内容

ksh、bash 和 zsh 在 printf 中包含一个 %q 运算符,它以 eval 安全的方式引用内容:

printf -v out_str '%q ' "${X[@]}"
bash -c "./a.out $out_str"

为什么错误的方法不起作用

bash -c './a.out "${X[@]}"'

"${foo[@]}" 内部 字符串中扩展时,结果是一个数组,其中扩展之前的内容放在第一个元素之前,其他元素在最后一个元素之前独立发出,最后一个字符串之后的任何内容附加到最后一个元素。因此 bash -c "./a.out begin${X[@]}end" 扩展为:

bash -c "./a.out beginA" "B" "C D" "Eend"

...因此,BC DE 都传递给您的 shell,但在 -c 参数之外;它们可以通过 "$@" 访问,或者查看 </code>、<code>


bash -c './a.out ${X[*]}'

相比之下,使用 "${foo[*]},结果是在每个数组元素之间替换 IFS 的第一个字符(默认情况下,一个简单的 space)。因此:

bash -c "./a.out ${X[*]}"

...变成...

bash -c "./a.out A B C D E"

...这样 CD 之间的 space 文字与扩展过程中放置​​在其他字符之间的文字 space 无法区分。