Запрос пароля в приложении командной строки Haskell
Следующая программа 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
Сделай это:
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
Ответ 2
В System.Console.Haskeline
есть getPassword
. Вероятно, это излишний случай для вашего дела, но кто-то может найти его полезным.
Пример:
> runInputT defaultSettings $ do {p <- getPassword (Just '*') "pass:"; outputStrLn $ fromJust p}
pass:***
asd
Ответ 3
Можно отключить эхо в терминале с помощью модуля System.Posix.Terminal
. Однако для этого требуется поддержка 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
Обратите внимание, что stdin буферизируется по строке, поэтому, если вы используете putStr "Password:"
вместо putStrLn
, вам нужно сначала сбросить буфер, иначе также будет заблокирован запрос.
Ответ 4
withEcho можно записать с меньшим количеством шумов:
withEcho :: Bool -> IO a -> IO a
withEcho echo action =
bracket (hGetEcho stdin)
(hSetEcho stdin)
(const $ hSetEcho stdin echo >> action)
Ответ 5
Как я уже говорил выше, я предлагаю вам использовать haskeline, который представляет собой полную библиотеку приглашений. Я использовал его для LambdaCalculator без каких-либо жалоб.