Всегда ли /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
"невозможно" не удается?
Если ваша программа проверяет
fopen
сбой, он напечатает сообщение как"Impossible error! Can't open /dev/null"
и будет понятно, что происходит.Если ваша программа не может проверить
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()
Функция, которую я здесь написал, содержит ошибку: она открывает файл (возможно) каждый раз, когда он вызывается, но никогда не закрывает его.
"Хорошее" использование троичного оператора - или любые другие "хорошие" трюки - писать весело, и они хороши, если работают, но не пожалуйста, не цепляйтесь за них за счет других, более важные аспекты вашего кода, например, убедитесь, что он работает хорошо при любых обстоятельствах.:-)