Làm thế nào để tôi lặp lại một phạm vi số được xác định bởi các biến trong Bash?


1544

Làm thế nào để tôi lặp lại một phạm vi số trong Bash khi phạm vi được đưa ra bởi một biến?

Tôi biết tôi có thể làm điều này (được gọi là "biểu thức trình tự" trong tài liệu Bash ):

 for i in {1..5}; do echo $i; done

Cung cấp cho:

1
2
3
4
5

Tuy nhiên, làm thế nào tôi có thể thay thế một trong hai điểm cuối phạm vi bằng một biến? Điều này không hoạt động:

END=5
for i in {1..$END}; do echo $i; done

Bản in nào:

{1..5}


26
Xin chào tất cả, những thông tin và gợi ý tôi đã đọc ở đây đều thực sự hữu ích. Tôi nghĩ tốt nhất là tránh sử dụng seq. Lý do là một số tập lệnh cần phải có khả năng di động và phải chạy trên nhiều hệ thống unix khác nhau, trong đó một số lệnh có thể không có mặt. Chỉ cần làm một ví dụ, seq không có mặt theo mặc định trên các hệ thống FreeBSD.


9
Tôi không nhớ chính xác phiên bản Bash nào nhưng lệnh này cũng hỗ trợ các số 0 ở cuối. Mà đôi khi thực sự hữu ích. Lệnh for i in {01..10}; do echo $i; donesẽ cho số như thế nào 01, 02, 03, ..., 10.
topr

1
Đối với những người như tôi, những người chỉ muốn lặp đi lặp lại trong phạm vi các chỉ số của một mảng , cách bash sẽ là: myarray=('a' 'b' 'c'); for i in ${!myarray[@]}; do echo $i; done(lưu ý dấu chấm than). Nó cụ thể hơn câu hỏi ban đầu, nhưng có thể giúp ích. Xem phần mở rộng tham số bash
PlasmaBinturong

1
Mở rộng cú đúp cũng được sử dụng cho các biểu thức {jpg,png,gif}không được đề cập trực tiếp ở đây, mặc dù câu trả lời sẽ giống hệt nhau. Xem Brace mở rộng với biến? [trùng lặp] được đánh dấu là bản sao của cái này.
tripleee

Câu trả lời:


1746
for i in $(seq 1 $END); do echo $i; done

chỉnh sửa: Tôi thích seqhơn các phương pháp khác vì tôi thực sự có thể nhớ nó;)


36
seq liên quan đến việc thực hiện một lệnh bên ngoài thường làm mọi thứ chậm lại. Điều này có thể không quan trọng nhưng nó trở nên quan trọng nếu bạn đang viết một tập lệnh để xử lý nhiều dữ liệu.
paxdiablo

37
Chỉ tốt cho một lót. Giải pháp của Pax cũng tốt, nhưng nếu hiệu suất thực sự là mối lo ngại thì tôi sẽ không sử dụng tập lệnh shell.
eschercycle

17
seq được gọi chỉ một lần để tạo ra các số. exec () 'ing nó không đáng kể trừ khi vòng lặp này nằm trong một vòng lặp chặt chẽ khác.
Javier

29
Lệnh bên ngoài không thực sự phù hợp: nếu bạn lo lắng về chi phí hoạt động của các lệnh bên ngoài, bạn hoàn toàn không muốn sử dụng các tập lệnh shell, nhưng nói chung là trên unix thì chi phí thấp. Tuy nhiên, có vấn đề về việc sử dụng bộ nhớ nếu END cao.
Đánh dấu Baker

18
Lưu ý rằng seq $ENDsẽ đủ, vì mặc định là bắt đầu từ 1. Từ man seq: "Nếu FIRST hoặc INCREMENT bị bỏ qua, nó mặc định là 1".
fedorqui 'SO ngừng làm hại'

474

Các seqphương pháp là đơn giản nhất, nhưng Bash đã được xây dựng trong việc đánh giá số học.

END=5
for ((i=1;i<=END;i++)); do
    echo $i
done
# ==> outputs 1 2 3 4 5 on separate lines

Cấu for ((expr1;expr2;expr3));trúc hoạt động giống như for (expr1;expr2;expr3)trong C và các ngôn ngữ tương tự, và giống như các ((expr))trường hợp khác , Bash coi chúng là số học.


