Haskell 实战:惰性地读取子进程输出
source link: https://blog.lilydjwg.me/2012/4/18/haskell-in-action-read-subprocess-output-lazily.33103.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Haskell 实战:惰性地读取子进程输出
本文来自依云's Blog,转载请注明。
突然想给 locate 命令写个 wrapper,把输出中的家目录和一些因加密而引入的软链接显示为~
。自然,这需要读取 locate 命令的输出。在 process 这个库中看到了readProcess
函数,似乎是自己想要的(完整代码):
readLocate :: [
String
] ->
IO
String
readLocate args = getArgs >>= \cmd ->
let
args' = args ++ cmd
in
readProcess
"locate"
args'
""
结果却发现,原本 locate 命令是边查找边输出的,现在变成了先静默,然后一下子全部吐出来。没有按 Haskell 惯常的「懒惰」脾气来。这样一来,当我发现输出项目太多想按Ctrl-C中断时已经晚了。
Google 了一下,找到这个:
I guess people who want laziness can implement it themselves directly, taking care to get whatever laziness it is that they want.
好吧。我先下回 process 库的源码看看readProcess
为什么不是惰性的:
readProcess
::
FilePath
-- ^ command to run
-> [
String
] -- ^
any
arguments
->
String
-- ^ standard input
->
IO
String
-- ^ stdout
readProcess cmd args input =
do
(
Just
inh,
Just
outh, _, pid) <-
createProcess (proc cmd args){ std_in = CreatePipe,
std_out = CreatePipe,
std_err = Inherit }
-- fork off a thread to start consuming the output
output <- hGetContents outh
outMVar <- newEmptyMVar
_ <- forkIO $ C.evaluate (
length
output) >> putMVar outMVar ()
-- now write and flush any input
when (
not
(
null
input)) $
do
hPutStr inh input; hFlush inh
hClose inh -- done with stdin
-- wait on the output
takeMVar outMVar
hClose outh
-- wait on the process
ex <- waitForProcess pid
case
ex
of
ExitSuccess ->
return
output
ExitFailure r ->
ioError
(mkIOError OtherError (
"readProcess: "
++ cmd ++
' '
:
unwords
(
map
show
args) ++
" (exit "
++
show
r ++
")"
)
Nothing
Nothing
)
原来是另开了一 IO 线程读输出,然后等待进程结束后关闭管道。这解释为什么它不是惰性的——它得进程善后处理。
那好吧,改用createProcess
好了:
doLocate ::
IO
(
String
, ProcessHandle)
doLocate =
do
argv0 <- getProgName
let
args =
case
argv0
of
"lre"
-> [
"-b"
,
"--regex"
]
_ -> []
args' <- getArgs
let
args
''
= args ++ args'
(_,
Just
out, _, p) <- createProcess (proc
"locate"
args
''
){ std_in = Inherit,
std_out = CreatePipe,
std_err = Inherit }
hSetBuffering out LineBuffering
(,) <$> hGetContents out <*>
return
p
改进后的程序,不会等待进程结束,而是返回输出和进程句柄。进程句柄用来等待子进程结束,同时获取退出状态。至于那个管道就不关闭了,留给操作系统解决好了。
main =
do
(out, p) <- doLocate
putStr
$ transform out
waitForProcess p >>= exitWith
改进版的完整程序在此。
9 年前
期待你再改一下 indet/haskell.vim ,
单行 where 缩进两格,下一行缩进两格
do 也是缩进两格
两格 = shiftwidth / 2
像 emacs 的 haskell-mode 那样
f = f
where
f = f
g = do
return f
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK