Đối với vòng lặp với Bảng chữ cái


12

Điều này hoạt động hoàn hảo trên OSX

#!/bin/bash
chars=( {a..z} )
n=3
for ((i=0; i<n; i++))
do
  echo "${chars[i]}"
done

Nhưng khi tôi chạy nó trên Ubuntu, tôi gặp lỗi sau.

ForLoopAlphabetTest.sh: 2: ForLoopAlphabetTest.sh: Syntax error: "(" unexpected

Tôi dường như không thể giải quyết vấn đề. Bất kỳ đề xuất?


2
Điều này hoạt động trên Ubuntu.
Pilot6

Tôi không thể làm cho nó hoạt động trên 16.04 bash 4.3 dưới dạng tập lệnh. Nhưng nó hoạt động nếu tôi sao chép nó qua thiết bị đầu cuối.
denski

Câu trả lời:


25

Có lẽ, bạn đang chạy tập lệnh như:

sh ForLoopAlphabetTest.sh

Trong Ubuntu, shđược liên kết với dash; vì dashkhông có khái niệm về mảng, bạn sẽ nhận được lỗi cú pháp (.

Kịch bản hoạt động hoàn hảo bash, vì vậy sẽ ổn nếu bạn chạy nó dưới dạng bashđối số:

bash ForLoopAlphabetTest.sh

Bây giờ, bạn có bashshebang trên tập lệnh, vì vậy bạn có thể làm cho tập lệnh có thể thực thi được ( chmod u+x ForLoopAlphabetTest.sh) và chạy nó dưới dạng:

/path/to/ForLoopAlphabetTest.sh

hoặc từ thư mục của tập lệnh:

./ForLoopAlphabetTest.sh

Cũng lưu ý rằng, tập lệnh của bạn chứa mở rộng dấu ngoặc {a..z}forcấu trúc kiểu C : for (( ... ))cũng không được hỗ trợ bởi dash; vì vậy nếu mục tiêu của bạn là tính di động, bạn chỉ nên xem các shcú pháp POSIX .


Cảm ơn bạn. Có cách nào để vượt qua sự thiếu khái niệm về mảng không?
denski

3
@denski Nếu bạn muốn viết các tập lệnh di động có thể chạy /bin/shtrên bất kỳ hệ điều hành nào giống Unix, thì bạn sẽ không thể sử dụng mảng. Bash (và một số shell khác) đã thêm chúng vì chúng rất tiện lợi và luôn không thể dễ dàng thay thế bằng mã di động hơn. Tuy nhiên, đối với tập lệnh của bạn nói riêng, bạn có thể thực hiện nó mà không gặp rắc rối và không sử dụng bất kỳ tính năng cụ thể nào của bash. Bạn có quan tâm làm thế nào để làm điều đó?
Eliah Kagan

Nếu bạn có một số gợi ý đọc sẽ hữu ích. Cảm ơn.
denski

1
@denski Tôi đã đăng một câu trả lời bao gồm một số liên kết và ví dụ. Trong nhận xét trước đây của tôi ở đây, tôi đã đề cập đến việc bạn đã sử dụng mảng và kiểu chữ C cho các vòng lặp nhưng không đề cập đến việc bạn sử dụng mở rộng niềng răng. Câu trả lời của tôi bao gồm làm thế nào để làm mà không có cả ba. Lưu ý rằng câu trả lời này (nghĩa là của heemayl, không phải của tôi) là giải pháp chính cho vấn đề của bạn; của tôi tập trung vào cách bạn có thể viết lại tập lệnh của mình nếu bạn không thể dựa vào các tính năng cụ thể của bash.
Eliah Kagan

@heemayl Để ghi lại, tôi muốn thêm rằng bạn đã đúng trong giả định của bạn rằng tôi đang chạy các tập lệnh vớish
denski

10

Kịch bản của bạn sử dụng ba tính năng của trình bao Bash không được cung cấp bởi tất cả các trình bao kiểu Bourne. Như heemayl nói , bạn chỉ có thể chạy tập lệnh đó với bashthay vì sh. Dòng hashbang của bạn ở đầu ( #!/bin/bash) chỉ định bashnhưng chỉ hiệu quả nếu bạn thực thi tập lệnh, như heemayl đã giải thích . Nếu bạn chuyển tên tập lệnh sang sh, shsẽ không tự động gọi bash, nhưng sẽ chỉ chạy tập lệnh. Điều này là do một khi tập lệnh của bạn thực sự chạy, dòng hashbang không có hiệu lực .

Một lựa chọn khác của bạn, nếu bạn cần viết các tập lệnh di động hoàn toàn không phụ thuộc vào các tính năng của Bash, là thay đổi tập lệnh của bạn để nó hoạt động mà không có chúng. Các tính năng Bash bạn sử dụng là:

  • Một mảng . Điều này xuất hiện đầu tiên, vì vậy đây là lỗi tạo ra khi bạn cố chạy tập lệnh của mình với trình bao Dash . Biểu thức được ngoặc đơn ( {a..z} ), mà bạn gán cho chars, tạo một mảng và ${chars[i]}, xuất hiện trong vòng lặp của bạn, lập chỉ mục vào nó.
  • Niềng răng mở rộng. Trong Bash, và trong nhiều shell khác, {a..z}được mở rộng thành 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. Tuy nhiên, đây không phải là một tính năng phổ biến (hoặc được tiêu chuẩn hóa) của các kiểu vỏ Bourne và Dash không hỗ trợ nó.
  • C-phong cách thay thế forcú pháp -loop . Mặc dù dựa trên sự mở rộng số học , bản thân nó không đặc trưng cho Bash (mặc dù một số shell rất cũ, không tuân thủ POSIX cũng không có), forvòng lặp kiểu C Bash-ism và không được di chuyển rộng rãi vỏ khác.

Bash có sẵn rộng rãi, đặc biệt là trên các hệ thống GNU / Linux như Ubuntu và (như bạn đã thấy) cũng có sẵn trên macOS và nhiều hệ thống khác. Xem xét mức độ bạn đang sử dụng các tính năng dành riêng cho Bash, bạn có thể chỉ muốn sử dụng chúng và chỉ cần đảm bảo rằng bạn đang sử dụng Bash (hoặc một số shell khác hỗ trợ các tính năng bạn đang sử dụng) khi bạn chạy tập lệnh của mình.

Tuy nhiên, bạn có thể thay thế chúng bằng các cấu trúc di động nếu bạn muốn. forVòng lặp mảng và kiểu C dễ thay thế; tạo ra phạm vi các chữ cái mà không cần mở rộng dấu ngoặc (và không mã hóa chúng trong tập lệnh của bạn) là một phần hơi khó.


Đầu tiên, đây là một tập lệnh in tất cả các chữ cái Latinh viết thường:

#!/bin/sh

for i in $(seq 97 122); do
    printf "\\$(printf %o $i)\n"
done
  • Các seqlệnh tạo ra các chuỗi số. $( )thực hiện thay thế lệnh , do đó $(seq 97 122)được thay thế bằng đầu ra của seq 97 122. Đây là các mã ký tự cho athông qua z.
  • Sự mạnh mẽ printflệnh có thể biến mã ký tự thành chữ (ví dụ, printf '\141'bản in a, tiếp theo là một newline ), nhưng các mã phải trong bát phân , trong khi seqđầu ra chỉ trong hệ thập phân . Vì vậy, tôi đã sử dụng printfhai lần: bên trong printf %o $ichuyển đổi số thập phân (được cung cấp bởi seq) thành số bát phân và được thay thế thành printflệnh bên ngoài . (Mặc dù cũng có thể sử dụng thập lục phân , nhưng nó không đơn giản và dường như ít di động hơn .)
  • printfthông dịch \theo sau là một số bát phân là ký tự với mã đó và \ndưới dạng một dòng mới. Nhưng vỏ cũng sử dụng \như một nhân vật thoát. Một \ở phía trước $sẽ ngăn chặn $từ gây sự mở rộng để xảy ra (trong trường hợp này, lệnh thay ), nhưng tôi không muốn ngăn chặn điều đó, vì vậy tôi đã thoát khỏi nó bằng một \; đó là lý do cho \\. Cái thứ hai \trước nđó không cần phải thoát vì không giống như \$, \nkhông có ý nghĩa đặc biệt đối với shell trong chuỗi trích dẫn kép.
  • Để biết thêm thông tin về cách trích dẫn kép và dấu gạch chéo ngược được sử dụng trong lập trình shell, hãy xem phần trích dẫn trong tiêu chuẩn quốc tế . Xem thêm 3.1.2 Trích dẫn trong Hướng dẫn tham khảo Bash , đặc biệt là 3.1.2.1 Ký tự thoát3.1.2.3 Dấu ngoặc kép . (Đây là toàn bộ phần , trong ngữ cảnh.) Lưu ý rằng dấu ngoặc đơn ( ') cũng là một phần quan trọng của cú pháp trích dẫn shell, tôi chỉ không tình cờ sử dụng chúng trong tập lệnh đó.

Đây là thiết bị di động cho hầu hết các hệ thống giống Unix và không phụ thuộc vào loại vỏ Bourne nào bạn sử dụng. Tuy nhiên, một số hệ thống giống Unix không seqđược cài đặt theo mặc định ( jotthay vào đó chúng có xu hướng sử dụng , không được cài đặt theo mặc định của hầu hết các hệ thống GNU / Linux). Bạn có thể sử dụng vòng lặp có exprhoặc thay thế số học để tăng tính di động hơn nữa, nếu bạn cần:

#!/bin/sh

i=97
while [ $i -le 122 ]; do
    printf "\\$(printf %o $i)\n"
    i=$((i + 1))
done

Đó là sử dụng một while-loop với các [lệnh tiếp tục chỉ lặp khi $inằm trong phạm vi.


Thay vì in toàn bộ bảng chữ cái, tập lệnh của bạn xác định một biến nvà in các $nchữ cái viết thường đầu tiên . Đây là phiên bản tập lệnh của bạn không dựa trên các tính năng cụ thể của Bash và hoạt động trên Dash, nhưng yêu cầu seq:

#!/bin/sh

n=3 start=97
for i in $(seq $start $((start + n - 1))); do
    printf "\\$(printf %o $i)\n"
done

Điều chỉnh giá trị nthay đổi số lượng chữ được in, như trong tập lệnh của bạn.

Đây là phiên bản không yêu cầu seq:

#!/bin/sh

n=3 i=97 stop=$((i + n))
while [ $i -lt $stop ]; do
    printf "\\$(printf %o $i)\n"
    i=$((i + 1))
done

Ở đó, $stoplà một cao hơn so với mã ký tự của bức thư cuối cùng mà phải được in, vì vậy tôi sử dụng -lt(ít hơn) hơn -le(nhỏ hơn hoặc bằng) với [lệnh. (Nó cũng sẽ làm việc để thực hiện stop=$((i + n - 1))và sử dụng [ $i -le $stop ]).


1
Đây là một câu trả lời chi tiết phi thường, hơn bạn cho giáo dục. Tôi rất nhiều người mới bắt đầu, vì vậy cách viết kịch bản của tôi là mang các yếu tố làm việc được tìm thấy trên internet cho đến khi nó hoạt động. Tôi không nghi ngờ gì về việc có 1) tốt hơn và 2) cách đơn giản hơn để thực hiện những điều tôi đang tạo bằng các tập lệnh và ở trên đi một chặng đường dài để giúp đỡ điều đó.
denski

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.