2012-12-24 13 views
9

Desidero richiamare un processo da un programma haskell e acquisire stdout e stderr.Haskell: System.Process unire stdout e stderr

Quello che faccio io:

(_, stdout, stderr) <- readProcessWithExitCode "command" [] "" 

Il problema: In questo modo, stdout e stderr vengono catturati separatamente, ma voglio che i messaggi vengano visualizzati nel posto giusto (altrimenti avrei semplicemente stdout ++ stderr che separa i messaggi di errore da le loro controparti stdout).

So che avrei potuto ottenere questo se avessi pipe l'output in un file, vale a dire

tmp <- openFile "temp.file" ... 
createProcess (proc "command" []) { stdout = UseHandle tmp, 
            stderr = UseHandle tmp } 

Quindi la mia soluzione attuale è quella di uscite di tubo ad un file temporaneo e leggere nuovamente. Comunque io sto cercando un approccio più diretto.

Se ero su Unix di sicuro mi piacerebbe semplicemente richiamare un comando di shell à la

command 2>&1 

e basta. Tuttavia, mi piacerebbe avere questo più portatile possibile.

Ciò di cui ho bisogno: ho creato un minuscolo script di haskell cgi (solo per giocare con esso) che richiama un determinato programma e stampa l'output. Voglio html-escape dell'output, quindi non posso semplicemente riversarlo sullo stdout.

Stavo pensando: Forse è possibile creare un handle in-memory, come un PipedInputStream/PipedOutputStream in Java, o ArrayInputStream/ArrayOutputStream che consente di processare i flussi IO all'interno della memoria. Ho cercato una funzione :: Handle su hoogle, ma non ho trovato nulla.

Forse c'è un altro modulo Haskell là fuori che mi consente di unire due flussi?

+0

Forse ['createPipe'] (http://hackage.haskell.org/packages/archive/unix/latest/doc/html/System-Posix-IO-ByteString.html#v:createPipe) e [' fdToHandle '] (http://hackage.haskell.org/packages/archive/unix/latest/doc/html/System-Posix-IO-ByteString.html#v:fdToHandle) aiuterà. –

risposta

6

È possibile utilizzare pipes per unire contemporaneamente due flussi di input. Il primo trucco è quello di leggere da due flussi contemporaneamente, che si può fare utilizzando il pacchetto stm:

import Control.Applicative 
import Control.Proxy 
import Control.Concurrent 
import Control.Concurrent.STM 
import System.Process 

toTMVarC :: (Proxy p) => TMVar a ->() -> Consumer p a IO r 
toTMVarC tmvar() = runIdentityP $ forever $ do 
    a <- request() 
    lift $ atomically $ putTMVar tmvar a 

fromTMVarS :: (Proxy p) => TMVar a ->() -> Producer p a IO r 
fromTMVarS tmvar() = runIdentityP $ forever $ do 
    a <- lift $ atomically $ takeTMVar tmvar 
    respond a 

Io presto fornire le primitive di cui sopra in un pacchetto pipes-stm, ma utilizzare quanto sopra per il momento.

Poi basta alimentano ogni Handle ad un separato MVar e leggere da entrambi contemporaneamente:

main = do 
    (_, mStdout, mStderr, _) <- createProcess (proc "ls" []) 
    case (,) <$> mStdout <*> mStderr of 
     Nothing    -> return() 
     Just (stdout, stderr) -> do 
      out <- newEmptyTMVarIO 
      err <- newEmptyTMVarIO 
      forkIO $ runProxy $ hGetLineS stdout >-> toTMVarC out 
      forkIO $ runProxy $ hGetLineS stderr >-> toTMVarC err 
      let combine() = runIdentityP $ forever $ do 
        str <- lift $ atomically $ 
         takeTMVar out `orElse` takeTMVar err 
        respond str 
      runProxy $ combine >-> putStrLnD 

basta cambiare fuori putStrLnD con tuttavia si desidera elaborare l'ingresso.

Per ulteriori informazioni su pipes package, leggere solo Control.Proxy.Tutorial.

3

Per il sistema POSIX è possibile utilizzare createPipe e fdToHandle in System.Posix.IO per creare un paio di nuove maniglie (non sono sicuro dove poter chiudere quelle maniglie e fds però ..):

readProcessWithMergedOutput :: String -> IO (ExitCode, String) 
    readProcessWithMergedOutput command = do 
    (p_r, p_w) <- createPipe 
    h_r <- fdToHandle p_r 
    h_w <- fdToHandle p_w 
    (_, _, _, h_proc) <- createProcess (proc command []) 
             { std_out = UseHandle h_w 
             , std_err = UseHandle h_w 
             } 
    ret_code <- waitForProcess h_proc 
    content <- hGetContents h_r 
    return (ret_code, content) 

Per le finestre, this post ha implementato una piattaforma multipiattaforma createPipe.

Problemi correlati