无法将 space 分隔的参数从 shell 脚本传递给 C++ 可执行文件
Cannot pass space separated argument from shell script to C++ executable
我正在从 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;
}
}
现在,我不想使用 $@,因为我必须过滤一些要传递给主程序的参数。
但是,当我尝试使用 space 分隔字符串作为参数时,我得到以下输出:
Command : ./a.out "a b"
Output :
./a.out
a b
Command: a.sh "a b"
Output :
"a b"
./a.out
"a
b"
问题是,在第二种情况下,参数会自动拆分,并且 'a' 和 'b' 作为不同的参数出现,即使我保留了引号 ""
有什么解决办法?
只需使用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
一旦引号进入 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"
Bash 是一样的:
$ v="a b"
$ echo "$v"
a b
$ w="\"a b\""
$ echo "$w"
"a b"
在上面,我在我的变量扩展周围加上了引号,你应该总是这样做。 (好吧,几乎总是这样。但是除非你有充分的理由,并且可以解释你的理由,否则就这样做。)
之所以引用扩展是为了防止扩展后字符串的内容被分词。 Word-splitting 在空格处将字符串拆分为单独的单词(除非 $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
单词的例外是因为 shell 在该上下文中不进行单词拆分扩展。但是您不需要记住这一点;只需使用所示的引号即可。)
我正在从 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;
}
}
现在,我不想使用 $@,因为我必须过滤一些要传递给主程序的参数。 但是,当我尝试使用 space 分隔字符串作为参数时,我得到以下输出:
Command : ./a.out "a b"
Output :
./a.out
a b
Command: a.sh "a b"
Output :
"a b"
./a.out
"a
b"
问题是,在第二种情况下,参数会自动拆分,并且 'a' 和 'b' 作为不同的参数出现,即使我保留了引号 ""
有什么解决办法?
只需使用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
一旦引号进入 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"
Bash 是一样的:
$ v="a b"
$ echo "$v"
a b
$ w="\"a b\""
$ echo "$w"
"a b"
在上面,我在我的变量扩展周围加上了引号,你应该总是这样做。 (好吧,几乎总是这样。但是除非你有充分的理由,并且可以解释你的理由,否则就这样做。)
之所以引用扩展是为了防止扩展后字符串的内容被分词。 Word-splitting 在空格处将字符串拆分为单独的单词(除非 $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
单词的例外是因为 shell 在该上下文中不进行单词拆分扩展。但是您不需要记住这一点;只需使用所示的引号即可。)