67
Cách này giúp tránh chi phí bộ nhớ của một danh sách lớn và phụ thuộc vào seq. Sử dụng nó!
bobbogo

3
@MarinSagovac Điều này không hoạt động và không có lỗi cú pháp. Bạn có chắc cái vỏ của bạn là Bash?
gniourf_gniourf

3
@MarinSagovac Đảm bảo tạo #!/bin/bashdòng đầu tiên của tập lệnh của bạn. wiki.ubuntu.com/...
Melebius

7
chỉ là một câu hỏi rất ngắn về vấn đề đó: tại sao ((i = 1; i <= END; i ++)) VÀ KHÔNG ((i = 1; i <= $ END; i ++)); Tại sao không có $ trước khi kết thúc?
Baedsch

5
@Baedsch: vì lý do tương tự tôi không được sử dụng như $ i. trang bash man cho đánh giá số học: "Trong một biểu thức, các biến shell cũng có thể được tham chiếu theo tên mà không sử dụng cú pháp mở rộng tham số."
dùng3188140

193

thảo luận

Sử dụng seqlà tốt, như Jiaaro đề nghị. Pax Diablo đã đề xuất một vòng lặp Bash để tránh gọi một quy trình con, với lợi thế bổ sung là thân thiện với bộ nhớ hơn nếu $ END quá lớn. Zathrus đã phát hiện ra một lỗi điển hình trong việc thực hiện vòng lặp và cũng gợi ý rằng vì ilà một biến văn bản, các số chuyển đổi liên tục sang số được thực hiện với sự chậm lại liên quan.

số học số nguyên

Đây là phiên bản cải tiến của vòng lặp Bash:

typeset -i i END
let END=5 i=1
while ((i<=END)); do
    echo $i
    
    let i++
done

Nếu điều duy nhất mà chúng ta muốn là echo, thì chúng ta có thể viết echo $((i++)).

ephemient đã dạy tôi một điều: Bash cho phép for ((expr;expr;expr))xây dựng. Vì tôi chưa bao giờ đọc toàn bộ trang man cho Bash (giống như tôi đã làm với kshtrang man Korn shell ( ) và đó là một thời gian dài trước đây), tôi đã bỏ lỡ điều đó.

Vì thế,

typeset -i i END # Let's be explicit
for ((i=1;i<=END;++i)); do echo $i; done

dường như là cách tiết kiệm bộ nhớ nhất (không cần phân bổ bộ nhớ để tiêu thụ seqđầu ra, điều này có thể là vấn đề nếu END rất lớn), mặc dù có lẽ không phải là nhanh nhất.

câu hỏi ban đầu

eschercycle lưu ý rằng ký hiệu { a .. b } Bash chỉ hoạt động với chữ; đúng, theo hướng dẫn của Bash. Người ta có thể vượt qua trở ngại này bằng một (bên trong) fork()mà không cần exec()(như trường hợp khi gọi seq, đó là một hình ảnh khác yêu cầu fork + exec):

for i in $(eval echo "{1..$END}"); do

Cả hai evalecholà các nội trang Bash, nhưng a fork()là bắt buộc để thay thế lệnh ( $(…)cấu trúc).


1
Hạn chế duy nhất đối với vòng lặp kiểu C là nó không thể sử dụng các đối số dòng lệnh, khi chúng bắt đầu bằng "$".
karatedog

3
@karatedog: for ((i=$1;i<=$2;++i)); do echo $i; donetrong một kịch bản hoạt động tốt với tôi trên bash v.4.1.9, vì vậy tôi không thấy vấn đề gì với các đối số dòng lệnh. Bạn có ý gì khác?
tzot

Có vẻ như giải pháp eval nhanh hơn so với được xây dựng trong C-like cho: $ time for ((i = 1; i <= 100000; ++ i)); làm :; thực hiện 0m21.220s người dùng thực 0m19.763s sys 0m1.203s $ thời gian cho i bằng $ (eval echo "{1..100000}"); làm :; làm xong; người dùng thực 0m13.881s 0m13.536s sys 0m0.152s
Marcin Zaluski

3
Phải, nhưng eval là ác ... @MarcinZaluski time for i in $(seq 100000); do :; donenhanh hơn rất nhiều!
F. Hauri

Hiệu suất phải là nền tảng cụ thể vì phiên bản eval là nhanh nhất trên máy của tôi.
Andrew Prock

103

