Это отлично работает на OSX
#!/bin/bash
chars=( {a..z} )
n=3
for ((i=0; i<n; i++))
do
echo "${chars[i]}"
done
Но когда я запускаю его в Ubuntu, я получаю следующую ошибку.
ForLoopAlphabetTest.sh: 2: ForLoopAlphabetTest.sh: Syntax error: "(" unexpected
Я не могу решить эту проблему. Какие-либо предложения?
Ответы:
Предположительно, вы запускаете скрипт как:
В Ubuntu
sh
есть символическая ссылка наdash
; посколькуdash
не имеет понятия о массивах, вы получаете синтаксическую ошибку для(
.Сценарий отлично работает
bash
, поэтому было бы хорошо, если бы вы запускали его какbash
аргумент:Теперь у вас есть
bash
сценарий, поэтому вы можете сделать скрипт исполняемым (chmod u+x ForLoopAlphabetTest.sh
) и запустить его как:или из каталога скрипта:
Также обратите внимание, что ваш скрипт содержит расширение скобок
{a..z}
иfor
конструкцию в стиле C :for (( ... ))
которые также не поддерживаютсяdash
; поэтому, если ваша цель - переносимость, вы должны смотреть только наsh
синтаксис POSIX .источник
/bin/sh
в любой Unix-подобной операционной системе, вы не сможете использовать массивы. Bash (и некоторые другие оболочки) добавили их, потому что они очень удобны и не всегда могут быть легко заменены более переносимым кодом. Тем не менее, для вашего скрипта, в частности, вы можете сделать это без проблем и без использования каких-либо специфичных для bash функций. Вы заинтересованы в том, как это сделать?sh
В вашем сценарии используются три функции оболочки Bash, которые предоставляются не всеми оболочками в стиле Борна. Как говорит Химэйл , вы можете просто запустить этот скрипт
bash
вместоsh
. Ваша строка hashbang вверху (#!/bin/bash
) указывает,bash
но эффективна только если вы выполняете скрипт, как объяснил heemayl . Если вы передадите имя скриптаsh
,sh
он не будет автоматически вызыватьсяbash
, а просто запустит скрипт. Это потому, что когда ваш скрипт действительно запущен, строка hashbang не имеет никакого эффекта .Другой вариант, если вам нужно написать полностью переносимые скрипты, которые не зависят от функций Bash, - это изменить ваш скрипт так, чтобы он работал без них. Используемые вами функции Bash:
( {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 не поддерживает ее.for
-loop синтаксис . Несмотря на то, что он основан на арифметическом расширении , которое не является специфическим для Bash (хотя некоторые очень старые, не POSIX-совместимые оболочки также не имеют его),for
цикл в стиле C представляет собой Bash-ism и не является широко переносимым для другие снаряды.Bash широко доступен, особенно в системах GNU / Linux, таких как Ubuntu, и (как вы видели) также доступен в macOS и многих других системах. Учитывая, сколько вы используете специфичных для Bash функций, вы можете просто захотеть использовать их и просто убедиться, что вы используете Bash (или какую-либо другую оболочку, которая поддерживает используемые вами функции) при запуске ваших сценариев.
Однако вы можете заменить их переносимыми конструкциями, если хотите. Массив и
for
цикл в стиле C легко заменить; генерация диапазона букв без расширения фигурных скобок (и без жесткого кодирования их в вашем скрипте) - это немного сложная часть.Во-первых, вот скрипт, который печатает все строчные латинские буквы:
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
не имеет особого значения для оболочки в двойных кавычках.'
) также являются важной частью синтаксиса цитирования оболочки, я просто не использовал их в этом сценарии.Это переносимо для большинства Unix-подобных систем и не зависит от того, какую оболочку в стиле Bourne вы используете. Тем не менее, некоторые Unix-подобные системы не
seq
установлены по умолчанию (jot
вместо этого они, как правило, используют , что не устанавливается по умолчанию в большинстве систем GNU / Linux). Вы можете использовать цикл сexpr
или арифметической заменой для дальнейшего увеличения переносимости, если вам необходимо:Это использует
while
-loop с в[
команде , чтобы продолжить цикл только тогда , когда$i
находится в пределах досягаемости.Вместо того, чтобы печатать весь алфавит, ваш скрипт определяет переменную
n
и печатает первые$n
строчные буквы. Вот версия вашего скрипта, которая не использует специфичные для Bash функции и работает на Dash, но требуетseq
:Регулировка значения
n
изменяет количество печатаемых букв, как в вашем скрипте.Вот версия, которая не требует
seq
:Там,
$stop
один выше , чем характер код последнего письма , которое должно быть напечатано, поэтому я использую-lt
(меньше), чем-le
(меньше или равно) с[
командой. (Это также работало бы, чтобы сделатьstop=$((i + n - 1))
и использовать[ $i -le $stop ]
).источник