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

Strange behavior of argv when passing string containing "!!!!"

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

这个有效:

./program -m "Hello, world!"

这不起作用:

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

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

然而,我完全不明白的是:以下,奇怪的是,确实有效:

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

^^ 输出正好是...

Hello, world!!!!

...随心所欲

所以,我的问题是:

我代码的相关部分:

// 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* 缓冲区并向其附加 '[=22=]',但没有任何改变。

shell 对双引号字符串进行扩展。如果你阅读 the Bash manual page (assuming you use Bash, which is the default on most Linux distributions) then if you look at the History Expansion section 你会发现 !! 意味着

Refer to the previous command.

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

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

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

这与您的代码无关,但与启动它的 shell 相关。

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

这对您的程序意味着所有这些都发生在 之前 您的程序被执行,所以除了检查传入的字符串是否是有效。

相比之下,当您使用单引号时,shell 不会 进行任何替换,并且字符串会原封不动地传递给程序。

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

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

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

这会向您展示一些奇怪的地方,并可能帮助您朝着正确的方向前进。