O Bash tem duas formas de fazer ciclos:
Para iterar sobre uma lista de itens, usamos o ciclo for.
for varname in words; do COMMANDS; done
O words é expandido através da divisão de palavras e da expansão de nomes de ficheiros (de que falámos no conceito de quoting).
Cada palavra é atribuída, por sua vez, à varname, e os COMMANDS são executados.
Eis alguns exemplos que mostram quando o ciclo for pode ser útil:
Executar alguns comandos sobre um grupo de ficheiros.
# source all the bash library files in the current directory
for file in ./*.bash; do
source "$file"
done
Executar um comando sobre cada um dos parâmetros de um script ou de uma função.
for arg in "$@"; do
echo "argument: ${arg}"
done
Executar um comando para cada palavra separada por espaços na saída de um comando.
for i in $(seq 10); do
echo "${i} squared is $((i * i))"
done
Vamos ver alguns avisos sobre este estilo mais à frente.
O Bash tem, de facto, um ciclo aritmético. Isto parecerá de certo modo familiar a 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 aritmética do Bash num conceito posterior).
O exemplo acima com o seq pode escrever-se assim:
for ((i = 1; i <= 10; i++)); do
echo "${i} squared is $((i * i))"
done
Este é um antipadrão que vais ver muitas vezes: iterar sobre a saída do cat.
for line in $(cat some.file); do
do_something_with "$line"
done
Isto está errado por dois motivos.
Como aprendemos no conceito de quoting, a divisão de palavras pode ser controlada com a variável IFS, e a expansão de nomes de ficheiros pode ser desativada.
Mas isto tende a ser uma solução frágil.
O for é o método errado para iterar sobre as linhas de um ficheiro.
A forma idiomática de ler um ficheiro é com um ciclo while.
Usa o while para repetir uma sequência de comandos enquanto uma condição for verdadeira.
while CONDITION_COMMANDS; do COMMANDS; done
Tal como acontece com o if (como aprendemos no conceito de condicionais), não há sintaxe especial para CONDITION_COMMANDS.
O estado de saída da lista de comandos determinará "verdadeiro" ou "falso".
O comando break sai do ciclo.
O controlo retoma no comando que se segue ao terminador done do ciclo.
O comando continue salta para a iteração seguinte do ciclo.
Como já foi referido, um ciclo while é a forma idiomática de ler um ficheiro.
while IFS= read -r line; do
do_something_with "$line"
done < some.file
O conteúdo do ficheiro é fornecido ao ciclo como entrada, com a redireção <.
O comando read devolve "verdadeiro" se conseguir ler uma linha completa da sua entrada.
Quando a entrada se esgota, ou se a última linha terminar sem uma mudança de linha final, o read devolve "falso".
A forma realmente idiomática de ler as linhas de um ficheiro, mesmo que a última linha não termine com uma mudança 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 caráter normal.
IFS= atribui a string vazia ao IFS apenas durante a execução do comando read.
Isto desativa temporariamente a divisão de palavras, para que os espaços em branco no início ou no fim da linha de texto recebida sejam preservados.
A construção until repete uma sequência de comandos enquanto uma condição for falsa.
O ciclo repete até que a condição se torne verdadeira.
until CONDITION_COMMANDS; do COMMANDS; done
É raro usar o until.
É mais comum usar um ciclo "while not".
while ! CONDITION_COMANDS; do ...
Por vezes queres repetir indefinidamente. Eis duas formas de o fazer.
um ciclo while com uma condição que tem sempre um estado de saída de sucesso
while true; do ...
um ciclo for aritmético com expressões vazias
for ((;;)); do ...
Não existe uma construção do-while explícita, mas podes obter o mesmo efeito:
while true; do
COMMANDS
if END_CONDITION; then
break
fi
done