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

问题描述 投票:0回答:3

我编写了一个小程序,它从

*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'
,这没有改变任何内容。

bash quotes argv
3个回答
68
投票

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

在大多数 shell 中,

!!
是最后运行的命令的简写。当您使用双引号时,shell 允许在字符串中进行历史扩展(以及变量替换等),因此当您将
!!
放入双引号字符串内时,它会替换最后运行的命令。

这对您的程序意味着所有这些都发生在您的程序执行之前,因此除了检查传入的字符串是否有效之外,程序无能为力。

相反,当您使用单引号时,shell

不会进行任何替换,并且字符串会不经修改地传递给程序。

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


9
投票
shell 在双引号字符串中进行扩展。如果您阅读

Bash 手册页(假设您使用 Bash,这是大多数 Linux 发行版的默认设置),那么如果您查看 历史扩展部分,您将看到 !!

 的意思是

请参阅上一条命令。

因此,双引号字符串中的

!!!!

 将扩展为上一个命令,两次。

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

所以问题不在你的程序内部,而是由于调用你的程序的环境(shell)造成的。


8
投票
除了提供的答案之外,您还应该记住 echo 是您的 shell 朋友。如果您的命令带有“echo”前缀,您将看到 shell 实际发送到您的脚本的内容。

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

这会给你带来一些奇怪的感觉,并可能帮助你走向正确的方向。

© www.soinside.com 2019 - 2024. All rights reserved.