Ocultando entrada do usuário no terminal no script Linux

121

Eu tenho o script bash como o seguinte:

#!/bin/bash

echo "Please enter your username";
read username;

echo "Please enter your password";
read password;

Quero que, quando o usuário digitar a senha no terminal, ela não seja exibida (ou algo como *******) seja exibido). Como faço para conseguir isso?

jww
fonte
2
Nota geral apenas para evitar confusão: esse nome de usuário / senha não tem nada a ver com o nome de usuário / senha linux - estou apenas procurando uma maneira de ocultar os dados digitados pelo usuário durante a "leitura da senha".
Eu adicionei uma atualização para se você quiser obter extravagantes, *enquanto digita a senha.
SiegeX
Muito obrigado. Uma pergunta, se alguém souber - isso impedirá automaticamente que entre no .bash_history?
2
Eu acho que não, bash_history captura apenas o seu comando, o que acontece depois de executar o seu comando não captura.
Andreas Wong

Respostas:

271

Basta fornecer -s à sua chamada de leitura da seguinte forma:

$ read -s PASSWORD
$ echo $PASSWORD
Andreas Wong
fonte
9
Eu gosto desse jeito melhor. Eu votaria-lo Se eu não bati meu limite diário
SiegeX
4
Para fornecer contexto: não-s exibe nada enquanto a entrada é digitada. ( -sÉ uma extensão não-POSIX, nem todas as conchas apoiá-lo, como o ashshell que vem com o BusyBox; usar a ssty -echoabordagem em tais conchas)
mklement0
3
@electron Na manverdade, nada na página sugere que -sdefina a cor do texto para a mesma cor de fundo. Apenas "ecoa silenciosamente". ss64.com/bash/read.html Silent mode. If input is coming from a terminal, characters are not echoed.
Andreas Wong
2
@electron Eu testei na minha caixa, não só não move o cursor, quando tento destacar a linha em que digitei minha senha, não consigo colá-la mais tarde. Ambos devem acontecer se o que o livro diz for verdadeiro. Suponho que talvez tenha um sabor diferente de casca (eu estava usando o bash 4.1.2 (1)).
Andreas Wong
1
@DominykasMostauskis sim, mas o rótulo é para bash, portanto, a solução. Há provavelmente toneladas de shell variantes onde esta não funcionam :)
Andreas Wong
25

Atualizar

