使用空格将参数传递给 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"
...因此,B
、C D
和 E
都传递给您的 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"
...这样 C
和 D
之间的 space 文字与扩展过程中放置在其他字符之间的文字 space 无法区分。
我将程序的命令行参数存储在 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"
...因此,B
、C D
和 E
都传递给您的 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"
...这样 C
和 D
之间的 space 文字与扩展过程中放置在其他字符之间的文字 space 无法区分。