Ваш сценарій використовує три функції оболонки Bash, які надаються не всіма оболонками в стилі Борна. Як говорить heemayl , ви можете просто запустити цей сценарій, bash
а не sh
. Ваша hashbang лінія у верхній ( #!/bin/bash
) специфікує , bash
але ефективний тільки якщо ви виконати сценарій, як і heemayl пояснив . Якщо ви передасте ім'я скрипту sh
, sh
не буде автоматично дзвонити bash
, а просто запустить сценарій. Це тому, що коли ваш сценарій фактично запущений, рядок хешбангу не впливає .
Ваша інша альтернатива, якщо вам потрібно написати повністю портативні сценарії, які не залежать від можливостей Bash, - це змінити свій сценарій так, щоб він працював без них. Використовувані функції Bash:
- Масив . Це було першим, тому саме це спричинило помилку, коли ви намагалися запустити свій скрипт із оболонкою Dash . Вираз у дужках
( {a..z} )
, якому ви призначаєте chars
, створює масив і ${chars[i]}
, який з’являється у вашому циклі, вказує на нього.
- Розширення брекетів. У Bash, а також у багатьох інших оболонках,
{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
. Однак це не універсальна (або стандартизована) особливість оболонок у стилі Борна, і Dash не підтримує це.
- C-стилі альтернативний
for
-loop синтаксис . Хоча на основі арифметичного розширення , яке само по собі не характерне для Bash (хоча деякі дуже старі, не сумісні з POSIX оболонками , його також немає), for
цикл стилю C є Bash-ism і не є широко переносним для інші снаряди.
Bash широко доступний, особливо в таких системах GNU / Linux, як Ubuntu, і (як ви вже бачили) також доступний на macOS та багатьох інших системах. Враховуючи те, наскільки ви використовуєте функції Bash, ви можете просто скористатися ними та просто переконатися, що ви використовуєте Bash (або іншу оболонку, яка підтримує функції, які ви використовуєте) під час запуску сценаріїв.
Однак ви можете замінити їх портативними конструкціями, якщо вам це подобається. Масив і for
цикл у стилі C легко замінити; генерування діапазону літер без дугового розширення (і без жорсткого кодування їх у вашому сценарії) - це та частина, яка є трохи хитрою.
По-перше, ось сценарій, який друкує всі малі латинські літери:
#!/bin/sh
for i in $(seq 97 122); do
printf "\\$(printf %o $i)\n"
done
seq
Команда генерує числові послідовності. $(
)
виконує підстановку команд , тому $(seq 97 122)
її замінюють на вихід seq 97 122
. Ці коди символів для a
через z
.
- Потужна
printf
команда може перетворювати символьні коди в літери (наприклад, printf '\141'
друкується з a
наступним новим рядком ), але коди повинні бути у восьмикутнику , а seq
виводи - лише у десятковій . Тому я використав printf
двічі: внутрішній printf %o $i
перетворює десяткові числа (надані seq
) у вісімкові і замінюється на зовнішню printf
команду. (Хоча також можна використовувати шістнадцятковий , він не простіший і здається менш портативним .)
printf
інтерпретує \
подальше восьмеричне число як символ із цим кодом та \n
як новий рядок. Але оболонка також використовується \
як символ втечі. \
Перед $
завадять $
від викликаючи розширення відбудеться (в даному випадку, команда підстановки ), але я не хочу , щоб запобігти цьому, так що я уникнув його з іншим \
; це причина для \\
. Друге \
перед цим n
не потрібно уникати, оскільки, на відміну від цього \$
, \n
не має особливого значення для оболонки у двоцитуваному рядку.
- Для отримання додаткової інформації про те, як в програмуванні оболонок використовуються подвійні лапки та зворотна риса, див . Розділ про цитування в міжнародному стандарті . Див. Також 3.1.2 Цитування в Довідковому посібнику Bash , особливо 3.1.2.1. Escape Character та 3.1.2.3 Double Quotes . (Ось у цьому розділі весь контекст.) Зауважте, що одиничні цитати (
'
) також є важливою частиною синтаксису цитування оболонок, я просто не випадково використовував їх у цьому сценарії.
Це портативно для більшості Unix-подібних систем і не залежить від того, яку оболонку в стилі Борна ви використовуєте. Однак деякі Unix-подібні системи не seq
встановлені за замовчуванням (вони, як правило, використовують jot
, що не встановлено за замовчуванням більшості систем GNU / Linux). Ви можете використовувати цикл із expr
заміною чи арифметикою для подальшого збільшення портативності, якщо вам потрібно:
#!/bin/sh
i=97
while [ $i -le 122 ]; do
printf "\\$(printf %o $i)\n"
i=$((i + 1))
done
Це використовує while
-loop з в [
команді , щоб продовжити цикл тільки тоді , коли $i
знаходиться в межах досяжності.
Замість того, щоб друкувати цілий алфавіт, ваш сценарій визначає змінну n
і друкує перші $n
малі літери. Ось версія вашого сценарію, яка не поширюється на особливості Bash і працює на Dash, але вимагає seq
:
#!/bin/sh
n=3 start=97
for i in $(seq $start $((start + n - 1))); do
printf "\\$(printf %o $i)\n"
done
Регулювання значення n
зміни кількості друкованих літер, як у вашому сценарії.
Ось версія, яка не вимагає 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
є на один вищий код символу останньої літери, який слід було надрукувати, тому я використовую -lt
(менше ніж), а не -le
(менше або рівний) з [
командою. (Це також працювало б для виготовлення stop=$((i + n - 1))
та використання [ $i -le $stop ]
).