Đây là lý do tại sao biểu thức ban đầu không hoạt động.

Từ người đàn ông bash :

Mở rộng cú đúp được thực hiện trước bất kỳ bản mở rộng nào khác và kết quả là bất kỳ ký tự nào đặc biệt cho các bản mở rộng khác đều được giữ nguyên. Nó là đúng văn bản. Bash không áp dụng bất kỳ giải thích cú pháp nào cho bối cảnh mở rộng hoặc văn bản giữa các dấu ngoặc nhọn.

Vì vậy, mở rộng cú đúp là một cái gì đó được thực hiện sớm như là một hoạt động macro hoàn toàn bằng văn bản, trước khi mở rộng tham số.

Shell là các giống lai được tối ưu hóa cao giữa các bộ xử lý macro và các ngôn ngữ lập trình chính thức hơn. Để tối ưu hóa các trường hợp sử dụng thông thường, ngôn ngữ được thực hiện khá phức tạp và một số hạn chế được chấp nhận.

sự giới thiệu

Tôi sẽ đề nghị gắn bó với các tính năng Posix 1 . Điều này có nghĩa là sử dụng for i in <list>; do, nếu danh sách đã được biết, nếu không, sử dụng whilehoặc seq, như trong:

#!/bin/sh

limit=4

i=1; while [ $i -le $limit ]; do
  echo $i
  i=$(($i + 1))
done
# Or -----------------------
for i in $(seq 1 $limit); do
  echo $i
done


1. Bash là một cái vỏ tuyệt vời và tôi sử dụng nó một cách tương tác, nhưng tôi không đưa bash-isms vào kịch bản của mình. Các kịch bản có thể cần một lớp vỏ nhanh hơn, một lớp an toàn hơn, một kiểu được nhúng nhiều hơn. Họ có thể cần chạy trên bất cứ thứ gì được cài đặt dưới dạng / bin / sh, và sau đó có tất cả các đối số theo tiêu chuẩn pro thông thường. Nhớ shellshock, còn gọi là bashdoor?


13
Tôi không có sức mạnh, nhưng tôi sẽ đưa danh sách này lên một chút, trên tất cả các kiểu nhìn rốn nhưng ngay sau kiểu chữ C để đánh giá vòng lặp và số học.
đời 15/03/13

2
Một hàm ý là mở rộng cú đúp không tiết kiệm nhiều bộ nhớ so với seqphạm vi lớn. Ví dụ, echo {1..1000000} | wctiết lộ rằng tiếng vang tạo ra 1 dòng, một triệu từ và 6,888,896 byte. Thử seq 1 1000000 | wcmang lại một triệu dòng, một triệu từ và 6,888,896 byte và cũng nhanh hơn bảy lần, như được đo bằng timelệnh.
George

Lưu ý: Tôi đã đề cập đến whilephương pháp POSIX trước đây trong câu trả lời của mình: stackoverflow.com/a/31365662/895245 Nhưng rất vui khi bạn đồng ý :-)
Ciro Santilli 病毒 审查 六四 事件 法轮功

Tôi đã bao gồm câu trả lời này trong câu trả lời so sánh hiệu suất của tôi dưới đây. stackoverflow.com/a/54770805/117471 (Đây là một lưu ý cho bản thân tôi để theo dõi những gì tôi còn lại để làm.)
Bruno Bronosky

@mateor Tôi nghĩ kiểu C cho vòng lặp và đánh giá số học là cùng một giải pháp. Tui bỏ lỡ điều gì vậy?
Oscar Zhang

73

Cách POSIX

Nếu bạn quan tâm đến tính di động, hãy sử dụng ví dụ từ tiêu chuẩn POSIX :

i=2
end=5
while [ $i -le $end ]; do
    echo $i
    i=$(($i+1))
done

Đầu ra:

2
3
4
5

Những thứ không phải là POSIX:


Chỉ có 4 câu trả lời cho câu trả lời này, điều này rất bất thường. Nếu điều này đã được đăng trên một số trang web tổng hợp liên kết, xin vui lòng cho tôi một liên kết, chúc mừng.
Ciro Santilli 冠状 病毒 审查 事件

Các trích dẫn đề cập đến x, không phải toàn bộ biểu thức. $((x + 1))chỉ là tốt
chepner

