Em scripts Bash, a entrada de dados do usuário via teclado é fundamental para a interatividade. O comando interno read é a ferramenta primária para essa finalidade, permitindo capturar informações digitadas e atribuí-las a variáveis.
1. Leitura Básica de Entrada
A sintaxe fundamental do comando read é:
read [-p "prompt"] [VARIAVEL1 VARIAVEL2...]
A opção -p permite exibir uma mensagem de prompt para o usuário antes que a entrada seja solicitada. Isso é útil para guiar o usuário sobre qual tipo de informação é esperada. O comando read lê uma linha completa da entrada padrão (ou de um descritor de arquivo especificado com -u). A primeira "palavra" digitada é atribuída à VARIAVEL1, a segunda à VARIAVEL2 e assim por diante.
As palavras são separadas com base nos caracteres definidos na variável de ambiente IFS (Internal Field Separator). Se o número de palavras digitadas for menor que o número de variáveis especificadas, as variáveis restantes são definidas como vazias.
Considere o seguinte exemplo para coletar informações básicas do usuário e confirmar a operação:
#!/bin/bash
# Exemplo de uso básico do comando read
read -p "Por favor, digite seu nome: " nome_completo
read -p "Agora, digite seu endereço de e-mail: " email_contato
read -p "Deseja realmente continuar? [s/n]: " resposta
case "$resposta" in
[sS]|[sS][iI][mM])
echo "Confirmação recebida."
echo "Nome: $nome_completo"
echo "E-mail: $email_contato"
;;
[nN]|[nN][aA][oO])
echo "Operação cancelada."
exit 0
;;
*)
echo "Entrada inválida. Por favor, digite 's' ou 'n'."
exit 1
;;
esac
Ao executar este script, ele solicitará o nome e o e-mail, e em seguida pedirá uma confirmação. A saída será baseada na entrada do usuário:
$ ./script_entrada_basica.sh
Por favor, digite seu nome: Alice
Agora, digite seu endereço de e-mail: alice@exemplo.com
Deseja realmente continuar? [s/n]: s
Confirmação recebida.
Nome: Alice
E-mail: alice@exemplo.com
2. Entrada com Tempo Limite (Timeout)
A opção -t do comando read permite definir um tempo limite em segundos para a entrada do usuário. Se o usuário não digitar uma linha completa (ou seja, não pressionar Enter) dentro do tempo especificado, o comando read irá falhar e retornar um código de saída diferente de zero.
#!/bin/bash
# Exemplo de read com timeout
echo "Você terá 5 segundos para cada entrada."
read -t 5 -p "Digite seu nome (5s): " usuario_nome
if [ $? -ne 0 ]; then
echo -e "\nTempo esgotado para o nome!"
exit 1
fi
read -t 5 -p "Digite seu e-mail (5s): " usuario_email
if [ $? -ne 0 ]; then
echo -e "\nTempo esgotado para o e-mail!"
exit 1
fi
read -t 5 -p "Confirma as informações? [s/n] (5s): " confirmacao
if [ $? -ne 0 ]; then
echo -e "\nTempo esgotado para a confirmação!"
exit 1
fi
case "$confirmacao" in
[sS]|[sS][iI][mM])
echo "Dados confirmados:"
echo "Nome: $usuario_nome"
echo "E-mail: $usuario_email"
;;
*)
echo "Dados não confirmados ou entrada inválida."
exit 0
;;
esac
3. Leitura Oculta (Sem Exibir na Tela)
Em cenários que exigem a entrada de informações sensíveis, como senhas, a opção -s (silent) do read impede que os caracteres digitados pelo usuário sejam exibidos no terminal. Este recurso é crucial para a segurança.
O exemplo a seguir ilustra como implementar uma solicitação de senha com feedback visual (*) e tratamento de backspace:
#!/bin/bash
# Coletando senha de forma oculta com feedback visual
senha_digitada=""
prompt_senha="Digite sua senha: "
echo -n "$prompt_senha"
while IFS= read -r -s -n1 char; do
if [[ -z "$char" ]]; then # Se Enter foi pressionado (caractere vazio)
echo
break
elif [[ "$char" == $'\x7f' || "$char" == $'\x08' ]]; then # Se Backspace ou Delete
if [[ -n "$senha_digitada" ]]; then
senha_digitada=${senha_digitada%?} # Remove o último caractere
printf '\b \b' # Apaga o '*' anterior
fi
else
senha_digitada+="$char" # Adiciona o caractere à senha
printf '*' # Exibe um '*'
fi
done
echo "Senha armazenada: $senha_digitada"
Ao executar este script, os caracteres digitados para a senha serão substituídos por asteriscos:
$ ./script_senha_oculta.sh
Digite sua senha: ***
Senha armazenada: minha_senha_secreta
4. Lendo Dados de um Arquivo
O comando read pode ser combinado com loops para processar o conteúdo de arquivos. Existem duas abordagens comuns:
- Utilizar
readdentro de um loopwhile, lendo o arquivo linha por linha (uma técnica robusta que será abordada em detalhes posteriormente). - Utilizar um loop
forem conjunto comcatpara iterar sobre o conteúdo do arquivo.
A abordagem com for e cat é mais simples para demonstração imediata, mas é importante entender como o IFS afeta a leitura. Por padrão, IFS contém espaço, tabulação e nova linha, o que faz com que o loop for itere sobre palavras, não linhas.
Sintaxe básica:
for item in $(cat nome_do_arquivo)
do
# Comandos para processar cada item
done
Para garantir que o loop for leia o arquivo linha por linha, é necessário modificar temporariamente a variável IFS para conter apenas o caractere de nova linha ($'\n').
Vamos demonstrar a leitura de um arquivo, primeiro com o IFS padrão e depois com IFS configurado para leitura linha a linha.
Considere um arquivo chamado listagem.txt com o seguinte conteúdo:
item1 valor A
item2 valor B
item3
456
abc def
Exemplo com IFS Padrão:
#!/bin/bash
# Leitura de arquivo com IFS padrão
if [ $# -ne 1 ]; then
echo "Uso: $(basename "$0") <arquivo>"
exit 1
fi
if [ ! -f "$1" ]; then
echo "Erro: O arquivo '$1' não existe!"
exit 1
fi
echo "--- Conteúdo do arquivo com IFS padrão (leitura por palavra) ---"
for dado in $(cat "$1"); do
echo "$dado"
done
Ao executar com ./script_leitura_arquivo.sh listagem.txt, a saída seria:
--- Conteúdo do arquivo com IFS padrão (leitura por palavra) ---
item1
valor
A
item2
valor
B
item3
456
abc
def
Note que cada palavra é tratada como um item separado.
Exemplo com IFS Modificado (Leitura por Linha):
#!/bin/bash
# Leitura de arquivo linha a linha, preservando formato
if [ $# -ne 1 ]; then
echo "Uso: $(basename "$0") <arquivo>"
exit 1
fi
if [ ! -f "$1" ]; then
echo "Erro: O arquivo '$1' não existe!"
exit 1
fi
OLD_IFS=$IFS # Salva o IFS original
IFS=$'\n' # Define IFS para separar apenas por nova linha
echo "--- Conteúdo do arquivo com IFS modificado (leitura por linha) ---"
for linha_conteudo in $(cat "$1"); do
echo "$linha_conteudo"
done
IFS=$OLD_IFS # Restaura o IFS original
A execução com ./script_leitura_arquivo_linha.sh listagem.txt resultaria em:
--- Conteúdo do arquivo com IFS modificado (leitura por linha) ---
item1 valor A
item2 valor B
item3
456
abc def
Neste caso, o formato original de cada linha é mantido, incluindo espaços múltiplos e tabulações, pois a iteração ocorre por linha completa. É crucial restaurar o IFS original após a operação para evitar efietos colaterais em outras partes do script.