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 bash
thay vì sh
. Dòng hashbang của bạn ở đầu ( #!/bin/bash
) chỉ định bash
như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
, sh
sẽ 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ế
for
cú 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ó), for
vòng lặp kiểu C là 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. for
Vò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
seq
lệ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 a
thông qua z
.
- Sự mạnh mẽ
printf
lệ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 printf
hai lần: bên trong printf %o $i
chuyể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 printf
lệ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 .)
printf
thông dịch \
theo sau là một số bát phân là ký tự với mã đó và \n
dướ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ư \$
, \n
khô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át và 3.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 ( jot
thay 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ó expr
hoặ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 $i
nằ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 n
và in các $n
chữ 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ị n
thay đổ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
Ở đó, $stop
là 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 ]
).