Mặc dù không có khả năng di động và khác với GNU seq(BSD seqcho phép bạn đặt chuỗi kết thúc chuỗi với -t), FreeBSD và NetBSD cũng có seqtừ 9.0 và 3.0, tương ứng.
Adrian Günter

@CiroSantilli @chepner $((x+1))$((x + 1))phân tích cú pháp giống hệt nhau, như khi phân tích cú pháp tokenizes x+1nó sẽ được chia thành 3 thẻ: x, +, và 1. xkhông phải là mã thông báo số hợp lệ, nhưng nó là mã thông báo tên biến hợp lệ, tuy nhiên x+không phải là phân tách. +là mã thông báo toán tử số học hợp lệ, tuy nhiên +1không phải vậy, vì vậy mã thông báo lại được phân chia ở đó. Và kể từ đó trở đi.
Adrian Günter

Tôi đã bao gồm câu trả lời này trong câu trả lời so sánh hiệu suất của tôi dưới đây. stackoverflow.com/a/54770805/117471 (Đây là một lưu ý cho bản thân tôi để theo dõi những gì tôi còn lại để làm.)
Bruno Bronosky

35

Một lớp khác của sự gián tiếp:

for i in $(eval echo {1..$END}); do
    

2
+1: Ngoài ra, eval 'cho i trong {1 ..' $ END '}; làm ... 'eval dường như là cách tự nhiên để giải quyết vấn đề này.
William Pursell

28

Bạn có thể dùng

for i in $(seq $END); do echo $i; done

seq liên quan đến việc thực hiện một lệnh bên ngoài thường làm mọi thứ chậm lại.
paxdiablo

9
Nó không liên quan đến việc thực hiện một lệnh bên ngoài cho mỗi lần lặp, chỉ một lần. Nếu thời gian để khởi chạy một lệnh bên ngoài là một vấn đề, bạn đang sử dụng ngôn ngữ sai.
Mark Baker

1
Vì vậy, làm tổ là trường hợp duy nhất trong trường hợp này? Tôi đã tự hỏi nếu có một sự khác biệt hiệu suất, hoặc một số tác dụng phụ kỹ thuật chưa biết?
Sqeaky

@Squeaky Đó là một câu hỏi riêng biệt được trả lời ở đây: stackoverflow.com/questions/4708549/ trên
tripleee

Tôi đã bao gồm câu trả lời này trong câu trả lời so sánh hiệu suất của tôi dưới đây. stackoverflow.com/a/54770805/117471 (Đây là một lưu ý cho bản thân tôi để theo dõi những gì tôi còn lại để làm.)
Bruno Bronosky

21

Nếu bạn cần tiền tố hơn bạn có thể thích điều này

 for ((i=7;i<=12;i++)); do echo `printf "%2.0d\n" $i |sed "s/ /0/"`;done

điều đó sẽ mang lại

07
08
09
10
11
12

4
Sẽ không printf "%02d\n" $idễ dàng hơn printf "%2.0d\n" $i |sed "s/ /0/"?
zb226

19

Nếu bạn đang sử dụng BSD / OS X, bạn có thể sử dụng jot thay vì seq:

for i in $(jot $END); do echo $i; done

17

Điều này hoạt động tốt trong bash:

END=5
i=1 ; while [[ $i -le $END ]] ; do
    echo $i
    ((i = i + 1))
done

6
echo $((i++))hoạt động và kết hợp nó vào một dòng.
Bruno Bronosky

1
Điều này có phần mở rộng bash không cần thiết. Phiên bản POSIX: stackoverflow.com/a/31365662/895245
Ciro Santilli 病毒 审查 六四 事件

1
@Ciro, vì câu hỏi đặc biệt nêu bash và có thẻ bash, tôi nghĩ có lẽ bạn sẽ thấy rằng bash 'phần mở rộng' không quá ổn :-)
paxdiablo

@paxdiablo Tôi không có nghĩa là nó không đúng, nhưng tại sao chúng ta không thể mang theo khi chúng ta có thể ;-)
Ciro Santilli 病毒 审查

Trong bash, chúng ta chỉ đơn giản có thể thực hiện while [[ i++ -le "$END" ]]; dođể tăng (sau) trong bài kiểm tra
Aaron McDaid

14

Tôi đã kết hợp một vài ý tưởng ở đây và đo lường hiệu suất.

