传递包含“!!!!”的字符串时argv的奇怪行为

时间:2018-02-08 13:46:52

标签: c bash quotes argv

我编写了一个小程序,它从*argv[]获取一些输入参数并打印出来。几乎在所有用例中,我的代码都运行得很好。只有当我想在传递的字符串末尾使用多个感叹号作为参数时,才会出现问题......

这有效:

./program -m "Hello, world!"

这不起作用:

./program -m "Hello, world!!!!"

^^如果我这样做,程序输出要么是该字符串的两倍,要么是我在./program之前输入的命令。

但是,我绝对不明白:以下,奇怪的是,它可以工作:

./program -m 'Hello, world!!!!'

^^输出正是......

Hello, world!!!!

......正如所希​​望的那样。

所以,我的问题是:

  • 为什么在字符串中使用多个感叹号时会出现这种奇怪的行为?
  • 据我所知,在C语言中,您使用""表示字符串,''表示单个字符。那么为什么我在使用''时会得到所需的结果,而不是在我应该使用""时(根据我的理解)?
  • 我的代码中是否存在错误,或者我需要更改哪些内容才能输入任何字符串(无论是否,使用了什么以及使用了多少个标点符号)并准确打印出该字符串?

我的代码的相关部分:

// this is a simplified example that, in essence, does the same 
// as my (significantly longer) code
int main(int argc, char* argv[]) {
    char *msg = (char *)calloc(1024, sizeof(char));

    printf("%s", strcat(msg, argv[2])); // argv[1] is "-m"

    free(msg);
}

我已经尝试首先将argv[2]的内容复制到char*缓冲区并向其添加'\0',但这并没有改变任何内容。

3 个答案:

答案 0 :(得分:68)

这与你的代码无关,而是与启动它的shell有关。

在大多数shell中,!!是最后一个运行命令的简写。当你使用双引号时,shell允许在字符串中使用history expansion(以及变量替换等),所以当你将!!放在双引号字符串中时,它会替换最后一个命令运行。

这对你的程序来说意味着所有这一切都发生在之前你的程序执行,所以除了检查传入的字符串是否有效之外,程序不能做什么。

相反,当你使用单引号时,shell会进行任何替换,并且字符串将被未经修改地传递给程序。

因此您需要使用单引号来传递此字符串。如果用户不希望发生任何替换,他们需要知道这一点。另一种方法是创建一个包装器shell脚本,提示用户输入字符串,然后脚本将使用正确的参数调用您的程序。

答案 1 :(得分:9)

shell在双引号字符串中进行扩展。如果您阅读the Bash manual page(假设您使用Bash,这是大多数Linux发行版的默认设置),那么如果您查看the History Expansion section,您会看到!!表示

  

请参阅上一个命令。

因此,双引号字符串中的!!!!将扩展为上一个命令,两次。

这种扩展不适用于单引号字符串。

所以问题不在你的程序中,这是由于环境(shell)调用你的程序。

答案 2 :(得分:8)

除了提供的答案之外,你应该记住echo是你的shell好友。如果您在命令前加上" echo",您将看到实际发送到您的脚本的shell。

echo ./program -m "Hello, world!!!!"

这会让你有些陌生,可能会帮助你引导你走向正确的方向。