La

Laços em Bash

{one: "1 exercício", many: "%{count} de exercícios", other: "%{count} exercícios"}

Sobre Laços

O Bash tem duas formas de laço:

  • Repetir alguns comandos para cada item de uma lista de itens,
  • Repetir alguns comandos enquanto uma condição for verdadeira (ou falsa).

Laços for

Para iterar sobre uma lista de itens, usamos o laço for.

for varname in words; do COMMANDS; done

words é expandido usando divisão de palavras e expansão de nomes de arquivos (que aprendemos no conceito Quoting). Cada palavra é atribuída ao varname, uma por vez, e os COMMANDS são executados.

Aqui estão alguns exemplos que mostram quando o laço for pode ser útil

  • Executar alguns comandos em um grupo de arquivos.

    # source all the bash library files in the current directory
    for file in ./*.bash; do
        source "$file"
    done
    
  • Executar algum comando em cada um dos parâmetros de um script ou função.

    for arg in "$@"; do
        echo "argument: ${arg}"
    done
    
  • Executar algum comando para cada palavra separada por espaço na saída de um comando.

    for i in $(seq 10); do
        echo "${i} squared is $((i * i))"
    done
    

    Vamos ver alguns avisos sobre esse estilo mais adiante.

Laço for aritmético

O Bash tem sim um laço aritmético. Ele vai parecer um pouco familiar para quem conhece outras linguagens de programação.

for ((INITIALIZATION; CONDITION; INCREMENT)); do COMMANDS; done

Os parênteses duplos no Bash representam um contexto aritmético (veremos mais sobre a aritmética do Bash em um conceito posterior). O exemplo acima que usa seq pode ser escrito assim

for ((i = 1; i <= 10; i++)); do
    echo "${i} squared is $((i * i))"
done

Quando não usar o for

Não leia linhas com for.

Este é um antipadrão que você vai ver com frequência: iterar sobre a saída do cat.

for line in $(cat some.file); do 
    do_something_with "$line"
done

Isso está errado por dois motivos.

  1. A substituição de comando não está entre aspas, então está sujeita à divisão de palavras. A divisão de palavras, por padrão, separa por espaços em branco, não apenas por quebras de linha. Este laço for está iterando sobre as palavras do arquivo, não sobre as linhas.
  2. A substituição de comando não está entre aspas, então está sujeita à expansão de nomes de arquivos. Cada palavra do arquivo será comparada como um padrão glob.

Como aprendemos no conceito Quoting, a divisão de palavras pode ser controlada com a variável IFS, e a expansão de nomes de arquivos pode ser desativada. Mas essa costuma ser uma solução frágil. O for é o método errado para iterar sobre as linhas de um arquivo. A forma idiomática de ler um arquivo é com um laço while.

Laços while

Use o while para repetir uma sequência de comandos enquanto uma condição for verdadeira.

while CONDITION_COMMANDS; do COMMANDS; done

Assim como no if (como aprendemos no conceito Condicionais, não há sintaxe especial para CONDITION_COMMANDS. O status de saída da lista de comandos vai determinar "verdadeiro" ou "falso".

Controle de laços

O comando break salta para fora do laço. O controle continua com o comando que vem depois do terminador done do laço.

O comando continue salta para a próxima iteração do laço.

Ler as linhas de um arquivo

Como mencionado, um laço while é a forma idiomática de ler um arquivo.

while IFS= read -r line; do
    do_something_with "$line"
done < some.file
  • O conteúdo do arquivo é fornecido como entrada para o laço com o redirecionamento <.

  • O comando read retorna "verdadeiro" se conseguir ler uma linha inteira da sua entrada. Quando a entrada é consumida, ou se a última linha termina sem uma quebra de linha no final, o read retorna "falso".

    • A forma realmente idiomática de ler as linhas de um arquivo, mesmo que a última linha não termine com uma quebra de linha, é

      while IFS= read -r line || [[ -n "$line" ]]; do ...
      
  • A opção -r diz ao read para não substituir sequências de barra invertida: uma barra invertida é apenas um caractere comum.

  • IFS= atribui a string vazia a IFS apenas durante a execução do comando read. Isso desativa temporariamente a divisão de palavras para que qualquer espaço em branco no início ou no final da linha de texto recebida seja preservado.

until

A construção until repete uma sequência de comandos enquanto uma condição for falsa. O laço se repete até que a condição se torne verdadeira.

until CONDITION_COMMANDS; do COMMANDS; done

É raro usar o until. É mais comum usar um laço "while not".

while ! CONDITION_COMANDS; do ...

Laços infinitos

Às vezes você quer repetir para sempre. Aqui estão duas formas de fazer isso.

  1. um laço while com uma condição que sempre tem um status de saída de sucesso

    while true; do ...
    
  2. um laço for aritmético com expressões vazias

    for ((;;)); do ...
    

do-while

Não existe uma construção do-while explícita, mas você pode obter o mesmo efeito:

while true; do
    COMMANDS

    if END_CONDITION; then
        break
    fi
done
Editar via GitHub O link abre em uma nova janela ou aba