2013-10-22 7 views
11

Gestern habe ich eine kleine xinetd Übung für meine Schüler geschrieben: mache ein Reverse-Echo-Programm.Haskell default io buffering

Um etwas Neues zu lernen, habe ich versucht, eine Haskell-Lösung zu implementieren. Das triviale main = forever $ interact reverse funktioniert nicht. Ich ging durch this question und machte eine korrigierte Version:

import Control.Monad 
import System.IO 

main = forever $ interact revLines 

revLines = unlines . map (reverse) . lines 

Aber diese korrigierte Version auch nicht funktioniert. Ich lese die buffering documentation und spielte mit den verschiedenen Einstellungen. Wenn ich NoBuffering oder LineBuffering einstelle, funktioniert mein Programm korrekt. Schließlich gedruckt i die Standardpuffer Modi für stdin und stdout

import System.IO 

main = do 
    hGetBuffering stdin >>= print 
    hGetBuffering stdout >>= print 

ich BlockBuffering Nothing habe, wenn ich mein Programm von xinetd laufen (echo "test" | nc localhost 7), aber von cli habe ich LineBuffering

  • bekam Was ist der Unterschied zwischen einem xinetd TCP-Dienst und einem CLI-Programm, in Bezug auf die Pufferung?
  • Muss ich manuell die Pufferung einstellen, wenn ich ein Arbeitsprogramm mit beiden laufenden Methoden schreiben will?

Edit: Vielen Dank für die hilfreichen Antworten.

Ich akzeptiere die Antwort, die von blaze gegeben wurde, er gibt mir einen Hinweis mit isatty (3). Ich habe nochmal die System.IO Dokumentation durchgelesen und die hIsTerminalDevice Funktion gefunden, mit der ich die Verbindung des Griffes überprüfen kann.

Für das Protokoll, hier ist mein letztes Programm:

{-# OPTIONS_GHC -W #-} 

import System.IO 

main = do 
    hSetBuffering stdin LineBuffering 
    hSetBuffering stdout LineBuffering 

    interact revLines 

revLines = unlines . map (reverse) . lines 
+2

Sie brauchen nicht für immer hier. Aufgrund der Faulheit wird "interact" weiterhin auf Eingabe warten, und für jede vollständige Zeile von "stdin" wird die Umkehrung sofort gedruckt. – tempestadept

+0

Ich denke, es ist Betriebssystem-spezifisch, aber im Allgemeinen, wenn Sie ein zeilenorientiertes Programm haben, dann werden Sie wahrscheinlich das beste Verhalten von 'LineBuffering' bekommen. –

+5

Ich beschwere mich oft über schlechte Fragen, also wollte ich dich nur loben. Das ist eine gute Frage. Minimierter Code, klare Anweisungen zum Reproduzieren Ihres Problems, eindeutige Beweise dafür, dass Sie selbst Fortschritte erzielt haben, und eine klare Beschreibung dessen, was Sie wissen müssen, um Fortschritte zu erzielen. Herrlich. –

Antwort

10

Es ist nicht spezifisch für Haskell (zum Beispiel der Standard-C-Bibliothek macht das Gleiche). Wenn ein Dateideskriptor dem Terminal entspricht, wird die Pufferung traditionell auf den Zeilenmodus gesetzt, andernfalls auf den Blockierungsmodus. Der Dateideskriptortyp kann mit der Funktion isatty(3) überprüft werden - nicht sicher, ob er nach System.IO exportiert wird.

Und ja, Sie müssen den Puffermodus manuell einstellen, wenn Sie davon abhängig sind.

Übrigens können Sie das System betrügen und die Blockpufferung in der Befehlszeile erzwingen, indem Sie Ihr Programm als cat | ./prog | cat ausführen.

5

Das GHC-Laufzeitsystem versucht clever zu sein, wenn es die Standardpufferung wählt. Wenn es so aussieht, als wären stdin und stdout direkt mit dem Terminal verbunden, werden sie liniengepuffert. Wenn es so aussieht, als ob sie mit etwas anderem verbunden sind, werden sie im Block gepuffert. Dies kann problematisch sein, wenn Sie ein Programm mit zeilenweiser Eingabe ausführen möchten, das nicht direkt von einem Terminal kommt. Zum Beispiel denke ich, dass cat | your-program anders verhält als nur your-program.

Muss ich die Pufferung manuell einstellen, wenn ich ein Arbeitsprogramm mit beiden laufenden Methoden schreiben möchte?

Ja.