Ваш сценарій використовує три функції оболонки 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 ]).