我正在从Shell脚本中调用可执行文件。我正在编码的场景有所不同,但是我编写了一个虚拟的可执行文件和一个脚本来显示我所面临的情况
脚本:
#!/bin/sh -h
MYARGS=""
for arg in "$@"; do
shift
case $arg in
*)
pattern=" "
if [[ $arg =~ $pattern ]]; then
MYARGS="$MYARGS \"$arg\""
else
MYARGS="$MYARGS $arg"
fi
;;
esac
done
echo $MYARGS
./a.out $MYARGS
程序:
#include "iostream"
int main (int argc, char *argv[])
{
for (int i = 0; i < argc; i++)
{
std::cout << argv[i] << std::endl;
}
}
现在,我不想使用$ @,因为我必须过滤一些要传递给主程序的参数。 但是,当我尝试用空格分隔的字符串作为参数的上述片段时,得到以下输出:
Command : ./a.out "a b"
Output :
./a.out
a b
Command: a.sh "a b"
Output :
"a b"
./a.out
"a
b"
问题在于,在第二种情况下,即使我保留引号“”,参数也会自动拆分,并且“ a”和“ b”会作为不同的参数出现
对此有什么解决方案?
答案 0 :(得分:3)
一旦引号进入bash变量,它就只是一个字符。不要将您的类型与变量包含的内容混淆。在这方面,bash与C没什么不同。
const char* v = "a b";
printf("%s\n", v); // => a b
const char* w = "\"a b\"";
printf("%s\n", w); // => "a b"
重击是一样的:
$ v="a b"
$ echo "$v"
a b
$ w="\"a b\""
$ echo "$w"
"a b"
在上面,我在变量扩展名两边加上了引号,您应该总是这样做。 (嗯,几乎总是如此。但是除非您有充分的理由,并且可以解释您的理由,否则请这样做。)
我引用扩展的原因是为了防止扩展后字符串的内容被单词拆分。单词拆分将字符串在空格处拆分为单独的单词(除非$ IFS设置为其他内容)。而已。它不会在字符串中查找特殊字符,因为没有任何特殊字符。因为一旦在字符串中加上引号,它就只是另一个字符。 (所有其他特殊字符,例如$
和\
也是如此。
您可以(人们可能会建议您)使用eval
或类似eval的操作(例如bash -c
)来使用复杂的结构。这些几乎总是以悲痛告终。最简单,最佳和推荐的解决方案是使用数组:
myargs=()
for arg in "$@"; do
shift
case "$arg" in
*) myargs+=("$arg") ;;
esac
done
echo "${myargs[@]}"
./a.out "${myargs[@]}"
请注意,以上代码段中的每个引号是必不可少的(case "$arg"
中的引号除外),它们都有相同的目的:避免在变量(或变量)时分词数组元素)展开。 (case
单词的例外是,因为在这种情况下外壳程序不进行单词拆分扩展。但是您无需记住这一点;只需使用如图所示的引号即可。)
答案 1 :(得分:1)
只需使用xargs
#!/bin/bash
MYARGS=""
for arg in "$@"; do
shift
case $arg in
*)
pattern=" "
if [[ $arg =~ $pattern ]]; then
MYARGS="$MYARGS \"$arg\""
else
MYARGS="$MYARGS $arg"
fi
;;
esac
done
echo $MYARGS | xargs ./a.out