Làm cách nào để tạo vòng lặp số học trong tập lệnh shell POSIX?


12

Tôi biết làm thế nào để tạo một forvòng lặp số học trong bash.

Làm thế nào người ta có thể thực hiện một vòng lặp tương đương trong tập lệnh shell POSIX?

Vì có nhiều cách khác nhau để đạt được cùng một mục tiêu, hãy thoải mái thêm câu trả lời của riêng bạn và giải thích một chút về cách thức hoạt động của nó.

Một ví dụ về một bashvòng lặp như sau:

#!/bin/bash
for (( i=1; i != 10; i++ ))
do
    echo "$i"
done

@ StéphaneChazelas vì đó là một ghi chú về lịch sử dường như dựa trên sự hiểu lầm vì OP không cho rằng đó là một điều bash, mà chỉ đơn giản là sử dụng bash làm ví dụ. Nó không thực sự có liên quan.
terdon

Câu trả lời:


13

Tôi đã tìm thấy thông tin hữu ích trong wiki Shellcheck.net , tôi trích dẫn:

  1. Bash:

    for ((init; test; next)); do foo; done
  2. POSIX:

    : "$((init))"
    while [ "$((test))" -ne 0 ]; do foo; : "$((next))"; done

mặc dù hãy cẩn thận mà i++không phải là POSIX vì vậy sẽ phải được dịch sang, ví dụ như i += 1hoặc i = i + 1.


Vì vậy, đoạn script trên trong câu hỏi có thể được viết lại POSIX-khôn ngoan bằng cách sử dụng các quy tắc như thế này:

#!/bin/sh
: "$((i=1))"
while [ "$((i != 0))" -ne 0 ]
do
    echo "$i"
    : "$((i = i + 1))"
done

Mặc dù ở đây, bạn có thể làm cho nó dễ đọc hơn với:

#!/bin/sh
i=1
while [ "$i" -ne 10 ]
do
    echo "$i"
    i=$((i + 1))
done

như trong init, chúng ta đang gán một giá trị không đổi, vì vậy chúng ta không cần phải đánh giá biểu thức số học. Chữ i != 10in testcó thể dễ dàng được dịch thành [biểu thức và vì next, sử dụng phép gán biến shell trái ngược với phép gán biến trong biểu thức số học, cho phép chúng ta thoát khỏi :và cần trích dẫn.


Bên cạnh i++-> i = i + 1, có nhiều bản dịch hơn về các cấu trúc dành riêng cho ksh / bash không phải là POSIX mà bạn có thể phải thực hiện:

  • i=1, j=2. Các ,toán tử số học không phải là thực sự POSIX (và xung đột với các dấu phân cách thập phân ở một số vùng với ksh93). Bạn có thể thay thế nó bằng một toán tử khác +như trong : "$(((i=1) + (j=2)))"nhưng sử dụng i=1 j=2sẽ dễ đọc hơn rất nhiều.
  • a[0]=1: không có mảng nào trong vỏ POSIX
  • i = 2**20: không có toán tử nguồn trong cú pháp shell POSIX. <<được hỗ trợ mặc dù vậy đối với quyền hạn của hai, người ta có thể sử dụng i = 1 << 20. Đối với các quyền hạn khác, người ta có thể dùng đến bc:i=$(echo "3 ^ 20" | bc)
  • i = RANDOM % 3: không phải POSIX. Gần nhất trong bộ công cụ POSIX là i=$(awk 'BEGIN{srand(); print int(rand() * 3)}').

2

cảm ơn về kiến ​​thức nền tảng sâu sắc về sự khác biệt. Một sự thay thế phù hợp với tôi khi sử dụng shellcheck.net như dưới đây.

BASH

for i in {1..100}; do  
  ...  
done  

POSIX

i=0; while [ $i -le 100 ]; do  
  ...  
  i=$(( i + 1 ))  
done

một số người lưu ý rằng seq cũng là một tùy chọn sử dụng seq 1 10. Tạo một vòng lặp, tuy nhiên điều này phụ thuộc vào việc os có seq.


lưu ý rằng tôi đã đặt i = 0 trong cùng một dòng với while. mặc dù khả năng đọc không lớn, nhưng nó là một phần thả xuống. Điều này đảm bảo rằng biến i không bị làm mờ bởi bất kỳ nơi nào khác được xác định trong tập lệnh.
Jimmy MG Lim
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.