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.