4

我正在编写一个 C 程序,该程序输出stdoutstderr. 该程序接受一个命令,例如:

./myprogram function_to_run file_to_read

我的程序既可以输出到stdout文件,也可以被定向到输出文件,但不能重定向到/dev/null. 例如:

./myprogram function_to_run file_to_read //OK
./myprogram function_to_run file_to_read > file.txt //OK
./myprogram function_to_run file_to_read > /dev/null // NOT OK, should produce error in stderr

我尝试使用isatty(1),但它只能检测是否stdout正在输出到终端。因此,对于stdout重定向到文件的情况,它会失败,这在我的情况下是可以接受的

有没有办法在C中检查这个?如果没有,有什么建议我可以检查 /dev/null 场景吗?

4

3 回答 3

5

如果您只对 *nix 系统感兴趣,那么一种解决方案是检查/proc/self/fd/1链接到的内容。下面是一个执行此操作的示例程序(为简洁起见,省略了错误检查)。

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>

int main (void)
{
    char link[256];
    ssize_t rval;
    rval = readlink("/proc/self/fd/1", link, sizeof(link));
    link[rval] = '\0';

    if (!strcmp(link, "/dev/null")) {
        assert(!"Redirect to /dev/null not allowed!");
    } else {
        printf("All OK\n");
    }

    return 0;
}

示例测试运行:

$ ./a.out
All OK
$ ./a.out > some_file
$ cat some_file
All OK
$ ./a.out > /dev/null
a.out: test.c:14: main: Assertion `!"Redirect to /dev/null not allowed!"' failed.
Aborted (core dumped)
$
于 2016-01-26T06:29:24.253 回答
2

检查标准输出是否被重定向到的一种快速方法是/dev/null检查它们是否STDOUT_FILENO/dev/null具有相同的 inode:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>

int main()
{
    struct stat std_out;
    struct stat dev_null;
    if (fstat(STDOUT_FILENO, &std_out) == 0 &&
        S_ISCHR(std_out.st_mode) &&
        stat("/dev/null", &dev_null) == 0 &&
        std_out.st_dev == dev_null.st_dev &&
        std_out.st_ino == dev_null.st_ino)
    {
        fprintf(stderr, "Redirect to /dev/null not allowed!\n");
        exit(EXIT_FAILURE);
    }
    fprintf(stderr, "All OK\n");
    return 0;
}

检查 inode 可移植到所有类 Unix 系统:

$ ./a.out
All OK
$ ./a.out | cat
All OK
$ ./a.out > /dev/null
Redirect to /dev/null not allowed!

我们不应该依赖/proc/self/fd/1. 并非所有类 Unix 系统都支持它,尤其是 Mac OS X Darwin 和一些 BSD 变体。

于 2019-08-26T15:33:58.073 回答
1

有没有办法在C中检查这个?

不,没有。重定向的文件stderr由运行程序的 shell 控制。C 程序对此一无所知。

如果没有,有什么建议我可以检查 /dev/null 场景吗?

您可以更改程序以接受第二个参数并将其用作stderrusing的目标freopen。如果第二个参数是/dev/null,您可能会出错。

if ( strcmp(argv[2], "/dev/null") == 0 )
{
   // Deal with error.
   return EXIT_FAILURE;
}

if (freopen(argv[2], "w", stderr) == NULL)
{
   perror("freopen() failed");
   return EXIT_FAILURE;
}
于 2016-01-26T06:16:42.823 回答