【问题标题】:Passing arguments to bash -c with spaces使用空格将参数传递给 bash -c
【发布时间】:2023-03-18 19:32:01
【问题描述】:

我将程序的命令行参数存储在 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"

我能做什么?

【问题讨论】:

  • 顺便说一句,我认为这是 BashFAQ #50 的一个特例;见mywiki.wooledge.org/BashFAQ/050
  • 顺便说一句,您是否希望我的回答涉及为什么您的尝试会按照他们的方式行事,而不是只讨论正确的事情?
  • (说到正确的事情,一个更简单的以文字形式显示参数的测试程序将是一个 bash 脚本,在 shebang 之后有一行:printf '%q\n' "$@";您可以使用 '"%s"\n' 获得等效行为,但该输出格式不明确,因为参数可以包含引号和换行符)。
  • 谢谢。我会读一读。我在玩 IFS,试图让空间不混合,但感觉不对。我的同事刚刚说“摆脱空间”,但感觉就像失去了学习的机会。
  • 如果目标是学习,我将扩展答案以详细了解您尝试过的事情的行为方式。

标签: bash shell spaces


【解决方案1】:

正确的方法

从代码带外传递数据

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

xargs bash -c './a.out "$@"' _

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

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


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

ksh、bash 和 zsh 包含一个 %q 运算符到 printf,它以 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 参数之外;可以通过"$@" 访问它们,或者查看$1$2 等。


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

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

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

...变成...

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

...这样CD 之间的空格文字就无法与扩展过程中放置​​在其他字符之间的文字空格区别开来。

【讨论】:

  • 哦。我错过了你正在使用 xargs。因此,最佳实践是根本不替换到您生成的脚本内容中。我将扩展一点以使其更明确。
  • 哦,谢谢。我一直在使用 printf 测试诸如 printf "'%q' " "./a.out begin${X[@]}end" 之类的命令以查看示例的输出
  • 我想我已经得到了尽可能完整的答案——如果有任何遗漏或不清楚的地方,请告诉我。
  • X 不是 xargs 的输入,而是告诉我的 a.out 如何表现的参数。例如,我从午夜开始将秒数转换为纪元时间点,因此我在日期上有一个外部循环,在其中我用午夜纪元秒数填充 X 并通过 xargs 有效地将所有时间偏移回显到 a.out 中,这将添加正确的午夜每一个。
  • @JamesAndrews, printf '%s\0' 20151101 20151102 | xargs -0 echo 将是一种更简单的写法。一般来说,我还认为,如果没有令人信服的理由,最好避免使用 xargs-I 参数。 255 字节的限制... ick.
猜你喜欢
  • 2016-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-05
  • 2018-04-20
  • 2017-09-04
  • 1970-01-01
相关资源
最近更新 更多