Haskell default io buffering
Вчера я написал небольшое упражнение для моих учеников: сделайте обратную эхо-программу.
Чтобы узнать что-то новое, я попытался реализовать решение Haskell. Тривиальный main = forever $ interact reverse
не работает. Я прошел этот вопрос и произвел исправленную версию:
import Control.Monad
import System.IO
main = forever $ interact revLines
revLines = unlines . map (reverse) . lines
Но эта исправленная версия также не работает. Я прочитал буферную документацию и играл с различными настройками.
Если я устанавливаю NoBuffering
или LineBuffering
, моя программа работает правильно. Наконец, я напечатал режимы буферизации по умолчанию для stdin и stdout
import System.IO
main = do
hGetBuffering stdin >>= print
hGetBuffering stdout >>= print
У меня есть BlockBuffering Nothing
, если я запускаю свою программу из xinetd (echo "test" | nc localhost 7
), но из cli у меня есть LineBuffering
- В чем разница между службой xinetd tcp и программой cli, связанной с буферизацией?
- Нужно ли вручную настраивать буферизацию, если я хочу написать рабочую программу с использованием обоих методов?
Изменить: Спасибо всем за полезные ответы.
Я принимаю ответ, который дал пламя, он дает мне подсказку с исатти (3). Я снова просмотрел документацию System.IO и обнаружил функцию hIsTerminalDevice, с которой я могу проверить подключение дескриптора.
Для записи здесь моя последняя программа:
{-# OPTIONS_GHC -W #-}
import System.IO
main = do
hSetBuffering stdin LineBuffering
hSetBuffering stdout LineBuffering
interact revLines
revLines = unlines . map (reverse) . lines
Ответы
Ответ 1
Это не относится к Haskell (например, стандартная библиотека C делает то же самое).
Традиционно, если дескриптор файла соответствует терминалу, буферизация устанавливается в режим линии, иначе режим блокировки. Тип дескриптора файла может быть проверен функцией isatty(3)
- не уверен, что он экспортирован в System.IO
.
И да, вам нужно установить режим буферизации вручную, если вы зависите от него.
Кстати, вы можете обманывать систему и принудительно блокировать блокировку в командной строке, запустив вашу программу как cat | ./prog | cat
.
Ответ 2
Система времени выполнения GHC пытается быть умной, когда выбирает буферизацию по умолчанию. Если это похоже, что stdin и stdout напрямую связаны с терминалом, они будут буферизироваться по строке. Если похоже, что они связаны с чем-то другим, они блокируются. Это может быть проблематично, если вы хотите запустить программу с линейным вводом, который не поступает напрямую с терминала. Например, я думаю, что cat | your-program
ведет себя иначе, чем просто your-program
.
Нужно ли вручную настраивать буферизацию, если я хочу написать рабочую программу с использованием обоих методов?
Да.