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

use*_*448 1 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
Run Code Online (Sandbox Code Playgroud)

此函数采用一个元组(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)
   |                              ^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

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

dup*_*ode 5

虽然我现在无法尝试你的代码,但似乎通过Text(因为T.unlines强迫你)引导命令会导致不必要的麻烦.根据文件(强调我的):

(Shell a)是一个受保护的a的副作用

作为一个Shell Line流,它可以提供多个Lines.当然,还有一个叫做select...... 的功能......

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

...将列表(或任何其他Foldable)转换为a 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)
Run Code Online (Sandbox Code Playgroud)

附带问题:

不知道为什么(text str)需要这里,但没有它就不会进行类型检查(尽管是OverloadedStrings).

唯一OverloadedStrings自动的是处理字符串文字.它不会以静默方式将Text值转换为其他实例IsString.用另一种text会改变你的签名,这样的类型strPattern Text而非Text.

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

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

getPwd = fromMaybe "" . listToMaybe . drop 2
Run Code Online (Sandbox Code Playgroud)

你可能还喜欢atDef安全包:

getPwd xs = atDef "" xs 2
Run Code Online (Sandbox Code Playgroud)

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

NonEmpty是保证至少有一个元素的列表的类型.在你的情况下,缺乏从去一个明智的方式NonEmpty LineLine(串联的元素或者选择第一个,比如,不会在所有帮助)是一个信号的方法的改变是必要的.