1

在用于从密钥环获取密码的 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 的可能性(它确实有味道),如果是这样,请告知这个功能如何更好。

4

1 回答 1

5

虽然我现在无法试用您的代码,但似乎将您的命令往返TextT.unlines强制您这样做)会造成不必要的麻烦。根据文档(强调我的):

A(Shell a)是具有副作用的's 的受保护a

由于 aShell Line是一个流,它可以提供多个Lines。果然,有一个函数叫做select...

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是更改​​您的签名,以便类型为strisPattern Text而不是Text.

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

这是一种getPwd无需显式编写递归算法的编写方式,使用以下几个函数Data.Maybe

getPwd = fromMaybe "" . listToMaybe . drop 2

您可能还喜欢安全atDef中的:

getPwd xs = atDef "" xs 2

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

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

于 2018-02-09T00:34:46.750 回答