如何在Haskell中执行一系列shell命令并在出现错误时中断?
假设我有一个表示要执行的shell命令的如何在Haskell中执行一系列shell命令并在出现错误时中断?,shell,haskell,monads,Shell,Haskell,Monads,假设我有一个表示要执行的shell命令的Strings列表 commands = ["git clone https://github.com/test/repo.git", "git checkout origin"] 另外,假设我有一个命令,execCommand,它接受一个字符串,作为shell命令执行,检索退出代码stdout和stderr,如果退出代码为非零,则返回stdout和stderr的串联;否则,它将返回Nothing 现在,我如何顺序执行命令列表,同时确保后续命令不会在一个
String
s列表
commands = ["git clone https://github.com/test/repo.git", "git checkout origin"]
另外,假设我有一个命令,execCommand
,它接受一个字符串,作为shell命令执行,检索退出代码stdout和stderr,如果退出代码为非零,则返回stdout和stderr的串联;否则,它将返回Nothing
现在,我如何顺序执行命令列表,同时确保后续命令不会在一个命令产生错误后执行
下面是execCommand
的完整代码
import System.IO
import System.Process
import System.Exit
createCommand :: String -> FilePath -> CreateProcess
createCommand command curDir =
(shell command){std_out = CreatePipe, std_err = CreatePipe, cwd = Just curDir}
execCommand :: String -> FilePath -> IO (Maybe String)
execCommand command curDir = do
(_, Just hout, Just herr, procHandle) <- createProcess $ createCommand command curDir
exitCode <- waitForProcess procHandle
stdOut <- hGetContents hout
stdErr <- hGetContents herr
if exitCode /= ExitSuccess
then return $ Just $ concat [stdOut, stdErr]
else return $ Nothing
import System.IO
导入系统。进程
导入系统。退出
createCommand::String->FilePath->CreateProcess
createCommand命令curDir=
(shell命令){std_out=CreatePipe,std_err=CreatePipe,cwd=Just curDir}
execCommand::字符串->文件路径->IO(可能是字符串)
execCommand curDir=do
(u,Just hout,Just herr,procHandle)您可以使用mapM_u
,它的类型为
mapM_ :: ( (CmdSpec, FilePath) -> ExceptT String IO ())
-> [(CmdSpec, FilePath)] -> ExceptT String IO ()
以及适当的短路行为。这可能会解决您的问题:
{-# LANGUAGE FlexibleInstances #-}
import System.IO
import System.Process
import System.Exit
import Control.Exception
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.Trans.Maybe
createCommand :: CmdSpec -> FilePath -> CreateProcess
createCommand (ShellCommand command) curDir =
(shell command){std_out = CreatePipe, std_err = CreatePipe, cwd = Just curDir}
createCommand (RawCommand command arguments) curDir =
(proc command arguments){std_out = CreatePipe, std_err = CreatePipe, cwd = Just curDir}
execCommand :: CmdSpec -> FilePath -> IO ()
execCommand command curDir = do
(_, Just hout, Just herr, procHandle) <- createProcess $ createCommand command curDir
exitCode <- waitForProcess procHandle
when (exitCode /= ExitSuccess) $ do
stdOut <- hGetContents hout
stdErr <- hGetContents herr
throwIO $ stdOut ++ stdErr
instance Exception String
execList :: [(CmdSpec, FilePath)] -> MaybeT IO String
execList xs = do
out <- liftIO $ try $ mapM_ (uncurry execCommand) xs
case out of
Left c -> return c
Right _ -> mzero
{-#语言灵活实例}
导入系统.IO
导入系统。进程
导入系统。退出
导入控制。异常
进口管制
导入控制.Monad.IO.Class
进口管制。单子。翻译。也许
createCommand::CmdSpec->FilePath->CreateProcess
createCommand(ShellCommand命令)curDir=
(shell命令){std_out=CreatePipe,std_err=CreatePipe,cwd=Just curDir}
createCommand(RawCommand命令参数)curDir=
(proc命令参数){std_out=CreatePipe,std_err=CreatePipe,cwd=Just curDir}
execCommand::CmdSpec->FilePath->IO()
execCommand curDir=do
(uu,Just hout,Just herr,prochhandle)好吧,我很确定我找到了答案。我不再使用“花哨”的东西,而是转而使用好的ol'递归
runCommands :: [String] -> FilePath -> IO (Maybe String)
runCommands [] _ = return Nothing
runCommands (command:rest) curDir = do
result <- execCommand command curDir
case result of
Nothing -> runCommands rest curDir
Just err -> return $ Just err
runCommands::[String]->FilePath->IO(可能是String)
runCommands[]\=不返回任何内容
runCommands(命令:rest)curDir=do
结果run命令rest curDir
Just err->return$Just err
我的第一个问题是为什么在这里使用字符串Bool
。这很奇怪,有两个原因。1.按照惯例,由于其函子
、应用程序
、以及单子
实例的编写方式,右侧
是“一切顺利”的一侧,左侧
是“有什么事情发生了意外”的一侧。2.如果奇怪的一面总是真的
,你为什么需要它?一种选择是使用可能是String
。然而,更自然的方法是使用或Int-String
,这样您要么得到串联输出,要么得到退出代码。(CONT)(CONT)另一个需要考虑的自然事物(也许不是在您的应用程序中)是<代码>(INT,String)字符串
,它给出标准输出或给出标准错误和退出代码。(编辑:我实际上不确定退出代码是什么类型。它很可能不是Int
)嗯,还有一些带有类型签名的东西。您试图使用monad transformer而没有实际使用它,这可能会在将来给您带来很多痛苦。你应该在标准库中查找Edward Kmett或ErrorT制作的软件包。@Julián,在transformers
@dfeuer中也有Control.Monad.Trans.other
,因为我刚刚查找了它,而它来自于任何一个软件包。尽管再次查看代码会提醒我,标准库中使用IO时存在抛出和捕获。因此,当某些东西失败时抛出异常可能更好。