2010-10-31 75 views
23

下面的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

回答

32

嘗試了下:

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

9

使用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

13

有在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

4

正如我上面的評論,我建議你使用haskeline,這是一個完整的提示圖書館。我愉快地使用它LambdaCalculator沒有投訴。

+0

任何想法爲什麼所有的Haskeline函數都返回'Maybe String'而不是'String'?沒有關於它的文檔,在我看來,它應該總是返回'String',就像標準的'getLine'一樣。 – jameshfisher 2013-12-23 23:25:32

+0

@ jameshfisher這對我來說並不是顯而易見的,不。這需要挖掘一些源頭。 – 2013-12-23 23:55:38

5

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