簡體   English   中英

在haskell程序中運行vi(處理pty)

[英]Running vi from within haskell program (dealing with ptys)

我正在嘗試編寫一個日志外殼程序; 例如,它捕獲有關以結構化格式運行的命令的數據。 為此,我使用readline讀取命令,然后在子shell中執行命令,同時捕獲諸如所花費的時間,環境,退出狀態等信息。

到目前為止,一切都很好。 但是,最初嘗試從此日志記錄外殼程序中運行諸如viless嘗試失敗。 調查表明,要做的事情是建立一個偽tty,並將子外殼連接到該外殼,而不是連接到普通管道。 這使vi不再抱怨沒有連接到終端,但仍然失敗-我在屏幕上打印了一些廢話,並且命令在編輯器中以字符形式打印-例如'ESC'僅顯示^[

認為我需要做的是將pty置於原始模式。 為此,我嘗試了以下操作:

  pty <- do
    parentTerminal <- getControllingTerminalName >>= 
                      \a -> openFd a ReadWrite Nothing defaultFileFlags
    sttyp <- getTerminalAttributes parentTerminal
    (a, b) <- openPseudoTerminal
    let rawModes = [ProcessInput, KeyboardInterrupts, ExtendedFunctions, 
                    EnableEcho, InterruptOnBreak, MapCRtoLF, IgnoreBreak, 
                    IgnoreCR, MapLFtoCR, CheckParity, StripHighBit, 
                    StartStopOutput, MarkParityErrors, ProcessOutput]
        sttym = withoutModes rawModes sttyp
        withoutModes modes tty = foldl withoutMode tty modes
    setTerminalAttributes b sttym Immediately
    setTerminalAttributes a sttym Immediately
    a' <- fdToHandle a
    b' <- fdToHandle b
    return (a',b')

例如,我們獲得父終端的屬性,刪除我認為與將tty設置為原始模式相對應的各種標志(基於此代碼System.Posix.Terminal的haddock ),然后在pty的兩側進行設置。

然后,我使用createProcess在shell中啟動一個進程,並使用waitForProcess連接到它,為子進程的stdin和stdout句柄提供pty的從屬端:

eval :: (Handle, Handle) -> String -> IO ()
eval pty command = do
    let (ptym, ptys) = pty
    (_, _, hErr, ph) <- createProcess $ (shell command) { 
          delegate_ctlc = True
        , std_err = CreatePipe
        , std_out = UseHandle ptys
        , std_in = UseHandle ptys
      }
    snipOut <- tee ptym stdout
    snipErr <- sequence $ fmap (\h -> tee h stderr) hErr
    exitCode <- waitForProcess ph
    return ()
  where tee :: Handle -> Handle -> IO B.ByteString
        tee from to = DCB.sourceHandle from
            $= DCB.conduitHandle to -- Sink contents to out Handle
            $$ DCB.take 256 -- Pull off the start of the stream

這肯定會更改終端設置(已通過stty確認),但不能解決問題。 我想念什么嗎? 是否需要設置屬性的其他設備?

編輯:完整的可運行代碼可在https://github.com/nc6/tabula上獲得 -我為這篇文章簡化了一些內容。

這是創建vi過程的方式:

(_, _, hErr, ph) <- createProcess $ (shell command) { 

這些返回值是stdin/stdout/stderr 您丟棄了stdin/stdout (並保留了stderr )。 您將需要那些與vi進行交流的人。 基本上,當您輸入ESC ,它甚至都沒有進入該過程。

作為更大的體系結構筆記-您不僅在重寫終端代碼,而且在重寫完整的REPL / shell腳本。...這是一個比您可能想參與的項目還要大的項目(請閱讀bash手冊以了解它們的所有內容)。需要實施)。 您可能需要考慮包裝用戶可選擇的Shell腳本(如bash)。 Unix以這種方式非常模塊化,這就是為什么xterm,ssh,命令提示符等都以相同的方式工作的原因-它們代理選定的shell腳本,而不是各自編寫自己的腳本。

@jamshidh指出我實際上並沒有將stdin連接到pty的主端,所以我遇到的問題與vi或終端模式無關,而與不傳遞任何輸入完全有關!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM