Looping sem loop
A resposta de Zanna é absolutamente correta e adequada para o bash, mas podemos melhorar ainda mais sem utilizar um loop.
printf "%d\n" {1..15} {20..25}
Comportamento de printf
é tal que se o número de ARGUMENTS
for maior que os controles de formato em 'FORMAT STRING'
, então printf
dividirá todo ARGUMENTS
em partes iguais e continuará ajustando-as à cadeia de formato e acabou até ficar sem ARGUMENTS
list.
Se estamos nos esforçando para portabilidade, podemos utilizar printf "%d\n" $(seq 1 15) $(seq 20 25)
Vamos levar isso mais longe e mais divertido. Digamos que queremos realizar uma ação em vez de apenas imprimir números. Para criar arquivos fora dessa sequência de números, poderíamos facilmente fazer touch {1..15}.txt {20..25}.txt
. E se quisermos que várias coisas aconteçam? Nós também poderíamos fazer algo assim:
$ printf "%d\n" {1..15} {20..25} | xargs -I % bash -c 'touch ".txt"; stat ".txt"' sh %
Ou se quisermos tornar o estilo antigo:
printf "%d\n" {1..15} {20..25} | while read -r line; do
touch "$line".txt;
stat "$line".txt;
rm "$line".txt;
done
Alternativa portátil, mas detalhada
Se quisermos criar uma solução de script que funcione com shells que não tenham uma expansão de chaves (que é o que usa o {1..15} {20..25}
), podemos escrever um loop while simples:
#!/bin/sh
start=
jump=
new_start=
end=
i=$start
while [ $i -le $jump ]
do
printf "%d\n" "$i"
i=$((i+1))
if [ $i -eq $jump ] && ! [ $i -eq $end ];then
printf "%d\n" "$i"
i=$new_start
jump=$end
fi
done
Claro que esta solução é mais detalhada, algumas coisas podem ser encurtadas, mas funciona. Testado com ksh
, dash
, mksh
e, claro, bash
.
Loop de estilo C de Bash
Mas se quiséssemos criar um loop bash-specific (por qualquer motivo, talvez não apenas imprimindo, mas também fazendo algo com esses números), também podemos fazer isso (basicamente a versão C-loop da solução portátil):
last=15; for (( i=1; i<=last;i++ )); do printf "%d\n" "$i"; [[ $i -eq $last ]] && ! [[ $i -eq 25 ]] && { i=19;last=25;} ;done
Ou em formato mais legível:
last=15
for (( i=1; i<=last;i++ ));
do
printf "%d\n" "$i"
[[ $i -eq $last ]] && ! [[ $i -eq 25 ]] && { i=19;last=25;}
done
Comparação de desempenho de diferentes abordagens de looping
bash-4.3$ time bash -c 'printf "%d\n" {0..50000}>/dev/null'
real 0m0.196s
user 0m0.124s
sys 0m0.028s
bash-4.3$ time bash -c 'for i in {1..50000}; do echo $i > /dev/null; done'
real 0m1.819s
user 0m1.328s
sys 0m0.476s
bash-4.3$ time bash -c ' i=0;while [ $i -le 50000 ]; do echo $i>/dev/null; i=$((i+1)); done'
real 0m3.069s
user 0m2.544s
sys 0m0.500s
bash-4.3$ time bash -c 'for i in $(seq 1 50000); do printf "%d\n" > /dev/null; done'
real 0m1.879s
user 0m1.344s
sys 0m0.520s
Alternativa não shell
Só porque podemos aqui está a solução Python
$ python3 -c 'print("\n".join([str(i) for i in (*range(1,16),*range(20,26))]))'
Ou com um pouco de shell:
bash-4.3$ python3 << EOF
> for i in (*range(16),*range(20,26)):
> print(i)
> EOF