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

ci7*_*en4 31 c bash quotes argv

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

这有效:

./program -m "Hello, world!"
Run Code Online (Sandbox Code Playgroud)

这不起作用:

./program -m "Hello, world!!!!"
Run Code Online (Sandbox Code Playgroud)

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

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

./program -m 'Hello, world!!!!'
Run Code Online (Sandbox Code Playgroud)

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

Hello, world!!!!
Run Code Online (Sandbox Code Playgroud)

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

所以,我的问题是:

  • 为什么在字符串中使用多个感叹号时会出现这种奇怪的行为?
  • 据我所知,在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);
}
Run Code Online (Sandbox Code Playgroud)

我已经尝试先将内容复制argv[2]char*缓冲区中并附加一个缓冲区'\0',但不会改变任何内容.

dbu*_*ush 68

这与您的代码无关,而与启动它的shell无关.

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

这对你的程序意味着所有这一切都发生你的程序执行之前,所以除了检查传入的字符串是否有效之外,程序没有多少办法.

相反,当你使用单引号的外壳并没有做任何换人并将该字符串传递给程序不变.

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

  • @ ci7i2en4这取决于你的用户要弄明白.也许他们希望这种扩张能够发生,也许不是. (13认同)
  • 我会评论任何改变shell预期行为的解决方案都可能是一个坏主意.用户应该知道如何在他们正在使用的shell中发送输入. (8认同)
  • @ ci7i2en4,在Bash中,`set + o histexpand`或`set + H`来禁用历史记录扩展.其他shell可能有其他设置. (2认同)

Som*_*ude 9

shell在双引号字符串中进行扩展.如果您阅读Bash手册页(假设您使用Bash,这是大多数Linux发行版的默认设置),那么如果您查看历史扩展部分,您将看到这!!意味着

请参阅上一个命令.

所以!!!!在你的双引号字符串中会扩展到上一个命令,两次.

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

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


Unc*_*arl 8

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

echo ./program -m "Hello, world!!!!"
Run Code Online (Sandbox Code Playgroud)

这会让你有些陌生,可能会帮助你引导你朝着正确的方向前进.