2010-10-31 10 views
23

El siguiente programa Haskell le pide al usuario una contraseña en el terminal y continúa si ha entrado en la correcta:se pida la contraseña en la línea de comandos de Haskell

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

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

Desafortunadamente, aparecerá la contraseña en el pantalla a medida que el usuario la escribe, lo cual quiero evitar.

¿Cómo puedo leer una secuencia de caracteres que los usuarios escriben sin que aparezcan en la pantalla? ¿Cuál es el equivalente de getLine para este propósito?

Estoy en MacOS X, pero me gustaría que esto funcione en Windows y Linux, también.

+1

Utilice [haskeline] (http://hackage.haskell.org/packages/archive/haskeline/0.6.3.1/doc/html/System -Console-Haskeline.html # v: getPassword). –

+1

@TomMd: su comentario es la respuesta adecuada a la pregunta. ¿Por qué no convertirlo en una respuesta real para que pueda ser votado, comentado, aceptado, etc.? –

Respuesta

32

pruebe el siguiente:

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

¡Funciona como un amuleto! ¿Podría convertir esto en un pequeño ejemplo completo de 'getPassword :: IO String', para que pueda hacer de esto la respuesta aceptada? –

+0

He editado el fragmento – Yuras

+0

¡Impresionante, gracias! –

9

Es posible desactivar el eco en el terminal con the System.Posix.Terminal module. Sin embargo, esto requiere soporte POSIX, por lo que puede no funcionar en Windows (no lo compruebe).

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 

Tenga en cuenta que la entrada estándar es la línea de búfer, por lo que si se utiliza en lugar de putStr "Password:"putStrLn, es necesario vaciar el búfer en primer lugar, de lo contrario el indicador se inhibirá también.

+1

¡Bien, tenga un voto favorable! Parece que el humilde 'hSetEcho' de' System.IO' hará el truco, sin embargo. :-) –

+0

@Heinrich: Ah. Demasiado humilde para ser notado: P – kennytm

13

Hay una getPassword en System.Console.Haskeline. Probablemente sea un exceso para su caso, pero a alguien le puede resultar útil.

Un ejemplo:

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

¡Agradable! No sabía acerca de la biblioteca 'haskeline'. –

+1

¿Alguna idea de por qué todas las funciones Haskeline devuelven 'Maybe String' en vez de' String'? No hay documentación sobre él, y me parece que siempre debe devolver 'String', como el estándar' getLine'. – jameshfisher

+0

Eso se debe a que el 'getLine' estándar es una función parcial, que aumentará la excepción al recibir un' EOT'. haskeline en cambio devolverá 'Nothing', lo que le permite hacer algo diferente cuando tiene una línea vacía, en comparación con'^D' – berdario

4

Como ya he comentado anteriormente, le sugiero que utilice haskeline, que es una biblioteca rápida completa. Lo he usado felizmente para LambdaCalculator sin quejas.

+0

¿Alguna idea de por qué todas las funciones de Haskeline devuelven 'Maybe String' en lugar de' String'? No hay documentación sobre él, y me parece que siempre debe devolver 'String', como el estándar' getLine'. – jameshfisher

+0

@jameshfisher No es inmediatamente obvio para mí, no. Tomaría algo de excavación en la fuente. –

5

withEcho se puede escribir con un poco menos ruido:

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

Creo que esto es demasiado astuto para mí, tengo que mirar bastante duro para descubrir cómo funciona esto. Una mejor solución puede ser factorizar el comportamiento 'ejecutar acción con configuración cambiada temporalmente' en algo como 'withTemp :: a -> IO a -> (a -> IO b) -> IO c -> IO c',' withTemp tempValue getter setter action = ... 'para que pueda definir' withEcho echo = withTemp echo (hGetEcho stdin) (hSetEcho stdin) '. –

Cuestiones relacionadas