Caso você deseje ter uma fantasia, imprimindo um *para cada caractere digitado, você pode fazer algo assim (usando a read -ssolução 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

Sem ser chique

echo "Please enter your username";
read username;

echo "Please enter your password";
stty -echo
read password;
stty echo
SiegeX
fonte
Deve ser IFS=$'\n'por isso que você pode realmente terminar de digitar a senha. Caso contrário, agradável; muito esperto.
sorpigal
2
Não é necessário definir IFS=$'\n'porque o delimitador padrão da leitura é -d $'\n'. A instrução if para quebrar uma string nul, que acontece em uma nova linha, é o que permite que eles terminem a senha.
SiegeX 30/11/10
Nos testes no FreeBSD com o bash 3.x, acho que esse não é o caso. Testar no Linux com 3.1.17 produz seus resultados. Eu não tenho uma caixa de BSD à mão no momento, mas tentarei confirmar isso amanhã, apenas por minha curiosidade.
sorpigal
@ Escorpigal: interessante, deixe-me saber o que você descobre. Eu sou tudo sobre portabilidade
SiegeX 30/11/2010
2
Eu entendi. Meu teste FreeBSD foi baseado no código copiado e colado de sua edição equivocada original post de Lesmana, que contém uma diferença importante: você estivesse passando readum -d ''. Quando tentei novamente mais tarde no Linux, usei a versão republicada. Se eu tentar omitir -d '', é claro que funciona de forma idêntica no FreeBSD! Estou realmente bastante aliviado por não haver nenhuma mágica misteriosa de plataforma em ação aqui.
sorpigal
17

para uma solução que funcione sem o bash ou com certos recursos, readvocê pode usar sttypara desativar o eco

stty_orig=$(stty -g)
stty -echo
read password
stty $stty_orig
lesmana
fonte
9

Aqui está uma variação da excelente *solução de impressão do @ SiegeX, bash com suporte para backspace adicionado; isso permite que o usuário corrija sua entrada com a backspacechave ( deletechave em um Mac) , como normalmente é suportado por solicitações de senha:

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

  • Quanto ao motivo pelo qual pressionar backspace registra o código de caractere 0x7f: "Nos sistemas modernos, a tecla backspace é frequentemente mapeada para o caractere de exclusão (0x7f em ASCII ou Unicode)" https://en.wikipedia.org/wiki/Backspace
  • \b \bé necessário para dar a aparência de excluir o caractere para a esquerda; apenas usar \bmove o cursor para a esquerda, mas deixa o caractere intacto ( backspace não destrutivo ). Ao imprimir um espaço e voltar novamente, o caractere parece ter sido apagado (obrigado, o caractere de escape "backspace" '\ b' em C, comportamento inesperado? ).

Em um shell somente POSIX (por exemplo, shno Debian e Ubuntu, onde shestá dash), use a stty -echoabordagem (que é subótima, porque não imprime nada ), porque o readbuiltin não suporta as opções -se -n.

mklement0
fonte
Obrigado, @Dylan. Acabei de perceber que o código para remover o último caractere da senha digitada até agora pode ser simplificado - veja a atualização.
usar o seguinte comando
3

Um pouco diferente da resposta de (mas principalmente como) @ lesmana

stty -echo
read password
stty echo

simplesmente: esconda eco, faça seu material mostre eco

yerlilbilgin
fonte
Você pode explicar por que isso é melhor do que a resposta de @ lesmana ? Vejo que é mais simples, com menos sobrecarga para outra sttychamada e uma variável a menos na pilha - apenas retirando o eco e restaurando o eco. Existe alguma maneira possível de perturbar outras sttyconfigurações? O Lesmana preserva todas as configurações.
Jeff Puckett
2

Aqui está uma variação da resposta do @ SiegeX que funciona com o shell Bourne tradicional (que não tem suporte para +=atribuições).

password=''
while IFS= read -r -s -n1 pass; do
  if [ -z "$pass" ]; then
     echo
     break
  else
     printf '*'
     password="$password$pass"
  fi
done
triplo
fonte
Um shell Bourne tradicional (ou um compatível com POSIX) também não reconheceria [[ ... ]], nem seu readbuilt-in teria as opções -se -n. Seu código, portanto, não funcionará dash, por exemplo. (Ele vai trabalhar em plataformas onde shé, na verdade bash, como no OSX, onde bash, quando invocado como shmeramente modifica algumas opções padrão e ainda dar suporte a maioria dos bashisms.)
mklement0
2
Obrigado pelo feedback, mudei para single, [mas obviamente não posso fazer muito se o seu readnão possuir essas opções.
Tripleee
2

Eu sempre gosto de usar caracteres de escape Ansi:

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

8mtorna o texto invisível e 0mredefine o texto para "normal". O -e torna possível a fuga de Ansi.

A única ressalva é que você ainda pode copiar e colar o texto existente, portanto, provavelmente não deve usá-lo se realmente deseja segurança.

Ele apenas permite que as pessoas não vejam suas senhas quando você as digita. Apenas não deixe o computador ligado posteriormente. :)


NOTA:

O acima é independente da plataforma, desde que ele suporte sequências de escape do Ansi.

No entanto, para outra solução Unix, você pode simplesmente dizer readpara não ecoar os caracteres ...

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

Obter nome de usuário e senha

Torne a leitura mais clara, mas coloque-a em uma posição melhor sobre a tela

#!/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
MikeW
fonte