Ocultar la entrada del usuario en el terminal en un script de Linux


121

Tengo un script bash como el siguiente:

#!/bin/bash

echo "Please enter your username";
read username;

echo "Please enter your password";
read password;

Quiero que cuando el usuario escriba la contraseña en el terminal, no se muestre (o se muestre algo como *******)). ¿Cómo logro esto?


2
Nota general solo para evitar confusiones: este nombre de usuario / contraseña no tiene nada que ver con el nombre de usuario / contraseña de Linux; solo estoy buscando una manera de ocultar los datos que el usuario escribe durante la "lectura de contraseña".

Agregué una actualización para si quieres ser elegante al generar *mientras
escriben

Muchas gracias. Una pregunta si alguien lo sabe: ¿evitará esto automáticamente que entre en .bash_history?


2
No creo que lo haga, bash_history solo captura su comando, lo que sucede después de ejecutar su comando no captura.
Andreas Wong

Respuestas:


272

Simplemente proporcione -s a su llamada de lectura así:

$ read -s PASSWORD
$ echo $PASSWORD

9
Me gusta más así. Te votaría si no alcanzara mi límite diario
SiegeX

4
Para proporcionar contexto: no-s muestra nada mientras se escribe la entrada. ( -ses una extensión que no es POSIX, por lo que no todos los shells la admiten, como el ashshell que viene con BusyBox; use el ssty -echoenfoque en tales shells)
mklement0

3
@electron Nada en la manpágina sugiere que -sestablezca el color del texto al mismo que el color de fondo. Simplemente "resuena silenciosamente". ss64.com/bash/read.html Silent mode. If input is coming from a terminal, characters are not echoed.
Andreas Wong

2
@electron Probé en mi caja, no solo no mueve el cursor, cuando trato de resaltar la línea donde escribí mi contraseña, parece que no puedo pegarla más tarde. Ambos deberían suceder si lo que dice el libro es cierto. Supongo que tal vez un sabor diferente de shell (estaba usando bash 4.1.2 (1)).
Andreas Wong

1
@DominykasMostauskis sí, pero la etiqueta es para bash, por lo tanto, la solución. Probablemente hay toneladas de variantes de shell donde esto no funciona :)
Andreas Wong

25

Actualizar

En caso de que quiera ser elegante al generar un *para cada carácter que escriben, puede hacer algo como esto (usando la read -ssolución de andreas ):

unset password;
while IFS= read -r -s -n1 pass; do
  if [[ -z $pass ]]; then
     echo
     break
  else
     echo -n '*'
     password+=$pass
  fi
done

Sin ser elegante

echo "Please enter your username";
read username;

echo "Please enter your password";
stty -echo
read password;
stty echo

Debería ser IFS=$'\n'para que pueda terminar de escribir la contraseña. De lo contrario agradable; muy inteligente.
Sorigal

2
No es necesario establecerlo IFS=$'\n'porque el delimitador predeterminado de lectura es -d $'\n'. La sentencia if para romper en una cadena nul, que ocurre en una nueva línea, es lo que les permite terminar la contraseña.
SiegeX

Al probar en FreeBSD con bash 3.x, encuentro que este no es el caso. Probar en Linux con 3.1.17 arroja sus resultados. No tengo una caja BSD a mano en este momento, pero intentaré confirmarlo mañana, solo por mi propia curiosidad.
Sorigal

@Sorpigal: interesante, déjame saber lo que averiguas. Me encanta la portabilidad
SiegeX

2
Lo tengo. Mi prueba de FreeBSD se basó en el código copiado y pegado de su edición errónea original de la publicación de lesmana, que contiene una diferencia importante: había estado pasando readun -d ''. Cuando lo intenté más tarde en Linux, usé la versión reenviada. Si intento omitir, -d ''por supuesto, ¡funciona de manera idéntica en FreeBSD! De hecho, estoy bastante aliviado de que no haya ninguna magia de plataforma misteriosa en funcionamiento aquí.
Sorigal

17

para una solución que funcione sin bash o ciertas características readque puede usar sttypara deshabilitar el eco

stty_orig=$(stty -g)
stty -echo
read password
stty $stty_orig

9

Aquí hay una variación de la excelente *solución de impresión de @ SiegeX bash con soporte para retroceso agregado; esto permite al usuario corregir su entrada con la backspaceclave ( deleteclave en una Mac) , como suele ser compatible con las indicaciones de contraseña:

#!/usr/bin/env bash

password=''
while IFS= read -r -s -n1 char; do
  [[ -z $char ]] && { printf '\n'; break; } # ENTER pressed; output \n and break.
  if [[ $char == $'\x7f' ]]; then # backspace was pressed
      # Remove last char from output variable.
      [[ -n $password ]] && password=${password%?}
      # Erase '*' to the left.
      printf '\b \b' 
  else
    # Add typed char to output variable.
    password+=$char
    # Print '*' in its stead.
    printf '*'
  fi
