我是purescript和haskell(甚至是javascript和节点)的新手,所以我很想知道如何在purescript中保存子进程的输出。我正在使用purescript-node-childprocess和purescript-node-fs模块。基本上,我遇到的问题是:
import Node.ChildProcess (CHILD_PROCESS, SpawnOptions, defaultSpawnOptions, Exit(..), spawn, onExit, stdout)
import Node.Stream (onData)
type CPEffect = forall e
. Eff (cp :: CHILD_PROCESS
, console :: CONSOLE
, err :: EXCEPTION
, buffer :: BUFFER | e
) Unit
-- | Basically a wrapper around the spawn command.
-- | Takes a command, an array of arguments, and a record of
-- | options to pass to spawn.
launch :: String -> Array String -> SpawnOptions -> CPEffect
launch cmd args opts = do
cmd' <- spawn cmd args opts
onExit cmd' defaultExitHdlr
-- My problem is with onData due to its return type
onData (stdout cmd') -- what do I put as the callback handler arg?
log $ "done with " <> cmd
如上面的评论所示,问题是来自Node.Stream模块的onData函数。问题出在第二个arg和返回类型:
onData :: forall w eff
. Readable w (err :: EXCEPTION | eff)
-> (Buffer -> Eff (err :: EXCEPTION | eff) Unit)
-> Eff (err :: EXCEPTION | eff) Unit
由于返回是返回Unit的Eff,如何保存子进程输出?第二个参数是一个函数,它接受一个Buffer并返回相同的类型。实际上,正是这个函数从Readable获取数据(这是子进程的stdout)。换句话说,第一个参数是来自子进程节点的stdout流,第二个参数是一个回调处理程序,它将从stdout流中填充缓冲区。
但是由于回调处理程序返回Unit,我看不出如何累积子进程的输出。我还在学习Monad变形金刚,这是一个解决方案吗?我可以创建一个以某种方式包装它的Writer monad吗?
答案 0 :(得分:4)
如果我要在不使用FFI的情况下在purescript中执行此操作,我将使用Ref,并在onData回调中读取ref值,通过将新数据附加到ref值来创建新字符串,然后使用这个新字符串更新ref值。
WriterT确实对这种事情很有用,虽然我可能不会在这种特殊情况下使用它,因为找出如何使类型排队可能有点尴尬,因为回调需要Eff,而不是WriterT Eff。
我用来解决Pulp中这个问题的另一个选择是使用npm包concat-stream
。事实上,您可能会发现Pulp作为使用这些库的真实示例很有用 - 我认为Pulp.Exec和Pulp.System.Stream模块是您感兴趣的模块。
答案 1 :(得分:2)
在pscid中,我正在使用STRef来连接进程的输出,并在进程退出时将其结果打印到控制台。我复制了相关代码,以便您有一个例子:
execCommand
∷ ∀ e
. String
→ String
→ Eff (cp ∷ CHILD_PROCESS, console ∷ CONSOLE | e) Unit
execCommand name command =
catchLog (name <> " threw an exception") $
runST do
let cmd = unsafePartial fromJust (uncons (split (Pattern " ") command))
output ← newSTRef ""
log ("Running: \"" <> command <> "\"")
cp ← spawn cmd.head cmd.tail defaultSpawnOptions
let stout = stdout cp
sterr = stderr cp
onDataString stout UTF8 \s →
modifySTRef output (_ <> s) $> unit
onDataString sterr UTF8 \s →
modifySTRef output (_ <> s) $> unit
onExit cp \e → case e of
Normally 0 → logColored Green (name <> " successful!")
Normally code → do
log =<< readSTRef output
logColored Red (name <> " errored with code: " <> show code)
BySignal _ → pure unit