Interação com o Usuário em Scripts Bash: Coletando Entradas com `read`

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:

  1. Utilizar read dentro de um loop while, lendo o arquivo linha por linha (uma técnica robusta que será abordada em detalhes posteriormente).
  2. Utilizar um loop for em conjunto com cat para 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.

Tags: Bash Shell Scripting read command Input/Output Linux

Publicado em 6-11 00:46 por Thomas