如何将`Shell Text`转换为`Shell Line`?

时间:2018-02-08 19:53:27

标签: haskell haskell-turtle

在用于从密钥环获取密码的Turtle脚本中,使用这些密码调用ssh-add以便他们不必手动填写,具有以下功能:

processKey :: (T.Text, T.Text) -> Shell Line -> IO ()
processKey (kn, str) pwds = do
    -- Not sure why (text str) is needed here, but it won't typecheck without
    -- it (despite OverloadedStrings).
    let expectArg = do
        ml <- grep (contains (text str)) pwds
        let pass = getPwd $ cut tab (format l ml)
        return $ T.unlines [ "<< EOF"
                  , "spawn ssh-add"
                  , "expect \"Enter passphrase\""
                  , "send " <> pass
                  , "expect eof"
                  , "EOF"
                  ]
    view (inproc "expect" [] expectArg)
    where
       -- Safely get the third item `cut` from the list.
       getPwd xs = getPwd' 0 xs
       getPwd' _ []     = ""
       getPwd' n (x:xs) = if n == 2
                          then x
                          else getPwd' (n+1) xs

此函数采用(SSH密钥文件名,要在密钥环中存储的文本中搜索的字符串)和pwds :: Shell Line的元组,这是从shell命令获取的密钥环的全部内容。

该功能的目的是grep密码,并使用密钥文件名和密码调用ssh-add

问题是这个函数没有键入检查:

sshkeys-autopass.hs:45:30: error:
    • Couldn't match type ‘Text’ with ‘Line’
      Expected type: Shell Line
        Actual type: Shell Text
    • In the third argument of ‘inproc’, namely ‘expectArg’
      In the first argument of ‘view’, namely
        ‘(inproc "expect" [] expectArg)’
      In a stmt of a 'do' block: view (inproc "expect" [] expectArg)
   |
45 |     view (inproc "expect" [] expectArg)
   |                              ^^^^^^^^^

似乎Shell Line需要成为Shell Text,请问怎么办呢?我对这种结构很糟糕或者不是惯用的Haskell(它确实有异味)的可能性持开放态度,如果是这样的话请告诉我这个功能如何更好。

1 个答案:

答案 0 :(得分:5)

虽然我现在无法试用您的代码,但似乎通过Text(因为T.unlines迫使您)导致的命令往往会导致不必要的麻烦。根据{{​​3}}(强调我的):

  

(Shell a)a带有副作用的受保护

由于Shell Line是一个流,它可以提供多个Line。当然,有一个名为to the documentation ...

的函数
  

select :: Foldable f => f a -> Shell a

...将列表(或任何其他Foldable)转换为Shell。您可以使用它直接获得所需的Shell Line

{-# LANGUAGE OverloadedStrings #-}
-- etc.
    let expectArg = do
        ml <- grep (contains (text str)) pwds
        -- unsafeTextToLine is presumably safe here,
        -- as ml was a Line to begin with.
        let pass = unsafeTextToLine . getPwd $ cut tab (format l ml)
        select [ "<< EOF"
            , "spawn ssh-add"
            , "expect \"Enter passphrase\""
            , "send " <> pass
            , "expect eof"
            , "EOF"
            ]
    view (inproc "expect" [] expectArg)

附带问题:

  

不确定为什么(text str)在这里需要,但如果没有它就不会进行类型检查(尽管有OverloadedStrings)。

OverloadedStrings唯一自动处理的是字符串文字。它无法将Text值静默转换为IsString的其他实例。使用text的替代方法是更改​​您的签名,以使str的类型为Pattern Text而不是Text

  

从列表中安全地获取第三项cut

这是一种编写getPwd的方法,无需使用select中的一些函数显式编写递归算法:

getPwd = fromMaybe "" . listToMaybe . drop 2

您可能也喜欢 safe 包中的Data.Maybe

getPwd xs = atDef "" xs 2
  

但这会导致另一个问题:inproc不想要Shell (NonEmpty Line)。我不知道该怎么做。

atDef是保证至少包含一个元素的列表类型。在你的情况下,缺乏一种合理的方式从NonEmpty Line转到Line(连接元素或选择第一个元素,例如,根本没有帮助)是一个信号,改变方法是必要的。