隐藏 sh: -c 在 c++ Linux 中调用“system”时的错误消息

Hide sh: -c error messages when calling “system” in c++ Linux

本文关键字:system 消息 错误 调用 sh Linux c++ 隐藏      更新时间:2023-10-16

我正在使用系统来执行带有参数的命令。我不想使用exec/fork。当我的命令中有不匹配的引号时,会出现此错误:

sh: -c: line 0: unexpected EOF while looking for matching `''
sh: -c: line 1: syntax error: unexpected end of file

如何抑制这些外壳的错误消息? 我尝试在无效命令的末尾添加>/dev/null 2>&1,但它没有禁止显示 shell 错误消息。 对于后台,我正在运行用户提供的命令,这些命令可能有效,也可能无效。 我无法提前知道它们是否有效,但无论如何我都想抑制错误消息。

下面是生成我试图禁止显示的错误类型的代码示例:

int main()
{
   // This command is meant to be invalid as I'm trying to suppress the shell syntax error message
   system("date ' >/dev/null 2>&1");
   return 0;
}

你可以帮我吗?

认为system分叉一个进程,然后执行你提供的命令。新进程从其父进程继承描述符,并且该新进程正在写入其标准错误。

因此,此代码片段可能会执行您想要的操作:

#include <stdlib.h>
#include <unistd.h>
int main()
{
    int duperr;
    duperr = dup(2);
    close(2); /* close stderr so the new process can't output the error */
    system("date '");
    dup2(duperr, 2);
    close(duperr);
    /* here you can use stderr again */
    write(2, "hello worldn", 12);
    return 0;
}

要以静默方式抑制对 stderr 的写入,您可以将错误输出到 /dev/null

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
int main(void) {
    int devnull_fd, duperr_fd;
    /* get a descriptor to /dev/null (devnull_fd) */
    devnull_fd = open("/dev/null", O_WRONLY | O_APPEND);
    /* save a descriptor "pointing" to the actual stderr (duperr_fd) */
    duperr_fd = dup(STDERR_FILENO);
    /* now STDERR_FILENO "points" to "/dev/null" */
    dup2(devnull_fd, STDERR_FILENO); 
    system("date '");
    /* restore stderr */
    dup2(duperr_fd, STDERR_FILENO);
    close(duperr_fd);
    close(devnull_fd);
    /* here you can use stderr again */
    write(STDERR_FILENO, "hello worldn", 12);
    return 0;
}

请记住检查函数调用的返回值。

逻辑问题是您没有重定向任何内容,因为>/dev/null 2>&1部分最终包含在单引号部分中。

然而,字符串没有结束,因此 bash 在 stderr 上抱怨。

这种特定情况的解决方案是通过在单引号前面加上反斜杠来引用单引号,即调用 system("date ' >/dev/null 2>&1") 。但请注意,bash精确引用规则是一场小噩梦。

我通常可以想到的一种解决方法是将命令保存在文件中,例如cmd.txt,然后执行

system("bash < cmd.txt >/dev/null 2>&1");

也许这也可以用bash -c在不创建文件的情况下完成,但我根本无法理解-c的单引号转义规则,我不会在完全破碎的bash语法上浪费神经元。

您可能希望运行正确的命令,然后删除引号,因此

  system("date >/dev/null 2>&1");

但是,运行它有什么意义呢?如果(您的PATH足够常见,以便... date确实是/bin/date(参见 date(1)...),它的唯一效果是产生一些输出,而您通过将其重定向到 /dev/null 来丢弃它。因此,调用system的唯一可见效果是花费数百万个 CPU 周期。当然,在奇怪的情况下,它可能会失败(例如,因为/bin/sh不存在,/bin/date不存在,fork失败了,等等......

也许你正在编写一个外壳。然后你应该解析命令并做forkexecve.....

如果你想获取一些命令的输出,比如 date,最好使用 popen(3) 和 pclose 。在 date 的特殊情况下,使用 time(2)、localtime(3)、strftime(3) 要好得多(这样你就不需要依赖外部命令,也不需要使用 popen

如果你真的想抑制由system启动的shell的输出,你可以自己做forkexecvebin/sh -c),并使用dup2重定向标准和错误输出(到文件描述符打开/dev/null)。参见 daemon(3) 和 wordexp(3)

也许在你的应用程序中嵌入一个解释器(例如.lua或诡计)会更明智(假设你不关心用户错误)