Vòng lặp không có vòng lặp
Câu trả lời của Zanna hoàn toàn chính xác và phù hợp với bash, nhưng chúng tôi có thể cải thiện điều đó hơn nữa mà không cần sử dụng vòng lặp.
printf "%d\n" {1..15} {20..25}
Hành vi của printf
nó là nếu số lượng ARGUMENTS
lớn hơn các điều khiển định dạng 'FORMAT STRING'
, sau đó printf
sẽ chia tất cả ARGUMENTS
thành các phần bằng nhau và tiếp tục khớp chúng với chuỗi định dạng nhiều lần cho đến khi hết ARGUMENTS
danh sách.
Nếu chúng ta đang phấn đấu cho tính di động, chúng ta có thể sử dụng printf "%d\n" $(seq 1 15) $(seq 20 25)
thay thế
Chúng ta hãy làm điều này hơn nữa và vui hơn. Nói rằng chúng tôi muốn thực hiện một hành động thay vì chỉ in số. Để tạo các tệp trong chuỗi số đó, chúng ta có thể dễ dàng thực hiện touch {1..15}.txt {20..25}.txt
. Điều gì xảy ra nếu chúng ta muốn nhiều thứ xảy ra? Chúng ta cũng có thể làm một cái gì đó như thế này:
$ printf "%d\n" {1..15} {20..25} | xargs -I % bash -c 'touch "$1.txt"; stat "$1.txt"' sh %
Hoặc nếu chúng ta muốn biến nó thành phong cách trường học cũ:
printf "%d\n" {1..15} {20..25} | while read -r line; do
touch "$line".txt;
stat "$line".txt;
rm "$line".txt;
done
Thay thế di động nhưng dài dòng
Nếu chúng ta muốn tạo một giải pháp tập lệnh hoạt động với các shell không có mở rộng dấu ngoặc (đó là những gì {1..15} {20..25}
dựa vào), chúng ta có thể viết một vòng lặp while đơn giản:
#!/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
Tất nhiên giải pháp này dài dòng hơn, một số thứ có thể rút ngắn, nhưng nó hoạt động. Thử nghiệm với ksh
, dash
, mksh
, và tất nhiên bash
.
Vòng lặp kiểu Bash C
Nhưng nếu chúng ta muốn tạo một bash đặc thù cho vòng lặp (vì bất kỳ lý do gì, có lẽ không chỉ in mà còn làm gì đó với những con số đó), chúng ta cũng có thể làm điều này (về cơ bản là giải pháp vòng lặp C của giải pháp di động):
last=15; for (( i=1; i<=last;i++ )); do printf "%d\n" "$i"; [[ $i -eq $last ]] && ! [[ $i -eq 25 ]] && { i=19;last=25;} ;done
Hoặc ở định dạng dễ đọc hơn:
last=15
for (( i=1; i<=last;i++ ));
do
printf "%d\n" "$i"
[[ $i -eq $last ]] && ! [[ $i -eq 25 ]] && { i=19;last=25;}
done
So sánh hiệu suất của các phương pháp lặp khác nhau
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
Không thay thế vỏ
Chỉ vì chúng tôi có thể ở đây giải pháp Python
$ python3 -c 'print("\n".join([str(i) for i in (*range(1,16),*range(20,26))]))'
Hoặc với một chút vỏ:
bash-4.3$ python3 << EOF
> for i in (*range(16),*range(20,26)):
> print(i)
> EOF