Программирование

Как читать из пайпа в C++ без блокировки терминала

В C++ как отличить stdin от терминала и читать только из пайпа, чтобы при отсутствии пайпа программа получала пустой буфер и переходила к обработке ошибок.

Как в C++ правильно читать данные из пайпа, не беря ввод с терминала (TTY stdin), чтобы, если пайп отсутствует, программа просто получала пустой буфер и переходила к обработке ошибок?

В текущем варианте, если ничего не передать через пайп, программа читает из терминала (как делает input() в Python).

Как изменить логику чтения, чтобы различать TTY и пайп, и корректно обрабатывать ситуацию, когда пайп недоступен?

Если stdin подключён к терминалу, isatty() возвращает ненулевое значение, а при пайпе — 0. Проверив это, можно отличить два случая: читать только из пайпа, иначе просто переходить к обработке ошибок.


Как это работает

  1. Определяем, что stdin не терминал

    cpp
    #include <unistd.h>
    #include <sys/stat.h>
    
    bool stdinIsPipe() {
        struct stat st;
        if (fstat(STDIN_FILENO, &st) == 0) {
            return S_ISFIFO(st.st_mode);          // явный пайп
        }
        return !isatty(STDIN_FILENO);              // любой не‑терминал
    }
    
  2. Читаем данные только из пайпа

    cpp
    #include <iostream>
    #include <istreambuf_iterator>
    #include <string>
    
    std::string readPipe() {
        std::istreambuf_iterator<char> it(std::cin), end;
        return std::string(it, end);              // читаем до EOF
    }
    
  3. Главная логика программы

    cpp
    int main() {
        std::string data;
    
        if (stdinIsPipe()) {                       // есть пайп
            data = readPipe();
        } else {
            std::cerr << "Пайп не передан – вход пустой.\n";
        }
    
        if (data.empty()) {
            // обработка ошибки: данные отсутствуют
            std::cerr << "Ошибка: входные данные пусты.\n";
            return 1;
        }
    
        // дальнейшая обработка data
        std::cout << "Получено " << data.size() << " байт.\n";
        return 0;
    }
    

Ключевой момент
При isatty() результат ноль означает, что stdin не привязан к терминалу, то есть скорее всего это пайп или файл.
Если вход всё же терминал, программа не блокируется, просто получает пустой буфер и может перейти к обработке ошибок.


Полезные детали и альтернативы

Что ещё можно проверить Как сделать Где это пригодится
Удостовериться, что stdin действительно пайп, а не обычный файл fstat() + S_ISFIFO() При работе только с пайпами, не с редиректом файлов
Чтение в двоичном режиме (без преобразования CRLF) std::cin.rdbuf()->pubsetbuf(0,0) Поддержка бинарных данных
Чтение построчно, пока не встретится EOF std::getline(std::cin, line) При обработке текстовых потоков
Проверка на Windows <io.h> + _isatty() Портирование к Windows

Ссылки на источники

  1. Stack Overflow – Detect if stdin is a terminal or pipe?
  2. Stack Overflow – How do I check if my program has data piped into it
  3. Furkan Baytekin – Standard Streams (stdin, stdout, stderr) & Pipes
  4. POSIX isatty()

Заключение

  • Используйте isatty() (или fstat() + S_ISFIFO()) для проверки, что stdin не является терминалом.
  • Если stdin – пайп, считывайте данные до EOF; если терминал, обрабатывайте как «пустой буфер» и переходите к обработке ошибок.
  • Такой подход гарантирует, что программа не будет блокироваться, ожидая ввод из клавиатуры, и корректно реагирует, когда пайп отсутствует.

Пробуйте приведённый шаблон в своих проектах, и он быстро станет надёжным способом работы с потоками ввода.

Авторы
Проверено модерацией
Модерация