Estou tentando executar duas sequências no mesmo loop no meu shell, como abaixo:
#!/bin/bash
for i in (1..15) and (20..25) ;
do
echo $i
......
.....other process
done
alguma idéia de como eu posso conseguir isso?
Estou tentando executar duas sequências no mesmo loop no meu shell, como abaixo:
#!/bin/bash
for i in (1..15) and (20..25) ;
do
echo $i
......
.....other process
done
alguma idéia de como eu posso conseguir isso?
Respostas:
Você só precisa de expansão para isso
$ for n in {1..3} {200..203}; do echo $n; done
1
2
3
200
201
202
203
Podemos passar uma lista para for
( ).for i in x y z; do stuff "$i"; done
Então, aqui, os aparelhos {
}
recebem o shell para expandir suas seqüências em uma lista. Você só precisa colocar um espaço entre eles, pois o shell divide listas de argumentos sobre eles.
echo
os números
touch
arquivos, eles podem simplesmente fazer touch {1..15}.txt {20..25}.txt
, sem loop necessário aqui. Mas é claro que se houver várias ações no mesmo número - OK, isso poderia usar um loop.
Como alternativa, podemos usar seq
( imprimir uma sequência de números ), eis dois exemplos equivalentes:
for i in `seq 1 3` `seq 101 103`; do echo $i; done
for i in $(seq 1 3) $(seq 101 103); do echo $i; done
Se for um script, para tarefas repetitivas, você pode usar as funções:
#!/bin/bash
my_function() { echo "$1"; }
for i in {1..3}; do my_function "$i"; done
for i in {101..103}; do my_function "$i"; done
#!/bin/bash
my_function() { for i in `seq $1 $2`; do echo "$i"; done; }
my_function "1" "3"
my_function "101" "103"
A resposta de Zanna e a de pa4080 são boas e eu provavelmente iria com uma delas na maioria das circunstâncias. Talvez não seja preciso dizer, mas por uma questão de integridade, eu direi assim mesmo: você pode carregar cada valor em uma matriz e, em seguida, percorrer a matriz. Por exemplo:
the_array=( 1 2 3 4 5 6 7 8 9 10 20 21 22 23 24 25 )
for i in "${the_array[@]}";
do
echo $i
done
A resposta de Zanna é absolutamente correta e adequada para o bash, mas podemos melhorar isso ainda mais sem utilizar um loop.
printf "%d\n" {1..15} {20..25}
O comportamento de printf
é tal que, se o número de ARGUMENTS
for maior do que o formato controla 'FORMAT STRING'
, ele printf
dividirá tudo ARGUMENTS
em partes iguais e continuará ajustando-as à cadeia de caracteres de formato repetidas vezes, até ficar fora da ARGUMENTS
lista.
Se estamos lutando para a portabilidade, podemos utilizar printf "%d\n" $(seq 1 15) $(seq 20 25)
em vez
Vamos levar isso adiante e mais divertido. Digamos que queremos executar uma ação em vez de apenas imprimir números. Para criar arquivos a partir dessa sequência de números, poderíamos criar facilmente touch {1..15}.txt {20..25}.txt
. E se queremos que várias coisas ocorram? Também poderíamos fazer algo assim:
$ printf "%d\n" {1..15} {20..25} | xargs -I % bash -c 'touch "$1.txt"; stat "$1.txt"' sh %
Ou se queremos torná-lo à moda antiga:
printf "%d\n" {1..15} {20..25} | while read -r line; do
touch "$line".txt;
stat "$line".txt;
rm "$line".txt;
done
Se queremos criar uma solução de script que funcione com shells que não possuem expansão entre chaves (que é o que se {1..15} {20..25}
baseia), podemos escrever um loop while simples:
#!/bin/sh
start=$1
jump=$2
new_start=$3
end=$4
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 essa solução é mais detalhada, algumas coisas podem ser encurtadas, mas funciona. Testado com ksh
, dash
, mksh
e, é claro bash
.
Mas se quisermos fazer um loop específico do bash (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 em loop C 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 um 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
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
Só porque nós podemos aqui solução Python
$ python3 -c 'print("\n".join([str(i) for i in (*range(1,16),*range(20,26))]))'
Ou com um pouco de casca:
bash-4.3$ python3 << EOF
> for i in (*range(16),*range(20,26)):
> print(i)
> EOF
touch $(printf "%d\n" {1..15} {20..25})
:-)
bash
você nem precisa de $()
lá, apenas touch {1..15}.txt {20..25}.txt
:) Mas é claro que poderíamos usar printf "%d\n
{1..15} {20..25} `com xargs
se quiséssemos fazer mais do que apenas touch
arquivos. Há muitas maneiras de fazer as coisas e isso torna os scripts muito divertidos!