TL; DR Takeaways:

  1. seq{..}rất nhanh
  2. forwhilevòng lặp chậm
  3. $( ) chậm
  4. for (( ; ; )) vòng lặp chậm hơn
  5. $(( )) thậm chí còn chậm hơn
  6. Lo lắng về N số trong bộ nhớ (seq hoặc {..}) là ngớ ngẩn (ít nhất lên tới 1 triệu.)

Đây không phải là kết luận . Bạn sẽ phải nhìn vào mã C đằng sau mỗi mã này để đưa ra kết luận. Đây là nhiều hơn về cách chúng ta có xu hướng sử dụng từng cơ chế này để lặp qua mã. Hầu hết các hoạt động đơn lẻ đều đủ gần để có cùng tốc độ mà nó sẽ không thành vấn đề trong hầu hết các trường hợp. Nhưng một cơ chế như for (( i=1; i<=1000000; i++ ))nhiều hoạt động như bạn có thể thấy trực quan. Nó cũng là nhiều hoạt động trên mỗi vòng lặp hơn bạn nhận được từ for i in $(seq 1 1000000). Và điều đó có thể không rõ ràng với bạn, đó là lý do tại sao làm các bài kiểm tra như thế này là có giá trị.

Trình diễn

# show that seq is fast
$ time (seq 1 1000000 | wc)
 1000000 1000000 6888894

real    0m0.227s
user    0m0.239s
sys     0m0.008s

# show that {..} is fast
$ time (echo {1..1000000} | wc)
       1 1000000 6888896

real    0m1.778s
user    0m1.735s
sys     0m0.072s

# Show that for loops (even with a : noop) are slow
$ time (for i in {1..1000000} ; do :; done | wc)
       0       0       0

real    0m3.642s
user    0m3.582s
sys 0m0.057s

# show that echo is slow
$ time (for i in {1..1000000} ; do echo $i; done | wc)
 1000000 1000000 6888896

real    0m7.480s
user    0m6.803s
sys     0m2.580s

$ time (for i in $(seq 1 1000000) ; do echo $i; done | wc)
 1000000 1000000 6888894

real    0m7.029s
user    0m6.335s
sys     0m2.666s

# show that C-style for loops are slower
$ time (for (( i=1; i<=1000000; i++ )) ; do echo $i; done | wc)
 1000000 1000000 6888896

real    0m12.391s
user    0m11.069s
sys     0m3.437s

# show that arithmetic expansion is even slower
$ time (i=1; e=1000000; while [ $i -le $e ]; do echo $i; i=$(($i+1)); done | wc)
 1000000 1000000 6888896

real    0m19.696s
user    0m18.017s
sys     0m3.806s

$ time (i=1; e=1000000; while [ $i -le $e ]; do echo $i; ((i=i+1)); done | wc)
 1000000 1000000 6888896

real    0m18.629s
user    0m16.843s
sys     0m3.936s

$ time (i=1; e=1000000; while [ $i -le $e ]; do echo $((i++)); done | wc)
 1000000 1000000 6888896

real    0m17.012s
user    0m15.319s
sys     0m3.906s

# even a noop is slow
$ time (i=1; e=1000000; while [ $((i++)) -le $e ]; do :; done | wc)
       0       0       0

real    0m12.679s
user    0m11.658s
sys 0m1.004s

1
Đẹp! Đừng đồng ý với tóm tắt của bạn mặc dù. Có vẻ như tôi $(seq)là về tốc độ tương tự {a..b}. Ngoài ra, mỗi thao tác mất khoảng thời gian giống nhau, do đó, thêm khoảng 4μs cho mỗi lần lặp của vòng lặp cho tôi. Ở đây một hoạt động là một tiếng vang trong cơ thể, so sánh số học, gia tăng, vv Có bất kỳ điều này đáng ngạc nhiên? Ai quan tâm đến việc các vật liệu vòng lặp mất bao lâu để thực hiện công việc của mình. Thời gian chạy có thể bị chi phối bởi nội dung của vòng lặp.
bobbogo

@bobbogo bạn nói đúng, nó thực sự là về số lượng hoạt động. Tôi cập nhật câu trả lời của tôi để phản ánh điều này. Nhiều cuộc gọi chúng tôi thực hiện thực hiện nhiều hoạt động hơn chúng ta mong đợi. Tôi đã thu hẹp điều này từ một danh sách khoảng 50 bài kiểm tra tôi đã chạy. Tôi đã dự đoán rằng nghiên cứu của tôi quá phức tạp ngay cả đối với đám đông này. Như mọi khi, tôi đề nghị ưu tiên các nỗ lực mã hóa của bạn như vậy: Làm cho nó ngắn hơn; Làm cho nó dễ đọc; Lam no nhanh hơn; Làm cho nó di động. Thường # 1 nguyên nhân # 3. Đừng lãng phí thời gian của bạn vào # 4 cho đến khi bạn phải.
Bruno Bronosky

