终止进程时捕获输出
Capture the output while terminating a process
我需要 运行 一个进程,在它 运行ning 时做一些事情,最后终止它。这
有问题的过程将我想保留的东西写入标准输出。很遗憾,
似乎这个过程在我可以连接并提取它的最后一句话之前就已经死了。拥有稀缺
异步编程的经验,我很难找到一个好的解决方案。它
如果我能在RIO.Process
的框架内完成这个任务就很幸运了,虽然我
如果无法避免,我准备走出去。 (请注意 RIO
使用了一个不寻常的
通过回调系统调用外部进程的方式。)
下面是一个高度简化的运行我正在努力实现的可行示例。
这是程序的仿真 运行:
(将其放入名为 x.sh
的文件中,然后说 chmod +x x.sh
使其可执行。)
#!/bin/sh
trap 'echo "Terminating..."; exit 0' TERM
echo "Initialization complete."
while true; do sleep 1; done
这是我的代码:
(把它放在一个名为X.hs
的文件中,然后用ghc -package rio X.hs
编译。)
{-# language NoImplicitPrelude #-}
{-# language BlockArguments #-}
{-# language OverloadedStrings #-}
module Main where
import RIO
import RIO.Process
import Data.Text.IO (hGetContents, hGetLine)
main :: IO ()
main = runSimpleApp do
proc "./x.sh" [ ]
\processConfig -> withProcessWait_ (setStdout createPipe processConfig)
\processHandle -> bracket_
(initialize processHandle)
(terminate processHandle)
(return ())
initialize :: (HasProcessContext env, HasLogFunc env) => Process () Handle () -> RIO env ()
initialize processHandle = do
x <- liftIO $ hGetLine (getStdout processHandle)
if x == "Initialization complete." then return () else error "This should not happen."
terminate :: HasLogFunc env => Process () Handle () -> RIO env ()
terminate processHandle = do
log' <- async $ liftIO $ hGetContents (getStdout processHandle)
stopProcess processHandle
log <- wait log'
logInfo $ display log
事情是这样的:
% ./X
X: fd:3: hGetBuffering: illegal operation (handle is closed)
— x.sh
在说什么,但我听不见。
管理此问题的正确方法是什么?
来自 the documentation for stopProcess
:
Close a process and release any resources acquired. This will ensure terminateProcess
is called, wait for the process to actually exit, and then close out resources allocated for the streams. In the event of any cleanup exceptions being thrown this will throw an exception.
(强调我的)你不希望 stopProcess
在你阅读输出之前这样做。你只想要terminateProcess
。 withProcessWait_
会处理剩下的事情。不幸的是,你必须走出 RIO
才能做到这一点,使用 import System.Process (terminateProcess)
然后 liftIO $ terminateProcess (unsafeProcessHandle processHandle)
.
旁注:您有点误用 bracket_
。由于 bracket_
的 "middle" 是一个空操作,尤其是现在开始和结束实际上并没有获取或释放任何资源,所以它有点毫无意义。此外,完全不用 async
,您可以在终止进程后正常读取输出,因为进程已经产生的输出不会在终止时消失。
这是您的代码,上面所有内容都已修复:
{-# language NoImplicitPrelude #-}
{-# language BlockArguments #-}
{-# language OverloadedStrings #-}
module Main where
import RIO
import RIO.Process
import Data.Text.IO (hGetContents, hGetLine)
import System.Process (terminateProcess)
main :: IO ()
main = runSimpleApp do
proc "./x.sh" [ ]
\processConfig -> withProcessWait_ (setStdout createPipe processConfig)
\processHandle -> do
initialize processHandle
terminate processHandle
initialize :: (HasProcessContext env, HasLogFunc env) => Process () Handle () -> RIO env ()
initialize processHandle = do
x <- liftIO $ hGetLine (getStdout processHandle)
if x == "Initialization complete." then return () else error "This should not happen."
terminate :: HasLogFunc env => Process () Handle () -> RIO env ()
terminate processHandle = do
liftIO $ terminateProcess (unsafeProcessHandle processHandle)
log <- liftIO $ hGetContents (getStdout processHandle)
logInfo $ display log
我需要 运行 一个进程,在它 运行ning 时做一些事情,最后终止它。这
有问题的过程将我想保留的东西写入标准输出。很遗憾,
似乎这个过程在我可以连接并提取它的最后一句话之前就已经死了。拥有稀缺
异步编程的经验,我很难找到一个好的解决方案。它
如果我能在RIO.Process
的框架内完成这个任务就很幸运了,虽然我
如果无法避免,我准备走出去。 (请注意 RIO
使用了一个不寻常的
通过回调系统调用外部进程的方式。)
下面是一个高度简化的运行我正在努力实现的可行示例。
这是程序的仿真 运行:
(将其放入名为 x.sh
的文件中,然后说 chmod +x x.sh
使其可执行。)
#!/bin/sh
trap 'echo "Terminating..."; exit 0' TERM
echo "Initialization complete."
while true; do sleep 1; done
这是我的代码:
(把它放在一个名为X.hs
的文件中,然后用ghc -package rio X.hs
编译。)
{-# language NoImplicitPrelude #-}
{-# language BlockArguments #-}
{-# language OverloadedStrings #-}
module Main where
import RIO
import RIO.Process
import Data.Text.IO (hGetContents, hGetLine)
main :: IO ()
main = runSimpleApp do
proc "./x.sh" [ ]
\processConfig -> withProcessWait_ (setStdout createPipe processConfig)
\processHandle -> bracket_
(initialize processHandle)
(terminate processHandle)
(return ())
initialize :: (HasProcessContext env, HasLogFunc env) => Process () Handle () -> RIO env ()
initialize processHandle = do
x <- liftIO $ hGetLine (getStdout processHandle)
if x == "Initialization complete." then return () else error "This should not happen."
terminate :: HasLogFunc env => Process () Handle () -> RIO env ()
terminate processHandle = do
log' <- async $ liftIO $ hGetContents (getStdout processHandle)
stopProcess processHandle
log <- wait log'
logInfo $ display log
事情是这样的:
% ./X
X: fd:3: hGetBuffering: illegal operation (handle is closed)
— x.sh
在说什么,但我听不见。
管理此问题的正确方法是什么?
来自 the documentation for stopProcess
:
Close a process and release any resources acquired. This will ensure
terminateProcess
is called, wait for the process to actually exit, and then close out resources allocated for the streams. In the event of any cleanup exceptions being thrown this will throw an exception.
(强调我的)你不希望 stopProcess
在你阅读输出之前这样做。你只想要terminateProcess
。 withProcessWait_
会处理剩下的事情。不幸的是,你必须走出 RIO
才能做到这一点,使用 import System.Process (terminateProcess)
然后 liftIO $ terminateProcess (unsafeProcessHandle processHandle)
.
旁注:您有点误用 bracket_
。由于 bracket_
的 "middle" 是一个空操作,尤其是现在开始和结束实际上并没有获取或释放任何资源,所以它有点毫无意义。此外,完全不用 async
,您可以在终止进程后正常读取输出,因为进程已经产生的输出不会在终止时消失。
这是您的代码,上面所有内容都已修复:
{-# language NoImplicitPrelude #-}
{-# language BlockArguments #-}
{-# language OverloadedStrings #-}
module Main where
import RIO
import RIO.Process
import Data.Text.IO (hGetContents, hGetLine)
import System.Process (terminateProcess)
main :: IO ()
main = runSimpleApp do
proc "./x.sh" [ ]
\processConfig -> withProcessWait_ (setStdout createPipe processConfig)
\processHandle -> do
initialize processHandle
terminate processHandle
initialize :: (HasProcessContext env, HasLogFunc env) => Process () Handle () -> RIO env ()
initialize processHandle = do
x <- liftIO $ hGetLine (getStdout processHandle)
if x == "Initialization complete." then return () else error "This should not happen."
terminate :: HasLogFunc env => Process () Handle () -> RIO env ()
terminate processHandle = do
liftIO $ terminateProcess (unsafeProcessHandle processHandle)
log <- liftIO $ hGetContents (getStdout processHandle)
logInfo $ display log