Chạy qua hai chuỗi trong một vòng lặp


8

Tôi đang cố gắng chạy qua hai chuỗi trong cùng một vòng lặp trong vỏ của mình như dưới đây:

#!/bin/bash
for i in (1..15) and (20..25) ;
do
     echo $i
     ......
     .....other process
done

Bất cứ ý tưởng làm thế nào tôi có thể đạt được điều này?


@zanna - suy nghĩ đầu tiên của tôi là boolean "và" là độc quyền, có nghĩa là kết quả là các số tồn tại trên cả hai bộ; không có gì trong trường hợp này Có bao gồm "và"?
ravery

1
@ravery Tôi đặt "và" chỉ để giải thích những gì tôi đang tìm kiếm
NGÀY

2
@YassineSihi - Hãy ghi chú lại. Nhiều lập trình viên mới vấp ngã ở điểm này cho đến khi họ có thể kiềm chế bộ não của mình, bởi vì ngôn ngữ nói sử dụng "và" bao gồm nhưng logic "và" là độc quyền trong hầu hết các ngôn ngữ lập trình.
ravery

Câu trả lời:


10

Bạn chỉ cần mở rộng cú đúp cho điều đó

$ for n in {1..3} {200..203}; do echo $n; done
1
2
3
200
201
202
203

Chúng ta có thể chuyển một danh sách tới for( ).for i in x y z; do stuff "$i"; done

Vì vậy, ở đây, niềng răng { }có được vỏ để mở rộng trình tự của bạn vào một danh sách. Bạn chỉ cần đặt một khoảng trắng giữa chúng, vì shell tách danh sách các đối số trên đó.


Yep, niềng răng. . . Và bạn thậm chí không cần một vòng lặp cho điều đó ^ _0
Sergiy Kolodyazhnyy

@SergiyKolodyazhnyy Tôi hình dung họ thực sự không chỉ muốn echonhững con số
Zanna

vâng, nếu họ muốn một số loại hành động, như touchcác tệp, họ chỉ có thể làm touch {1..15}.txt {20..25}.txt, không cần vòng lặp ở đây. Nhưng tất nhiên nếu đó là nhiều hành động trên cùng một số - OK, điều đó có thể sử dụng một vòng lặp.
Sergiy Kolodyazhnyy

6

Ngoài ra, chúng ta có thể sử dụng seq( in một chuỗi số ), đây là hai ví dụ tương đương:

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

Nếu nó là một tập lệnh, đối với các tác vụ lặp đi lặp lại, bạn có thể sử dụng các hàm:

#!/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"

4

Câu trả lời của Zannacâu trả lời của pa4080 đều tốt và có lẽ tôi sẽ đi với một trong số họ trong hầu hết các trường hợp. Có lẽ không cần phải nói, nhưng để hoàn thiện, dù sao tôi cũng sẽ nói: Bạn có thể tải từng giá trị vào một mảng và sau đó lặp qua mảng. Ví dụ:

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

@SergiyKolodyazhnyy: Cảm ơn phản hồi. Tôi đã đủ tuổi, đó là cách tôi được dạy, và vẫn thường làm điều đó trong dịp hiếm hoi tôi đang viết một kịch bản shell. Tuy nhiên, tôi đã cập nhật câu trả lời để sử dụng một mảng.
GreenMatt

Rất tốt ! Chúc mừng kịch bản!
Sergiy Kolodyazhnyy

3

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 printfnó là nếu số lượng ARGUMENTSlớn hơn các điều khiển định dạng 'FORMAT STRING', sau đó printfsẽ 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 ARGUMENTSdanh 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

1
Tôi vừa mới thử nghiệm touch $(printf "%d\n" {1..15} {20..25}):-)
pa4080

1
@ pa4080 thực sự cho bashbạn thậm chí không cần $()ở đó, chỉ touch {1..15}.txt {20..25}.txt :) Nhưng tất nhiên chúng tôi có thể sử dụng printf "%d\n{1..15} {20..25} `với xargsnếu chúng tôi muốn làm nhiều hơn chỉ là touchcác tệp. Có nhiều cách để làm mọi thứ và nó làm cho kịch bản trở nên thú vị!
Sergiy Kolodyazhnyy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.