8

Tôi biết câu hỏi này là về bash, nhưng - chỉ dành cho hồ sơ - ksh93thông minh hơn và thực hiện nó như mong đợi:

$ ksh -c 'i=5; for x in {1..$i}; do echo "$x"; done'
1
2
3
4
5
$ ksh -c 'echo $KSH_VERSION'
Version JM 93u+ 2012-02-29

$ bash -c 'i=5; for x in {1..$i}; do echo "$x"; done'
{1..5}

8

Đây là một cách khác:

end=5
for i in $(bash -c "echo {1..${end}}"); do echo $i; done

1
Điều này có chi phí sinh sản một vỏ khác.
tiền mã hóa

1
Trên thực tế, điều này là cực kỳ khủng khiếp vì nó sinh ra 2 vỏ khi 1 sẽ đủ.
Bruno Bronosky

8

Nếu bạn muốn ở gần nhất có thể với cú pháp biểu thức cú đúp, hãy thử rangechức năng từ bash-trick 'range.bash .

Ví dụ: tất cả những điều sau đây sẽ thực hiện chính xác như echo {1..10}:

source range.bash
one=1
ten=10

range {$one..$ten}
range $one $ten
range {1..$ten}
range {1..10}

Nó cố gắng hỗ trợ cú pháp bash riêng với càng ít "gotchas" càng tốt: không chỉ các biến được hỗ trợ, mà hành vi thường không mong muốn của các phạm vi không hợp lệ cũng được cung cấp dưới dạng chuỗi (ví dụ for i in {1..a}; do echo $i; done) cũng bị ngăn chặn.

Các câu trả lời khác sẽ hoạt động trong hầu hết các trường hợp, nhưng tất cả chúng đều có ít nhất một trong những nhược điểm sau:

  • Nhiều người trong số họ sử dụng các lớp con , có thể gây hại cho hiệu suấtcó thể không khả thi trên một số hệ thống.
  • Nhiều người trong số họ dựa vào các chương trình bên ngoài. Thậm chí seqlà một nhị phân phải được cài đặt để sử dụng, phải được tải bằng bash và phải chứa chương trình bạn mong đợi, để nó hoạt động trong trường hợp này. Tuyệt vời hay không, đó là rất nhiều để dựa vào chứ không chỉ là ngôn ngữ Bash.
  • Các giải pháp chỉ sử dụng chức năng Bash gốc, như @ ephemient, sẽ không hoạt động trên các phạm vi chữ cái, như {a..z}; cú đúp mở rộng sẽ. Tuy nhiên, câu hỏi là về các dãy số , vì vậy đây là một vấn đề khó hiểu.
  • Hầu hết trong số chúng không giống với {1..10}cú pháp phạm vi mở rộng bằng nẹp, vì vậy các chương trình sử dụng cả hai có thể khó đọc hơn một chút.
  • Câu trả lời của @ bobbogo sử dụng một số cú pháp quen thuộc, nhưng thực hiện điều gì đó bất ngờ nếu $ENDbiến không phải là phạm vi hợp lệ "bookend" cho phía bên kia của phạm vi. Nếu END=a, ví dụ, một lỗi sẽ không xảy ra và giá trị đúng nguyên văn {1..a}sẽ được lặp lại. Đây cũng là hành vi mặc định của Bash - nó thường bất ngờ.

Tuyên bố miễn trừ trách nhiệm: Tôi là tác giả của mã được liên kết.


7

Thay thế {}bằng (( )):

tmpstart=0;
tmpend=4;

for (( i=$tmpstart; i<=$tmpend; i++ )) ; do 
echo $i ;
done

Sản lượng:

0
1
2
3
4

Tôi đã bao gồm câu trả lời này trong câu trả lời so sánh hiệu suất của tôi dưới đây. stackoverflow.com/a/54770805/117471 (Đây là một lưu ý cho bản thân tôi để theo dõi những gì tôi còn lại để làm.)
Bruno Bronosky

