Всегда ли /dev/null открываем?

Я хочу подавить некоторые fprintf, перенаправив целевой файл в /dev/null. Но могу ли я быть уверен, что fopen("/dev/null", "w"); НИКОГДА не возвращается NULL, Другими словами, можно ли каждый раз открывать этот "файл"?

Если это так, я мог бы использовать этот хороший троичный оператор:

FILE *whereToPrint = (strcmp(custom_topic, ERROR_TOPIC) == 0) ? fopen("/dev/null", "w") : stdout;

fprintf(whereToPrint, "Message sent!\n\n");

4 ответа

Решение

Да, возможно, что fopen не открывается /dev/null по какой-то причине, например, разрешение на /dev/null, Хотя это очень редко, но не может отрицать возможность.

Да, в правильно функционирующей системе, /dev/null доступен для записи в мире:

 ls -l /dev/null
 crw-rw-rw- 1 root root 1, 3 Jul 20  2017 /dev/null

Так что это всегда будет работать. Это не самый эффективный способ подавления вывода. Было бы лучше просто не пытаться писать, если вы этого не хотите... но если это не так много, это не будет иметь значения.

Кто-то указал, что это возможно для root установить права доступа /dev/null так что это не доступно для записи other, Или они могут полностью удалить устройство. Это правда.. но это приведет к поломке Unix. /dev/null должен иметь разрешения, как я показал выше.. он установлен таким образом и никогда не должен изменяться. Тем не менее, вы должны проверить возвращаемое значение fopen() или же open() при открытии любого файла.

Это возможно для /dev/null быть недоступным для записи или отсутствует. Я имею в виду, вы можете открыть корневую оболочку и набрать rm /dev/null и нажмите RETURN, и он будет весело идти дальше и удалить узел устройства.

Разумно для вашей программы потерпеть неудачу, если /dev/null недоступно для записи или отсутствует. Многие другие программы имеют это свойство. Но для вашей программы не имеет смысла слепо предполагать, что fopen("/dev/null", "w") успешно, если только это не разовая тестовая программа, которую никто, кроме вас, никогда не сможет запустить. Напишите две дополнительные строки для вызова perror а также exit если whereToPrint нулевой. Честно говоря, я бы включил это, даже если бы это была разовая тестовая программа, которую мог бы запустить только я. Это не более десяти дополнительных секунд печати, и кто знает? Может быть, проблема у меня в том, что какой-то глючный системный скрипт удалил /dev/null за моей спиной!

РЕДАКТИРОВАТЬ: Мне просто пришло в голову, что вы, возможно, не решаетесь проверить результат fopen не из-за лишней типизации, а потому, что вы не хотите испортить ваше "красивое троичное выражение". Там нет необходимости портить это; Вы просто ставите чек сразу после этого:

FILE *whereToPrint = (strcmp(custom_topic, ERROR_TOPIC) == 0) ? fopen("/dev/null", "w") : stdout;
if (!whereToPrint) {
    perror("/dev/null");
    exit(1);
}

Если whereToPrint был установлен равным stdout, тогда это не будет NULL и проверка будет успешной.

Если у вас есть целая куча этих тем об ошибках, вы должны поместить их и их имена в таблицу, чтобы вы могли зацикливать их при инициализации, а также нет необходимости открывать /dev/null более одного раза:

enum error_topic_codes { 
    ET_INPUT, ET_CRUNCHING, ET_FROBNICATING, ET_OUTPUT,
    ET_MISC
};
struct error_report_s {
    const char *label;
    FILE *fp;
};
static struct error_report_s error_destinations[] = {
    /* ET_INPUT */        { "input", 0 },
    /* ET_CRUNCHING */    { "crunching", 0 },
    /* ET_FROBNICATING */ { "frobnicating", 0 },
    /* ET_OUTPUT */       { "output", 0 },
    /* ET_MISC */         { "misc", 0 },
};

void error_report_init (const char *squelched)
{
    FILE *devnull = fopen("/dev/null", "w");
    if (!devnull) {
        perror("/dev/null");
        exit(1);
    }
    for (int i = 0; i <= ET_MISC; i++)
        error_destinations[i].fp =
            strstr(squelched, error_destinations[i].label)
            ? devnull : stderr;
    /* FIXME 2018-02-23: This leaks a FILE if none of the
       error categories are squelched.  */
}

Да, /dev/null всегда открывается - кроме случаев, когда это не так.

Это звучит глупо, но я не шучу. Если /dev/null не может быть открыт, у вас может быть сильно сломанная, возможно, пограничная нефункциональная система - но зная, что это не то же самое, что гарантия того, что файл открыт.

Всегда есть причина, по которой открытие файла может закончиться неудачей. Вы никогда не должны искать оправдания, чтобы не проверять возвращаемое значение fopen за неудачу.

Это может никогда не случиться, о чем вы знаете, это может никогда не произойти в правильно функционирующей системе, но спросите себя, что произойдет, если открытие /dev/null "невозможно" не удается?

  1. Если ваша программа проверяет fopen сбой, он напечатает сообщение как "Impossible error! Can't open /dev/null"и будет понятно, что происходит.

  2. Если ваша программа не может проверить fopen сбой, он таинственным образом вылетает при первой попытке что-то напечатать whereToPrint, и ваш пользователь будет задаваться вопросом, что пошло не так.

Программы, которые таинственным образом рушатся, являются Плохими. Программы, которые говорят вам, что происходит, это хорошо.

И чем больше вы сможете рассказать своему пользователю о происходящем, тем лучше. Я предложил печать "Impossible error! Can't open /dev/null"и это лучше, чем ничего, но на самом деле все еще значительно неполным. Вы должны действительно написать код, который ведет себя примерно так:

#include <stdio,h>
#include <string.h>
#include <errno.h>

FILE *whereToPrint;

if(strcmp(custom_topic, ERROR_TOPIC) != 0)
    whereToPrint  = stdout;
else if((whereToPrint = fopen("/dev/null", "w")) == NULL) {
    fprintf(stderr, "Impossible error! Can't open /dev/null: %s\n", strerror(errno));
    exit(1);
}

Теперь, в тот "невозможный" случай, когда он терпит неудачу, он скажет вам, почему он не мог открыть /dev/nullи это может быть фантастически полезной информацией. Это может напечатать

Impossible error! Can't open /dev/null: No such file or directory

если /dev/null как-то не существует. Или это может напечатать

Impossible error! Can't open /dev/null: Permission denied

если, как другие предложили, кто-то неправильно ограничил права на /dev/null в вашей системе. Или это может напечатать

Impossible error! Can't open /dev/null: Too many open files

И это, по сути, способ, которым он может потерпеть неудачу даже в правильно настроенной системе из-за ошибки в вашей программе!

Например, возвращаясь к вашему "хорошему троичному оператору", если вы когда-нибудь написали что-то вроде

void log_message(const char *msg)
{
    FILE *whereToPrint = (strcmp(custom_topic, ERROR_TOPIC) == 0) ?
                                       fopen("/dev/null", "w") : stdout;
    fprintf(whereToPrint, "%s", msg);
}

скорее всего, рано или поздно вы получите ошибку "Too many open files", потому что, конечно, log_message() Функция, которую я здесь написал, содержит ошибку: она открывает файл (возможно) каждый раз, когда он вызывается, но никогда не закрывает его.

"Хорошее" использование троичного оператора - или любые другие "хорошие" трюки - писать весело, и они хороши, если работают, но не пожалуйста, не цепляйтесь за них за счет других, более важные аспекты вашего кода, например, убедитесь, что он работает хорошо при любых обстоятельствах.:-)

Другие вопросы по тегам