Подтвердить что ты не робот

Запрос пароля в приложении командной строки 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.

4b9b3361

Ответ 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 без каких-либо жалоб.