6

Đây là tất cả tốt đẹp nhưng seq được cho là không dùng nữa và hầu hết chỉ hoạt động với các phạm vi số.

Nếu bạn đặt vòng lặp for của mình trong dấu ngoặc kép, các biến bắt đầu và kết thúc sẽ bị hủy đăng ký khi bạn lặp lại chuỗi và bạn có thể gửi chuỗi đó trở lại BASH để thực thi. $icần phải được thoát với \ vì vậy nó KHÔNG được đánh giá trước khi được gửi đến mạng con.

RANGE_START=a
RANGE_END=z
echo -e "for i in {$RANGE_START..$RANGE_END}; do echo \\${i}; done" | bash

Đầu ra này cũng có thể được gán cho một biến:

VAR=`echo -e "for i in {$RANGE_START..$RANGE_END}; do echo \\${i}; done" | bash`

"Chi phí" duy nhất mà cái này sẽ tạo ra phải là ví dụ thứ hai của bash để nó phù hợp với các hoạt động chuyên sâu.


5

Nếu bạn đang thực hiện các lệnh shell và bạn (như tôi) có một sự tôn sùng cho đường ống, thì điều này là tốt:

seq 1 $END | xargs -I {} echo {}


3

Có nhiều cách để làm điều này, tuy nhiên những cách tôi thích được đưa ra dưới đây

Sử dụng seq

Tóm tắt từ man seq

$ seq [-w] [-f format] [-s string] [-t string] [first [incr]] last

Cú pháp

Toàn lệnh
seq first incr last

  • đầu tiên là số bắt đầu trong chuỗi [là tùy chọn, theo mặc định: 1]
  • tăng là tăng [là tùy chọn, theo mặc định: 1]
  • cuối cùng là số cuối cùng trong chuỗi

Thí dụ:

$ seq 1 2 10
1 3 5 7 9

Chỉ với đầu tiên và cuối cùng:

$ seq 1 5
1 2 3 4 5

Chỉ cuối cùng:

$ seq 5
1 2 3 4 5

Sử dụng {first..last..incr}

Ở đây đầu tiên và cuối cùng là bắt buộc và tăng là tùy chọn

Chỉ sử dụng đầu tiên và cuối cùng

$ echo {1..5}
1 2 3 4 5

Sử dụng tăng

$ echo {1..10..2}
1 3 5 7 9

Bạn có thể sử dụng điều này ngay cả đối với các nhân vật như dưới đây

$ echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z

3

nếu bạn không muốn sử dụng định dạng mở rộng ' seq' hoặc ' eval' hoặc jothoặc số học, vd. for ((i=1;i<=END;i++))hoặc các vòng lặp khác, vd. whilevà bạn không muốn ' printf' và vui vẻ ' echo', thì cách giải quyết đơn giản này có thể phù hợp với ngân sách của bạn:

a=1; b=5; d='for i in {'$a'..'$b'}; do echo -n "$i"; done;' echo "$d" | bash

PS: seqDù sao thì bash của tôi không có lệnh ''.

Đã thử nghiệm trên Mac OSX 10.6.8, Bash 3.2.48


0

Điều này hoạt động trong Bash và Korn, cũng có thể đi từ số cao hơn đến số thấp hơn. Có lẽ không nhanh nhất hoặc đẹp nhất nhưng hoạt động đủ tốt. Xử lý tiêu cực quá.

function num_range {
   # Return a range of whole numbers from beginning value to ending value.
   # >>> num_range start end
   # start: Whole number to start with.
   # end: Whole number to end with.
   typeset s e v
   s=${1}
   e=${2}
   if (( ${e} >= ${s} )); then
      v=${s}
      while (( ${v} <= ${e} )); do
         echo ${v}
         ((v=v+1))
      done
   elif (( ${e} < ${s} )); then
      v=${s}
      while (( ${v} >= ${e} )); do
         echo ${v}
         ((v=v-1))
      done
   fi
}

function test_num_range {
   num_range 1 3 | egrep "1|2|3" | assert_lc 3
   num_range 1 3 | head -1 | assert_eq 1
   num_range -1 1 | head -1 | assert_eq "-1"
   num_range 3 1 | egrep "1|2|3" | assert_lc 3
   num_range 3 1 | head -1 | assert_eq 3
   num_range 1 -1 | tail -1 | assert_eq "-1"
}
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.