Код C, чтобы проверить, перенаправлена ​​ли командная строка на /dev/null

Я пишу программу на C, которая выводит stdout, а ошибки — stderr. Программа принимает такую ​​команду, как:

./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?


person LKT    schedule 26.01.2016    source источник
comment
Что вы пытаетесь решить? Если пользователь хочет отказаться от вывода вашей программы, он может это сделать, даже если вы каким-то образом отключите /dev/null. Например, канал к /tmp/junk, а затем к rm /tmp/junk. Или просто немного запутать ./myprogram | cat > /dev/null.   -  person Thilo    schedule 26.01.2016
comment
Вот одна из идей: 1. узнать pid программы, 2. затем проверить /proc/‹pid›/fd, чтобы увидеть, есть ли какая-либо ссылка на /dev/null.   -  person Eric    schedule 26.01.2016
comment
Обратите внимание, что ваша проверка снизит удобство использования вашей программы или продемонстрирует неоправданное высокомерие в отношении ценности ее вывода. Могут быть причины, по которым людям нужно отказаться от вывода. Если вы усложните им задачу, это не поможет вам завоевать друзей. Это действительно не должно иметь значения для вас. Если бы вы хотели оптимизировать свою программу, чтобы она ничего не писала при подключении к /dev/null, я полагаю, вы могли бы это сделать, но это исключило бы возможное допустимое использование программы. В принципе, вы почти наверняка не должны этого делать.   -  person Jonathan Leffler    schedule 26.01.2016
comment
Вариант использования для этого находится в Android. Если вы запускаете что-то из оболочки, вы хотите, чтобы ведение журнала шло на терминал или в перенаправленный файл. Если он запускается как демон, стандартный вывод будет /dev/null, и в этом случае использование ведения журнала Android (logcat) будет хорошим обходным путем.   -  person Renate    schedule 17.11.2017


Ответы (3)


Если вас интересуют только *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)
$
person kaylum    schedule 26.01.2016
comment
Спасибо @kaylum. Это именно то, что я ищу, и это отлично работает для моего сценария, описанного выше. Кстати, я работаю в системе Unix, извините, что не упомянул об этом в вопросе. - person LKT; 26.01.2016
comment
Хм, конечно, это сработает, но ИМХО это действительно плохая практика. Правило должно быть таким: вызывающий знает, куда должен идти вывод, а не вызываемый. Даже при использовании syslog вы можете перенаправить почти что угодно куда угодно (включая /dev/null) - person Serge Ballesta; 26.01.2016
comment
Обратите внимание, что некоторые Unix-подобные системы вообще не имеют файловой системы /proc. Есть различия в деталях тех, у кого он есть. - person Jonathan Leffler; 26.01.2016

Быстрый способ проверить, что стандартный вывод перенаправляется на /dev/null, состоит в том, чтобы проверить, что STDOUT_FILENO и /dev/null являются устройствами с одним и тем же индексным узлом:

#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;
}

Проверка инодов переносима на все 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.

person Dr. Alex RE    schedule 26.08.2019

Есть ли способ проверить это в C?

Нет, нет. Файл, в который перенаправляется stderr, управляется оболочкой, которая запускает программу. Программа C ничего об этом не знает.

Если нет, есть ли какие-либо предложения, как я могу проверить сценарий /dev/null?

Вы можете изменить свою программу так, чтобы она принимала второй аргумент и использовать его в качестве адресата stderr, используя 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;
}
person R Sahu    schedule 26.01.2016
comment
Программа C может узнать, кто является владельцем ссылок stdin, stdout и stderr, проверив /proc/self/fd/[0-2]. - person ; 05.01.2017