Os laços de repetição são fundamentais em qualquer linguagem de programação, e no shell script não é diferente. Nesta aula, vamos explorar dois tipos de laços: while e until. Enquanto o while executa um bloco de comandos enquanto uma condição for verdadeira, o until faz o oposto: executa até que a condição se torne verdadeira. Além disso, veremos como usar o comando read dentro de um laço while para ler arquivos linha a linha, e como controlar a execução com break e continue.

while

O laço while repete um conjunto de comandos enquanto uma condição for verdadeira (retorno zero). Sua sintaxe é:

while condição; do
    comandos
done

A condição pode ser qualquer comando ou teste. O laço termina quando o comando retorna um código diferente de zero. Exemplo simples que conta de 1 a 5:

#!/bin/bash
contador=1
while [ $contador -le 5 ]; do
    echo "Contador: $contador"
    ((contador++))
done

Saída:

Contador: 1
Contador: 2
Contador: 3
Contador: 4
Contador: 5

Note o uso de ((contador++)) para incrementar a variável. Isso é uma expansão aritmética do Bash.

until

O laço until é semelhante ao while, mas executa os comandos enquanto a condição for falsa (retorno diferente de zero). Ele para quando a condição se torna verdadeira. Sintaxe:

until condição; do
    comandos
done

Exemplo equivalente ao anterior, mas usando until:

#!/bin/bash
contador=1
until [ $contador -gt 5 ]; do
    echo "Contador: $contador"
    ((contador++))
done

A condição é [ $contador -gt 5 ], que verifica se o contador é maior que 5. Enquanto isso for falso (contador menor ou igual a 5), o laço continua. Quando contador chega a 6, a condição é verdadeira e o laço termina.

read em while (lendo arquivos)

Uma aplicação muito comum do while é ler arquivos linha a linha usando o comando read. A estrutura típica é:

while IFS= read -r linha; do
    echo "Linha: $linha"
done < arquivo.txt

O IFS= evita que espaços em branco no início ou fim da linha sejam removidos. A opção -r impede que barras invertidas sejam interpretadas como escapes. O redirecionamento < arquivo.txt fornece o conteúdo do arquivo como entrada para o laço.

Exemplo prático: criar um arquivo nomes.txt com um nome por linha e depois lê-lo:

#!/bin/bash
while IFS= read -r nome; do
    echo "Olá, $nome!"
done < nomes.txt

Se nomes.txt contiver:

Ana
Bruno
Carla

A saída será:

Olá, Ana!
Olá, Bruno!
Olá, Carla!

break e continue

Os comandos break e continue permitem controlar o fluxo dentro de laços. break encerra o laço imediatamente, enquanto continue pula para a próxima iteração (ignorando os comandos restantes no corpo do laço).

Exemplo com break:

#!/bin/bash
for i in {1..10}; do
    if [ $i -eq 5 ]; then
        break
    fi
    echo "Número: $i"
done

Saída:

Número: 1
Número: 2
Número: 3
Número: 4

Exemplo com continue (pula o número 3):

#!/bin/bash
for i in {1..5}; do
    if [ $i -eq 3 ]; then
        continue
    fi
    echo "Número: $i"
done

Saída:

Número: 1
Número: 2
Número: 4
Número: 5

Em laços aninhados, você pode passar um número para break ou continue para indicar quantos níveis devem ser afetados (ex.: break 2 sai de dois laços).

Boas práticas

  • Sempre use IFS= e -r ao ler arquivos com read para preservar o conteúdo original.
  • Evite laços infinitos acidentais: certifique-se de que a condição eventualmente se torna falsa (ou use break).
  • Para loops baseados em números, prefira for com faixas ({1..10}) ou expressões aritméticas (((i++))).
  • Use while true para laços infinitos intencionais, mas sempre com uma rota de saída (ex.: break).

Referências

Exercícios

  1. Escreva um script que use um laço while para imprimir os números pares de 2 a 10.
  2. ✓ Resposta:
    #!/bin/bash
    num=2
    while [ $num -le 10 ]; do
        echo $num
        ((num+=2))
    done
  3. Reescreva o exercício anterior usando until.
  4. ✓ Resposta:
    #!/bin/bash
    num=2
    until [ $num -gt 10 ]; do
        echo $num
        ((num+=2))
    done
  5. Crie um script que leia o arquivo /etc/passwd linha a linha e exiba apenas os nomes de usuário (primeiro campo, separado por :). Dica: use cut ou manipulação de string.
  6. ✓ Resposta:
    #!/bin/bash
    while IFS=: read -r username _; do
        echo "$username"
    done < /etc/passwd

    O IFS=: define o separador como dois pontos, e read atribui o primeiro campo a username e o resto a _ (descartado).

  7. Escreva um script que use while true para exibir um menu com opções (1: Data, 2: Hora, 3: Sair). Use break para sair quando o usuário escolher 3.
  8. ✓ Resposta:
    #!/bin/bash
    while true; do
        echo "1 - Data"
        echo "2 - Hora"
        echo "3 - Sair"
        read -p "Escolha uma opção: " opcao
        case $opcao in
            1) date +%d/%m/%Y ;;
            2) date +%H:%M:%S ;;
            3) break ;;
            *) echo "Opção inválida" ;;
        esac
    done
  9. Modifique o script do exercício 4 para que, ao invés de break, use continue para exibir uma mensagem de "Opção inválida" e voltar ao menu, mas sem usar case (use if).
  10. ✓ Resposta:
    #!/bin/bash
    while true; do
        echo "1 - Data"
        echo "2 - Hora"
        echo "3 - Sair"
        read -p "Escolha uma opção: " opcao
        if [ "$opcao" -eq 1 ]; then
            date +%d/%m/%Y
        elif [ "$opcao" -eq 2 ]; then
            date +%H:%M:%S
        elif [ "$opcao" -eq 3 ]; then
            break
        else
            echo "Opção inválida"
            continue
        fi
    done

    Note que o continue aqui é redundante, mas ilustra seu uso. Sem ele, o comportamento seria o mesmo, pois o laço volta ao início naturalmente.