Loop senza loop
La risposta di Zanna è assolutamente corretta e adatta per bash, ma possiamo migliorarla ancora di più senza utilizzare un loop.
printf "%d\n" {1..15} {20..25}
Il comportamento di printf
è tale che se il numero di ARGUMENTS
è maggiore dei controlli di formato in 'FORMAT STRING'
, allora printf
si divideranno tutti ARGUMENTS
in blocchi uguali e continueranno ad adattarli più volte alla stringa di formato fino a quando non si esaurisce l' ARGUMENTS
elenco.
Se stiamo cercando la portabilità, possiamo printf "%d\n" $(seq 1 15) $(seq 20 25)
invece utilizzare
Prendiamo questo ulteriore e più divertente. Supponiamo di voler eseguire un'azione anziché semplicemente stampare numeri. Per creare file da quella sequenza di numeri, potremmo facilmente farlo touch {1..15}.txt {20..25}.txt
. E se vogliamo che accadano più cose? Potremmo anche fare qualcosa del genere:
$ printf "%d\n" {1..15} {20..25} | xargs -I % bash -c 'touch "$1.txt"; stat "$1.txt"' sh %
O se vogliamo renderlo in stile vecchia scuola:
printf "%d\n" {1..15} {20..25} | while read -r line; do
touch "$line".txt;
stat "$line".txt;
rm "$line".txt;
done
Alternativa portatile ma dettagliata
Se vogliamo creare una soluzione di script che funzioni con shell che non hanno espansione di controvento (che è ciò {1..15} {20..25}
su cui si basa), possiamo scrivere un semplice ciclo while:
#!/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
Naturalmente questa soluzione è più dettagliata, alcune cose potrebbero essere abbreviate, ma funziona. Testato con ksh
, dash
, mksh
, e, naturalmente bash
.
Bash in stile C-loop
Ma se volessimo creare un loop bash specifico (per qualsiasi motivo, forse non solo stampare ma anche fare qualcosa con quei numeri), possiamo anche farlo (fondamentalmente versione C-loop della soluzione portatile):
last=15; for (( i=1; i<=last;i++ )); do printf "%d\n" "$i"; [[ $i -eq $last ]] && ! [[ $i -eq 25 ]] && { i=19;last=25;} ;done
O in un formato più leggibile:
last=15
for (( i=1; i<=last;i++ ));
do
printf "%d\n" "$i"
[[ $i -eq $last ]] && ! [[ $i -eq 25 ]] && { i=19;last=25;}
done
Confronto delle prestazioni di diversi approcci di 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 non shell
Solo perché possiamo ecco la soluzione Python
$ python3 -c 'print("\n".join([str(i) for i in (*range(1,16),*range(20,26))]))'
O con un po 'di shell:
bash-4.3$ python3 << EOF
> for i in (*range(16),*range(20,26)):
> print(i)
> EOF