提示输入哈斯克尔命令行应用程序

问题描述:

下面的Haskell程序的密码提示用户在终端输入密码,并继续他是否已经进入了正确的:提示输入哈斯克尔命令行应用程序

main = do 
    putStrLn "Password:" 
    password <- getLine 

    case hash password `member` database of 
     False -> putStrLn "Unauthorized use!" 
     True -> do 
       ... 

不幸的是,密码将出现在当用户键入它时,我想要避免。

如何在不显示屏幕的情况下阅读用户键入的字符序列?为此目的,相当于getLine

我在MacOS X上,但我希望它能在Windows和Linux上工作。

+1

使用[haskeline](http://hackage.haskell.org/packages/archive/haskeline/0.6.3.1/doc/html/System -console-Haskeline.html#五:getPassword来)。 – 2010-10-31 22:02:18

+1

@TomMd:您的评论是对该问题的适当答案。为什么不把它作为一个真正的答案,以便它可以被投票,评论,接受,等等? – 2010-11-01 02:40:06

尝试了下:

module Main 
where 

import System.IO 
import Control.Exception 

main :: IO() 
main = getPassword >>= putStrLn . ("Entered: " ++) 

getPassword :: IO String 
getPassword = do 
    putStr "Password: " 
    hFlush stdout 
    pass <- withEcho False getLine 
    putChar '\n' 
    return pass 

withEcho :: Bool -> IO a -> IO a 
withEcho echo action = do 
    old <- hGetEcho stdin 
    bracket_ (hSetEcho stdin echo) (hSetEcho stdin old) action 
+3

工程就像一个魅力!你可以把它变成一个小的完整的'getPassword :: IO String'的例子,所以我可以让这个接受的答案? – 2010-10-31 19:48:42

+0

我编辑过的片段 – Yuras 2010-10-31 20:50:19

+0

太棒了,谢谢! – 2010-11-01 08:18:42

使用the System.Posix.Terminal module可以在终端中禁用回声。但是,这需要POSIX支持,所以可能无法在Windows上工作(我没有检查)。

import System.Posix.Terminal 
import System.Posix.IO (stdInput) 

getPassword :: IO String 
getPassword = do 
    tc <- getTerminalAttributes stdInput 
    setTerminalAttributes stdInput (withoutMode tc EnableEcho) Immediately 
    password <- getLine 
    setTerminalAttributes stdInput tc Immediately 
    return password 

main = do 
    putStrLn "Password:" 
    password <- getPassword 
    putStrLn "Name:" 
    name <- getLine 
    putStrLn $ "Your password is " ++ password ++ " and your name is " ++ name 

注意,标准输入是行缓冲,所以如果你使用putStr "Password:"而不是putStrLn,你需要先刷新缓冲区,否则提示会也有抑制作用。

+1

不错,有一个upvote!虽然,看起来来自'System.IO'的谦逊的'hSetEcho'会起作用。 :-) – 2010-10-31 20:02:13

+0

@海因里希:啊。太谦卑,值得注意:P – kennytm 2010-10-31 20:21:47

有在System.Console.Haskeline一个getPassword。可能对你的情况有点矫枉过正,但有人会觉得它很有用。

一个例子:

> runInputT defaultSettings $ do {p <- getPassword (Just '*') "pass:"; outputStrLn $ fromJust p} 
pass:*** 
asd 
+1

不错!我不知道'haskeline'库。 – 2010-11-01 08:21:03

+1

任何想法为什么所有的Haskeline函数都返回'Maybe String'而不是'String'?没有关于它的文档,在我看来,它应该总是返回'String',就像标准的'getLine'一样。 – jameshfisher 2013-12-23 23:25:16

+0

这是因为标准'getLine'是一个局部函数,当接收到一个'EOT'时会引发异常。 haskeline会返回'Nothing',允许你在有空行的时候做一些不同的事情,而不是'^ D' – berdario 2016-10-14 09:41:37

正如我上面的评论,我建议你使用haskeline,这是一个完整的提示图书馆。我愉快地使用它LambdaCalculator没有投诉。

+0

任何想法为什么所有的Haskeline函数都返回'Maybe String'而不是'String'?没有关于它的文档,在我看来,它应该总是返回'String',就像标准的'getLine'一样。 – jameshfisher 2013-12-23 23:25:32

+0

@ jameshfisher这对我来说并不是显而易见的,不。这需要挖掘一些源头。 – 2013-12-23 23:55:38

withEcho可以用少一点在降噪被写成:

withEcho :: Bool -> IO a -> IO a 
withEcho echo action = 
    bracket (hGetEcho stdin) 
      (hSetEcho stdin) 
      (const $ hSetEcho stdin echo >> action) 
+1

我认为这对我来说太聪明了,我必须很难弄清楚它是如何工作的。更好的解决办法可能是将“暂时更改设置的行为动作”行为分解为类似于'withTemp :: a - > IO a - >(a - > IO b) - > IO c - > IO c', withtemmp tempValue getter setter action = ...'以便您可以定义'withEcho echo = withTemp echo(hGetEcho stdin)(hSetEcho stdin)'。 – 2014-02-22 23:40:50