Làm thế nào tôi có thể tạo ra một kịch bản để đếm lên bởi những người vợ?


10

Tôi đã cố gắng tạo một tập lệnh bash rất đơn giản để liệt kê tất cả các bội số của năm từ 375 đến 3500 (375, 380, 385 ...). Một điều mà tôi đã cố gắng và không làm việc là:

for i in {375..3500}
do
        echo $i
        (($i += 5))
done

Tôi đã bỏ cuộc sau một lúc và viết nó trong BASIC sau khoảng 15 giây:

10 count = 375
20 print count
30 count = count+5
40 if count < 3500 then goto 20

Làm cách nào để tạo chương trình BASIC của tôi trong tập lệnh bash?


điều này tốt hơn khi tràn stack - Unix & Linux là về HĐH, không phải mã hóa. Mã hóa là cho stackoverflow.
noɥʇʎԀʎzɐɹƆ 18/07/2016

Câu trả lời:


20

Ngoài ra, có thể sử dụng kiểu chữ C truyền thống cho vòng lặp:

for ((i=375; i<=3500; i+=5)); do
    echo $i
done

Điều này có lẽ ít rõ ràng hơn so với sử dụng seq, nhưng nó không sinh ra bất kỳ quy trình con nào. Mặc dù tôi quen thuộc với C, tôi sẽ không gặp khó khăn gì để hiểu điều này, nhưng YMMV.


1
Nhược điểm của phương pháp này là nó ít di động. Phương pháp seq hoạt động trong POSIX sh.
Wouter Verhelst

Ồ, kiểu chữ C cho các vòng lặp không tồn tại trong POSIX sh phải không? Bạn học được điều gì đó mới mỗi ngày ...
Muzer

2
@WouterVerhelst Chà, ngoại trừ nếu bạn bị giới hạn ở POSIX sh, bạn sẽ không có seq, một phần của lõi GNU. busybox có triển khai hạn chế hơn, nhưng bên ngoài đó, nhiều hệ thống nhúng có khả năng sẽ không có nó.
Chris Down

1
@Muzer - ít nhất là dashkhông hỗ trợ nó. @ChrisDown - Không có lý do tại sao "POSIX sh" ngụ ý "không seq". Nhưng vâng, được cho phép, tôi đã bỏ qua thực tế rằng seq không được chỉ định bởi POSIX.
Wouter Verhelst 24/07/2015

27

Vì dù sao bạn cũng sử dụng mở rộng nẹp, nên hãy sử dụng đầy đủ tính năng của nó:

echo {375..3500..5}

Bạn cũng có thể sử dụng kỹ thuật này để in từng số theo dòng riêng với văn bản tùy chọn bằng cách sử dụng printfthay vì echo, ví dụ:

$ printf "Number %s is generated.\n" {375..3500..5}
Number 375 is generated.
Number 380 is generated.
Number 385 is generated.
...

Biên tập

Như @kojiro đã chỉ ra trong nhận xét, Mac OS sử dụng bash 3 làm vỏ mặc định, không hỗ trợ tăng biểu thức trình tự mở rộng niềng răng. Bạn cần nâng cấp lên bash phiên bản 4 hoặc sử dụng shell khác hỗ trợ (ví dụ zsh gần đây).


2
Điều này không hoạt động với tôi trên Mac OS 10.10.3. Nó xuất ra '{375..3500..5}'.
vào

6
@aswine đó là lý do tại sao bạn nên luôn đề cập đến hệ điều hành của mình khi hỏi. Đặc biệt là vì OSX có một số điểm khác biệt cả từ các UNICE khác và từ Linux.
terdon

2
Đây không phải là một sự khác biệt hệ điều hành mỗi se. Đó là một sự khác biệt phiên bản. OS X chỉ có BASH 3 OOTB. Bạn có thể cài đặt BASH từ thế kỷ này bằng cách sử dụng hầu hết mọi trình quản lý gói OS X. Tôi sử dụng Home Brew, cho một.
kojiro

2
Thực tế là các bản mở rộng không chạy trực tiếp nhưng thành công trong bash -ckhá nhiều đảm bảo rằng có hai lớp vỏ khác nhau có liên quan. Có $SHELL --versionnói đó là bash 3 không?
dhag 24/07/2015

3
@mikeerv Câu trả lời rất đơn giản - Tôi đã không viết điều đó bởi vì tôi không biết phiên bản nào của bashtính năng này được giới thiệu. Tôi đã tự học nó từ bình luận kojiro. Và xin đừng so sánh tôi với SC, tôi thực sự không biết tất cả các chi tiết và lịch sử của tất cả các vỏ từ cuối năm 1970. Một lần nữa - Nếu bạn mong đợi điều đó, chỉ xin vui lòng downvote.
jimmij

17

Sử dụng SEQ (1)

for i in $(seq 375 5 3500)
do
    echo $i
done

Hoặc đơn giản:

seq 375 5 3500

4
Thậm chí đơn giản hơn sẽ là bỏ vòng lặp và chỉ cần sử dụng seq 375 5 3500.
dhag

11

Đoạn mã for-loop của bạn không hoạt động như bạn yêu cầu vì hai lý do:

  • (($i += 5))- ở đây $iđược mở rộng đến giá trị của i. Do đó, việc mở rộng sẽ là một cái gì đó giống như ((375 += 5)), điều này không có ý nghĩa (cố gắng gán một số bằng chữ cho một số theo nghĩa đen khác). Điều này thường sẽ đạt được với ((i += 5))(không $để mở rộng biến)
  • Các {375..3500}sẽ được mở rộng trước khi phiên đầu tiên của vòng lặp. Nó sẽ là danh sách các số 375 376 ... 3499 3500. Đối với mỗi lần lặp của vòng lặp, isẽ được gán cho từng số này, từng số một. Do đó, khi bắt đầu mỗi lần lặp, isẽ được gán lại cho giá trị tiếp theo trong danh sách đó, đếm theo các bước của 1. Hiệu ((i += 5))quả không làm gì cả - nó thêm 5 vào i, nhưng sau đó tôi chỉ được gán lại khi bắt đầu lần lặp tiếp theo.

