В вашем сценарии используются три функции оболочки Bash, которые предоставляются не всеми оболочками в стиле Борна. Как говорит Химэйл , вы можете просто запустить этот скрипт bash
вместо sh
. Ваша строка hashbang вверху ( #!/bin/bash
) указывает, bash
но эффективна только если вы выполняете скрипт, как объяснил heemayl . Если вы передадите имя скрипта sh
, sh
он не будет автоматически вызываться bash
, а просто запустит скрипт. Это потому, что когда ваш скрипт действительно запущен, строка hashbang не имеет никакого эффекта .
Другой вариант, если вам нужно написать полностью переносимые скрипты, которые не зависят от функций Bash, - это изменить ваш скрипт так, чтобы он работал без них. Используемые вами функции Bash:
- Массив . Это было первым, так что именно это и привело к ошибке, когда вы пытались запустить скрипт с помощью оболочки Dash . Выражение в скобках
( {a..z} )
, которое вы назначаете chars
, создает массив и ${chars[i]}
, который появляется в вашем цикле, индексирует его.
- Брекет-расширение. В Баше, а также во многих других оболочках,
{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
как новую строку. Но оболочка также использует \
в качестве escape-символа. \
Перед $
помешают $
от вызывая расширение произойдет (в данном случае, команда подстановки ), но я не хочу , чтобы предотвратить это, так что я избежал его с другим \
; вот причина \\
. Второй , \
прежде чем n
не нужно экранировать , так как , в отличие от \$
, \n
не имеет особого значения для оболочки в двойных кавычках.
- Для получения дополнительной информации о том, как двойные кавычки и обратный слеш используются в программировании оболочки, см. Раздел о цитировании в международном стандарте . См. Также 3.1.2 Цитирование в Справочном руководстве Bash , особенно 3.1.2.1 Escape-символ и 3.1.2.3 Двойные кавычки . (Вот весь раздел в контексте.) Обратите внимание, что одинарные кавычки (
'
) также являются важной частью синтаксиса цитирования оболочки, я просто не использовал их в этом сценарии.
Это переносимо для большинства Unix-подобных систем и не зависит от того, какую оболочку в стиле Bourne вы используете. Тем не менее, некоторые 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 ]
).