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

How can a `Shell Text` be converted to `Shell Line`?

在用于从密钥环中获取密码的 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,即从 [=31 中获取的密钥环的全部内容=]命令。

该函数的目的是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(它确实有味道),如果是这样,请告知如何改进此功能。

虽然我现在无法试用您的代码,但似乎通过 Text 来回传递您的命令(因为 T.unlines 迫使您这样做)会造成不必要的麻烦。根据 to the documentation(强调我的):

A (Shell a) is a protected stream of a's with side effects

由于 Shell Line 是一个流,它可以提供多个 Line。果然,有一个函数叫做 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)

附带问题:

Not sure why (text str) is needed here, but it won't typecheck without it (despite OverloadedStrings).

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

Safely get the third item cut from the list.

这是一种不用显式编写递归算法就可以编写 getPwd 的方法,它使用了 Data.Maybe 中的几个函数:

getPwd = fromMaybe "" . listToMaybe . drop 2

您可能还喜欢 safe 包中的 atDef

getPwd xs = atDef "" xs 2

But that causes another problem: inproc does not want a Shell (NonEmpty Line). I have no idea what to do about this.

NonEmpty 是一种保证至少有一个元素的列表类型。在您的情况下,缺乏从 NonEmpty LineLine 的明智方法(例如,连接元素或选择第一个元素根本无济于事)是一个变化的信号方法是必要的。