Nascondere l'input dell'utente sul terminale nello script Linux


121

Ho uno script bash come il seguente:

#!/bin/bash

echo "Please enter your username";
read username;

echo "Please enter your password";
read password;

Voglio che quando l'utente digita la password sul terminale, non dovrebbe essere visualizzata (o qualcosa come *******) dovrebbe essere visualizzato). Come ottengo questo?


2
Nota generale solo per evitare confusione: questo nome utente / password non ha nulla a che fare con il nome utente / password di Linux - Sto solo cercando un modo per nascondere i dati che l'utente digita durante la "lettura della password".

Ho aggiunto un aggiornamento per se vuoi essere fantasioso *
inviando l'

Grazie mille Una domanda se qualcuno lo sa: questo impedirà automaticamente che entri in .bash_history?


2
Non credo che lo farà, bash_history cattura solo il tuo comando, cosa succede dopo aver eseguito il tuo comando non cattura.
Andreas Wong

Risposte:


272

Fornisci solo -s alla tua chiamata di lettura in questo modo:

$ read -s PASSWORD
$ echo $PASSWORD

9
Mi piace di più così. Ti
voterei

4
Per fornire il contesto: non-s visualizza nulla durante la digitazione dell'input. ( -sè un'estensione non POSIX, quindi non tutte le shell la supportano, come la ashshell fornita con BusyBox; usa l' ssty -echoapproccio in tali shell)
mklement0

3
@electron Niente nella manpagina suggerisce effettivamente di -simpostare il colore del testo sullo stesso colore dello sfondo. Semplicemente "echeggia silenziosamente". ss64.com/bash/read.html Silent mode. If input is coming from a terminal, characters are not echoed.
Andreas Wong

2
@electron ho testato sulla mia scatola, non solo non sposta il cursore, quando cerco di evidenziare la riga in cui ho digitato la password, non riesco a incollarla in seguito. Entrambi dovrebbero accadere se ciò che dice il libro è vero. Immagino che forse un diverso sapore di shell (stavo usando bash 4.1.2 (1)).
Andreas Wong

1
@DominykasMostauskis sì, ma l'etichetta è per bash, quindi la soluzione. Probabilmente ci sono tonnellate di varianti di shell in cui questo non funziona :)
Andreas Wong

25

Aggiornare

Nel caso in cui desideri essere fantasioso emettendo un *per ogni carattere che digita, puoi fare qualcosa del genere (usando la read -ssoluzione di andreas ):

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

Senza essere fantasioso

echo "Please enter your username";
read username;

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

Dovrebbe essere IFS=$'\n'così che tu possa effettivamente finire di digitare la password. Altrimenti bello; molto intelligente.
sorpigal

2
Non è necessario impostare IFS=$'\n'perché il delimitatore predefinito di lettura è -d $'\n'. L'istruzione if da interrompere su una stringa nul, che accade su una nuova riga, è ciò che consente loro di completare la password.
SiegeX

Durante i test su FreeBSD con bash 3.x trovo che non sia così. Testare su Linux con 3.1.17 produce i risultati. Non ho una scatola BSD a portata di mano al momento ma cercherò di confermarlo domani, solo per amor mio.
sorpigal

@ Sorpigal: interessante, fammi sapere cosa scopri. Sono tutto sulla portabilità
SiegeX

2
Ho capito. Il mio test di FreeBSD era basato sul codice copiato e incollato dalla tua modifica originale errata del post di lesmana, che contiene un'importante differenza: avevi passato readun file -d ''. Quando l'ho riprovato più tardi su Linux ho usato la versione ripubblicata. Se provo a omettere, -d ''ovviamente funziona in modo identico su FreeBSD! In realtà sono abbastanza sollevato dal fatto che non ci sia una misteriosa magia della piattaforma al lavoro qui.
sorpigal

17

per una soluzione che funziona senza bash o alcune funzionalità readche puoi utilizzare sttyper disabilitare l'eco

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

9

Ecco una variazione dell'eccellente *soluzione di stampa di @ SiegeX bash con il supporto per backspace aggiunto; ciò consente all'utente di correggere la propria immissione con la backspacechiave ( deletechiave su un Mac) , come è tipicamente supportato dalle richieste di password:

#!/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:

  • Per quanto riguarda il motivo per cui premendo Backspace si registra il codice del carattere 0x7f: "Nei sistemi moderni, il tasto Backspace è spesso mappato sul carattere di cancellazione (0x7f in ASCII o Unicode)" https://en.wikipedia.org/wiki/Backspace
  • \b \bserve per dare l'impressione di cancellare il carattere a sinistra; semplicemente usando \bsposta il cursore a sinistra, ma lascia il carattere intatto ( backspace non distruttivo ). Stampando uno spazio e tornando indietro, il carattere sembra essere stato cancellato (grazie, il carattere di escape "backspace" "\ b" in C, comportamento inaspettato? ).

In una shell solo POSIX (ad esempio, shsu Debian e Ubuntu, dove si shtrova dash), usa l' stty -echoapproccio (che non è ottimale, perché non stampa nulla ), perché il readbuiltin non supporterà le opzioni -se -n.


Grazie, @Dylan. Mi sono appena reso conto che il codice per rimuovere l'ultimo carattere dalla password digitata finora può essere semplificato - vedi aggiornamento.
mklement0

3

Un po 'diverso dalla (ma soprattutto come) la risposta di @ lesmana

stty -echo
read password
stty echo

semplicemente: nascondi eco fai le tue cose mostra eco


Puoi spiegare perché questo è meglio della risposta di @ lesmana ? Vedo che è più semplice con meno overhead per un'altra sttychiamata e una variabile in meno sullo stack: toglie solo l'eco e ripristina solo l'eco. Esiste un modo in cui questo potrebbe sconvolgere altre sttyimpostazioni? Lesmana conserva tutte le impostazioni.
Jeff Puckett

2

Ecco una variazione della risposta di @ SiegeX che funziona con la shell Bourne tradizionale (che non ha supporto per gli +=incarichi).

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

Anche una shell Bourne tradizionale (o conforme a POSIX) non la riconoscerebbe [[ ... ]], né il suo readbuilt-in avrebbe le opzioni -se -n. Il tuo codice quindi non funzionerà dash, ad esempio. ( Funzionerà su piattaforme in cui shè effettivamente bash, come su OSX, dove bashquando viene richiamato shsemplicemente modifica alcune opzioni predefinite pur supportando la maggior parte dei bashismi.)
mklement0

2
Grazie per il feedback, sono passato al singolo [ma ovviamente non posso fare molto se readmancano queste opzioni.
tripleee

2

Mi piace sempre usare i caratteri di escape Ansi:

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

8mrende il testo invisibile e 0mripristina il testo su "normale". Il -e rende possibile la fuga di Ansi.

L'unico avvertimento è che puoi ancora copiare e incollare il testo che è lì, quindi probabilmente non dovresti usarlo se vuoi davvero sicurezza.

Permette solo alle persone di non guardare le tue password quando le digiti. Basta non lasciare il computer acceso dopo. :)


NOTA:

Quanto sopra è indipendente dalla piattaforma fintanto che supporta le sequenze di escape Ansi.

Tuttavia, per un'altra soluzione Unix, potresti semplicemente dire readdi non echeggiare i caratteri ...

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

1

Ottieni nome utente e password

Rendilo più chiaro da leggere ma mettilo in una posizione migliore sullo schermo

#!/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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.