Что такое TTY — и почему программа молчит, когда вывод уходит в файл

Ты запускаешь скрипт на Python. Он печатает прогресс: «обработано 1», «обработано 2»… Всё живо.
Теперь хочешь сохранить вывод в лог и пишешь python job.py | tee job.log. И экран пустой. Минуту. Пять. Ты уже уверен, что скрипт завис. А потом он вываливает все строки разом — и завершается.
Скрипт не зависал. Он просто понял, что на него больше не смотрит человек. Программы умеют это проверять. И проверка называется TTY.
Что такое TTY
TTY — сокращение от teletypewriter, телетайп. Сто лет назад это была печатная машинка, соединённая проводом с другой такой же. Позже к компьютерам подключали телетайпы как экран и клавиатуру. Отсюда и название.
Телетайпов давно нет, а слово осталось. Сегодня TTY — это «живой» терминал: окно, где сидит человек, видит вывод и может что-то нажать. Твой терминал — это TTY. А файл, пайп, лог в CI — нет.
Любая программа может спросить у системы: «мой вывод сейчас идёт в TTY?» Проверь сам:
[ -t 1 ] && echo "человек смотрит" || echo "вывод уходит в трубу"
Запусти так, а потом с | cat в конце. Ответ поменяется. Единица здесь — номер потока stdout, про потоки вывода есть отдельный разбор.
Что программы делают по-другому без TTY
Как только человек «пропал», многие инструменты меняют поведение. Специально, из лучших побуждений:
- Цвета исчезают.
ls,grep,gitи тесты не красят вывод, чтобы в лог не попал мусор вроде^[[31m. lsпишет в один столбец. В терминале — сетка, в пайпе — по файлу на строку. Так удобнее следующей команде.git logне открывает листалку. В терминале он показывает историю постранично, в пайпе отдаёт всё сразу.- Python копит вывод. Это наш случай. Для человека Python печатает построчно. Для трубы — собирает в буфер по несколько килобайт и выдаёт пачкой. Быстрее, но для тебя выглядит как зависание.
Лечится одной настройкой:
python -u job.py | tee job.log
# или
PYTHONUNBUFFERED=1 python job.py | tee job.log
Флаг -u выключает буфер. Строки снова пойдут сразу. Та же беда бывает у grep в середине пайпа, у него есть флаг --line-buffered.
Что без TTY вообще не работает
Есть программы, которым нужен человек на том конце. Без TTY они падают или ждут вечно:
sudoне может спросить пароль:a terminal is required to read the password.docker exec -itв CI падает с ошибкойthe input device is not a TTY. Букваtв-itпросит терминал, а в CI его нет. Убериt, оставь-i.- Интерактивные мастера вроде
npm init,git rebase -iили вопроса «Продолжить? (y/n)» ждут ответа, которого никто не даст.
Как понять, что странность именно из-за TTY? Простой тест: запусти команду как обычно, а потом ту же с | cat в конце. Если поведение поменялось — пропали цвета, вывод пришёл пачкой, исчез вопрос — дело в TTY. Если ничего не поменялось, ищи причину в другом месте.
А если программе позарез нужен терминал, а его нет? В Linux есть команда script, она подсовывает поддельный TTY: script -q -c "npm test" /dev/null. Программа думает, что на неё смотрит человек, и ведёт себя как в окне. Пользуйся редко: обычно правильнее найти флаг вроде --yes.
Зачем это тебе, если команды запускает ИИ
Агенты вроде Claude Code и Codex выполняют команды без TTY. Они читают вывод как текст из трубы, а не смотрят в окно.
Отсюда три вещи, которые ты заметишь:
- В логах агента нет цветов, а
lsвыглядит списком. Это нормально. - Команда, которая ждёт ответа «y/n», может повесить агента. Поэтому хорошие агенты добавляют
--yes,-yилиCI=true. Если видишь, что агент застрял на установке — проверь, не задала ли она вопрос. - Python-скрипт, запущенный агентом, «молчит» до конца. Попроси добавить
-u— и прогресс станет видно.
Тот же расклад в CI/CD: сборка на сервере — это тоже мир без TTY.
Как заставить цвета работать в пайпе?
У большинства инструментов есть флаг --color=always, например grep --color=always error log | less -R. Многие Node-утилиты понимают переменную FORCE_COLOR=1.
Что такое /dev/tty и pts?
Это имена TTY в системе. Команда tty покажет твоё, например /dev/pts/0. «pts» значит псевдотерминал: окно терминала, которое притворяется старым телетайпом.
Почему ssh иногда пишет «Pseudo-terminal will not be allocated»?
Ты передал ssh команду или вход из пайпа, и он решил, что терминал не нужен. Если удалённая команда интерактивная, добавь флаг -t.
Короткие уроки-истории, симулятор агента и ежедневная практика — в нашем мобильном приложении. Бесплатно.





