Вход в систему C печать на стандартный вывод и стандартный вывод без дубликатов - PullRequest
1 голос
/ 03 февраля 2020

Я реализовал свои собственные функции регистрации сообщений для моей программы командной строки C, я хочу иметь возможность печатать информационные сообщения на stdout, сообщения об ошибках на stderr и предупреждения на оба без дублирования сообщения, если они выводятся в одно и то же местоположение.

Сообщения Info и Error работают нормально, но для предупреждающих сообщений я понятия не имею, как эффективно проверить, указывают ли потоки файлов stdout и stderr на одно и то же место вывода. Мой код работает, но я не могу понять, почему, потому что логически при пошаговом выполнении функции он должен создавать повторяющиеся записи, если stdout и stderr указывают на один и тот же файл.

Я проверял, когда stdout и stderr имеют один и тот же выходной файл, они по-прежнему выдают разные адреса памяти в указателях, а fileno(stdout) и fileno(stderr) отличаются.

tl; dr У меня есть код, который работает, но как Насколько я знаю ... это не должно быть. Может кто-нибудь помочь объяснить, почему он работает, или кто-нибудь знает правильный способ решить эту проблему.

Редактировать: я вызываю программу так: myProgram >out.lis 2>out.lis

Редактировать 2: Когда при таком вызове получаются дубликаты: myProgram >out.lis 2>&1

Мой код для предупреждающего сообщения:

/* Warning message, a unified function for printing warning messages */
/* Warning messages are printed to both the output log and the error log (if different) */
void warningMessage(char * msg) {
    if (isatty(fileno(stderr))) {
        /* Add color if printing to terminal */
        fprintf(stderr, "\033[0;31;43mWarning: \033[0;30;43m%s\033[39;49m\r\n", msg);
    } else {
        fprintf(stderr, "\nWarning: %s\n", msg);

        if (isatty(fileno(stdout))) {
            fprintf(stdout, "\033[0;31;43mWarning: \033[0;30;43m%s\033[39;49m\r\n", msg);
        } else {
            fprintf(stdout, "\nWarning: %s\n", msg);
        }
    }
}

Любые другие указатели относительно моего кода также будут полезны! Я только недавно начал изучать C!

Ответы [ 2 ]

2 голосов
/ 03 февраля 2020

Трюк будет fstat ().

Получить fileno () для обоих (должно быть 1 и 2 соответственно, но может отличаться для не Unix ОС), передать их как первый параметр для fstat (), и сравните структуры, заполненные как второй параметр. Я ожидаю точных совпадений, если они выводят в одно и то же место Я мог бы поверить, что временные метки могут быть другими.

Боюсь, я не могу сказать вам, если MS- Windows имеет тот же вызов или нет, но он должен иметь эквивалент.

Не забывайте грипп sh соответственно.

Редактировать:

Ответ Некоторые программист чувак отмечает, что только нужно проверить два поля. Это правильно (за исключением, возможно, в некоторых странных файловых системах).

Есть некоторые странные вещи, которые могут произойти. Если для одного и того же устройства есть два узла устройства (как в / dev / tty1 и / dev / tty1_alternate, указывающих на одно и то же устройство), то st_ino не будет совпадать, но st_rdev будет соответствовать. Я бы отнесся к ним как к разным, поскольку пользователь играет с вами в игры.

Также было бы неплохо проверить, являются ли два открытия одинаковыми. (Имеется дело со случаем myprogram >out 2>out.) Для этого вам, вероятно, нужно возиться с некоторыми параметрами и посмотреть, не изменит ли один из них другой. Вероятно, функция fcntl (), использующая F_GETFL и F_SETFL.

1 голос
/ 03 февраля 2020

Упомянутый в ответ Дэвида Г. вы можете использовать fstat, чтобы проверить, где действительно записываются файловые дескрипторы.

В системе POSIX вы используйте структуру stat members st_dev и st_ino, чтобы узнать, какие файлы используются. Если эти два члена равны и для stdout, и для stderr, то вы пишете в один и тот же файл.

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

...