Tôi nghĩ rằng tôi thích for (( ; ; ))câu trả lời nhất, nhưng đây là một số lựa chọn thay thế để bạn suy nghĩ:


Vì chúng tôi đang xử lý bội số của 5 và việc {a..b..i}mở rộng không được hỗ trợ trong phiên bản bash 3.2.57 (1) (trong OS X), nên chúng tôi có thể thực hiện điều này hơi phức tạp:

for i in 375 {38..349}{0,5} 3500; do
    echo $i
done

Điều này cho thấy cách bash có thể được sử dụng để tạo ra một sản phẩm cartesian.


Tôi nghĩ nói chung, vòng lặp for là cách thuận tiện nhất để thực hiện việc này, nhưng nếu bạn quan tâm, bạn có thể sử dụng chương trình vòng lặp while (gần hơn một chút với BASIC) của bạn:

count=375
while (( count <= 3500 )); do
    echo $count
    (( count += 5 ))
done

1
@jimmij ... sản phẩm của Cartesian là gì? bạn có thể đánh giá bình luận nếu bạn muốn, và tôi thậm chí sẽ không quan tâm nếu bạn nói với tôi. Tôi tò mò.
mikeerv 24/07/2015

1
@mikeerv bạn không thể tải xuống nhận xét: en.wikipedia.org/wiki/Cartesian_product
jimmij 24/07/2015

@jimmij - vẫn ổn với tôi nếu bạn làm thế.
mikeerv 24/07/2015

@jimmij Ma quỷ bạn đang nói về? Một sản phẩm của Cartesian là một bộ các bộ của mọi yếu tố có thể có từ hai bộ (có thể có hoặc không cùng một bộ). Thông thường hơn, đó chỉ là "tất cả các kết hợp có thể" từ hai nhóm thứ. Tôi chỉ thấy một bộ. Làm thế nào có một sản phẩm của Cartesian ở đây?
jpmc26

1
@ jpmc26 Bạn nói đúng, đó là "tất cả các kết hợp có thể", vì vậy, hãy vẽ đồ thị hai trục - trước tiên hãy viết tất cả các số từ 38đến 349. Trên thứ hai chỉ có hai số: 05. Bây giờ cho phép tạo cặp có thứ tự của những tất cả kết hợp - bạn có thể viết chúng như là một bộ: {38,0}, {38,5}... {349,5}, hoặc chỉ cần loại bỏ những biểu tượng không cần thiết {, ,}và nhận được ... số mới 380... 3495.
jimmij 24/07/2015

5

Tất nhiên, trong khi có một ứng dụng cho điều đó ( seq 375 5 3500), có nhiều cách khác nhau để thực hiện điều này từ dòng lệnh. Mặc dù nhanh nhất và đơn giản nhất sẽ chỉ sử dụng seq, đây là một số tùy chọn khác:

for i in {375..3500}; do [[ (($i % 5)) -eq 0 ]] && echo $i; done

i=370; while [ $i -le 3500 ]; do printf "%s\n" $((i+=5)); done

perl -le '$i=shift;while($i<=$ARGV[0]){print $i; $i+=5; }' 375 3500
perl -le 'map{print $_ + 5} 370..3495'

awk 'BEGIN{ for(i=375;i<=3500;i+=5){print i}}'

Nit chọn: $(($i % 5))có thể được viết $((i % 5)).
G-Man nói 'Phục hồi Monica'

4

POSIXly:

i=370
while [ 3500 -gt "$i" ]
do    echo "$((i+=5))"
done

...hoặc là...

echo 'for(x=370;x<=3500;x+=5)x' |bc

Tôi không biết tại sao bạn lại làm điều đó theo bất kỳ cách nào khác. Ngoại trừ, tất nhiên ...

seq 375 5 3500

... hoặc với dc:

echo '370[5+pd3500>p]splpx'|dc

Tôi tò mò về cách thức dchoạt động của mã. Nó chắc chắn hấp dẫn hơn sử dụng seq.
dhag

1
@dhag - Nó viết một vòng lặp nhỏ. nó saves [chuỗi ]lên trên cùng của pmảng, sau đó ltải nó trở lại vào đầu stack và e xtính nó như là một macro. Trong macro chúng ta đẩy 5vào ngăn xếp, sau đó bật nó và thứ hai từ trên xuống và +thay thế cả hai giá trị ngăn xếp bằng tổng của chúng, được tạo ra pvà tăng lên dtrước khi 3500được đẩy lên ngăn xếp, khi chúng ta bật nó và dupe mà chúng ta đã tạo so sánh >. Nếu 3500lớn hơn, chúng ta tải và thực thi dưới dạng macro chuỗi được lưu trữ trong pmảng (macro hiện tại của chúng ta) hoặc nếu không bị phá vỡ.
mikeerv 24/07/2015

3

Nếu bạn bị kẹt trên Bash 3:

echo {375..3500} | tr ' ' '\n' | sed -n 'p;n;n;n;n'

và nếu bạn thích awk:

echo {375..3500} | tr ' ' '\n' | awk '!((NR-1)%5)'

Tôi không biết về mở rộng niềng răng - điều đó thực sự tuyệt vời.

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.