Вот некоторые примеры того, как взаимодействовать с порожденным процессом в моде, упомянутой @jberryman.
программа взаимодействует со сценарием ./compute
, который просто считывает строку из стандартного ввода в форме <x> <y>
и возвращает х +- после задержки у секунд. Более подробная информация: this gist.
Существует много предостережений при взаимодействии с порожденными процессами. Чтобы избежать «страдания от буферизации», вам необходимо очистить исходящую трубу всякий раз, когда вы отправляете входные данные, а порожденный процесс должен сбрасывать stdout каждый раз, когда он отправляет ответ. Взаимодействие с процессом с помощью псевдо-tty является альтернативой, если вы обнаружите, что stdout не сбрасывается достаточно быстро.
Кроме того, в примерах предполагается, что закрытие входной трубы приведет к прекращению процесса появления. Если это не так, вам нужно будет отправить сигнал для обеспечения завершения.
Вот пример кода - см. Подпрограмму main
в конце для выборочных вызовов.
import System.Environment
import System.Timeout (timeout)
import Control.Concurrent
import Control.Concurrent (forkIO, threadDelay, killThread)
import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar)
import System.Process
import System.IO
-- blocking IO
main1 cmd tmicros = do
r <- createProcess (proc "./compute" []) { std_out = CreatePipe, std_in = CreatePipe }
let (Just inp, Just outp, _, phandle) = r
hSetBuffering inp NoBuffering
hPutStrLn inp cmd -- send a command
-- block until the response is received
contents <- hGetLine outp
putStrLn $ "got: " ++ contents
hClose inp -- and close the pipe
putStrLn "waiting for process to terminate"
waitForProcess phandle
-- non-blocking IO, send one line, wait the timeout period for a response
main2 cmd tmicros = do
r <- createProcess (proc "./compute" []) { std_out = CreatePipe, std_in = CreatePipe }
let (Just inp, Just outp, _, phandle) = r
hSetBuffering inp NoBuffering
hPutStrLn inp cmd -- send a command, will respond after 4 seconds
mvar <- newEmptyMVar
tid <- forkIO $ hGetLine outp >>= putMVar mvar
-- wait the timeout period for the response
result <- timeout tmicros (takeMVar mvar)
killThread tid
case result of
Nothing -> putStrLn "timed out"
Just x -> putStrLn $ "got: " ++ x
hClose inp -- and close the pipe
putStrLn "waiting for process to terminate"
waitForProcess phandle
-- non-block IO, send one line, report progress every timeout period
main3 cmd tmicros = do
r <- createProcess (proc "./compute" []) { std_out = CreatePipe, std_in = CreatePipe }
let (Just inp, Just outp, _, phandle) = r
hSetBuffering inp NoBuffering
hPutStrLn inp cmd -- send command
mvar <- newEmptyMVar
tid <- forkIO $ hGetLine outp >>= putMVar mvar
-- loop until response received; report progress every timeout period
let loop = do result <- timeout tmicros (takeMVar mvar)
case result of
Nothing -> putStrLn "still waiting..." >> loop
Just x -> return x
x <- loop
killThread tid
putStrLn $ "got: " ++ x
hClose inp -- and close the pipe
putStrLn "waiting for process to terminate"
waitForProcess phandle
{-
Usage: ./prog which delay timeout
where
which = main routine to run: 1, 2 or 3
delay = delay in seconds to send to compute script
timeout = timeout in seconds to wait for response
E.g.:
./prog 1 4 3 -- note: timeout is ignored for main1
./prog 2 2 3 -- should timeout
./prog 2 4 3 -- should get response
./prog 3 4 1 -- should see "still waiting..." a couple of times
-}
main = do
(which : vtime : tout : _) <- fmap (map read) getArgs
let cmd = "10 " ++ show vtime
tmicros = 1000000*tout :: Int
case which of
1 -> main1 cmd tmicros
2 -> main2 cmd tmicros
3 -> main3 cmd tmicros
_ -> error "huh?"
Какую семантику вы хотите иметь эту функцию? Что делать, если процесс записывает символы «ABC» в «out», а затем ваша программа вызывает «hGetLineNonBlocking», читая символы «ABC», но неспособная вернуть «Just', потому что новая строка еще не была прочитана; и он не может блокироваться, пока не появится больше символов, таких как 'hGetLine' (очевидно). Вы выбрасываете эту частичную линию? Это почти наверняка неправильно. Я подозреваю, что в этом случае вы * хотите * блокировать, ожидая остальную часть строки. Если это так, просто проверьте, пустой ли дескриптор с 'hReady', прежде чем читать его с помощью' hGetLine'. – user2407038
@ user2407038 Извините, но я не понимаю ваш комментарий, так как выполнение 'timeout 1 $ hGetLine ...' всегда возвращает 'Just' что-то, если есть полная строка для чтения, иначе она возвращает' Nothing'. – mljrg
Чтобы 'hGetLine' читал строку, он должен читать символы последовательно, пока не достигнет символа новой строки, после чего он вернется. Однако, если 'hGetLine' читает кучу символов, но не имеет символа новой строки, линия не заканчивается - поэтому hGetLine будет блокироваться, пока не появится больше символов. Если ваша функция разумна, она не будет считывать символы из буфера и отбрасывать их, но она не может блокировать - что она делает с символами, которые уже читаются? Возвращает ли она частичную строку, которая * не * заканчивается символом новой строки? – user2407038