done

Nota:

  • En cuanto a por qué al presionar la tecla de retroceso se registra el código de carácter 0x7f: "En los sistemas modernos, la tecla de retroceso a menudo se asigna al carácter de eliminación (0x7f en ASCII o Unicode)" https://en.wikipedia.org/wiki/Backspace
  • \b \bes necesario para dar la apariencia de borrar el carácter de la izquierda; con solo usar \bmueve el cursor a la izquierda, pero deja el carácter intacto ( retroceso no destructivo ). Al imprimir un espacio y retroceder nuevamente, el carácter parece haber sido borrado (gracias, El carácter de escape "retroceso" '\ b' en C, ¿comportamiento inesperado? ).

En un shell de solo POSIX (por ejemplo, shen Debian y Ubuntu, donde shestá dash), use el stty -echoenfoque (que es subóptimo, porque no imprime nada ), porque el readbuiltin no admitirá las opciones -sy -n.


Gracias, @Dylan. Me acabo de dar cuenta de que el código para eliminar el último carácter de la contraseña escrita hasta ahora se puede simplificar; consulte la actualización.
mklement0

3

Un poco diferente de (pero sobre todo como) la respuesta de @ lesmana

stty -echo
read password
stty echo

simplemente: ocultar el eco haz tus cosas mostrar eco


¿Puede explicar por qué esto es mejor que la respuesta de @lesmana ? Veo que es más simple con menos gastos generales para otra sttyllamada y una variable menos en la pila: solo elimina el eco y solo restaura el eco. ¿Hay alguna forma posible de que esto pueda alterar otras sttyconfiguraciones? Lesmana conserva todos los escenarios.
Jeff Puckett

2

Aquí hay una variación de la respuesta de @ SiegeX que funciona con el shell Bourne tradicional (que no tiene soporte para +=asignaciones).

password=''
while IFS= read -r -s -n1 pass; do
  if [ -z "$pass" ]; then
     echo
     break
  else
     printf '*'
     password="$password$pass"
  fi
done

Un shell Bourne tradicional (o uno compatible con POSIX) tampoco reconocería [[ ... ]], ni su readincorporado tendría las opciones -sy -n. Por lo tanto, su código no funcionará en dash, por ejemplo. (Se trabajará en las plataformas donde shen realidad es bash, como en OSX, donde bashcuando se invoca como shmeramente modifica algunas opciones por defecto, mientras que la mayoría sigue apoyando bash.)
mklement0

2
Gracias por los comentarios, cambié a single [pero obviamente no puedo hacer mucho si no tienes readestas opciones.
tripleee

2

Siempre me gusta usar caracteres de escape Ansi:

echo -e "Enter your password: \x1B[8m"
echo -e "\x1B[0m"

8mhace que el texto sea invisible y 0mrestablece el texto a "normal". La -e hace posible que Ansi se escape.

La única advertencia es que aún puede copiar y pegar el texto que está allí, por lo que probablemente no debería usar esto si realmente desea seguridad.

Simplemente permite que las personas no miren sus contraseñas cuando las escribe. Simplemente no deje su computadora encendida después. :)


NOTA:

Lo anterior es independiente de la plataforma siempre que sea compatible con las secuencias de escape de Ansi.

Sin embargo, para otra solución Unix, simplemente podría decirle readque no haga eco de los caracteres ...

printf "password: "
let pass $(read -s)
printf "\nhey everyone, the password the user just entered is $pass\n"

1

Obtener nombre de usuario y contraseña

Haga que sea más claro para leer, pero colóquelo en una mejor posición sobre la pantalla

#!/bin/bash
clear
echo 
echo 
echo
counter=0
unset username
prompt="  Enter Username:"
while IFS= read -p "$prompt" -r -s -n 1 char
do
    if [[ $char == $'\0' ]]; then
        break
    elif [ $char == $'\x08' ] && [ $counter -gt 0 ]; then
        prompt=$'\b \b'
        username="${username%?}"
        counter=$((counter-1))
    elif [ $char == $'\x08' ] && [ $counter -lt 1 ]; then
        prompt=''
        continue
    else
        counter=$((counter+1))
        prompt="$char"
        username+="$char"
    fi
done
echo
unset password
prompt="  Enter Password:"
while IFS= read -p "$prompt" -r -s -n 1 char
do
    if [[ $char == $'\0' ]]; then
        break
    elif [ $char == $'\x08' ] && [ $counter -gt 0 ]; then
        prompt=$'\b \b'
        password="${password%?}"
        counter=$((counter-1))
    elif [ $char == $'\x08' ] && [ $counter -lt 1 ]; then
        echo
        prompt="  Enter Password:"
        continue
    else
        counter=$((counter+1))
        prompt='*'
        password+="$char"
    fi
done
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.