Это работает отлично над 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
Я, может казаться, не решаю проблему. Какие-либо предложения?
По-видимому, Вы запускаете скрипт как:
sh ForLoopAlphabetTest.sh
В Ubuntu, sh
symlinked к dash
; как dash
не имеет никакого понятия массивов, Вы получаете синтаксическую ошибку для (
.
Сценарий работает отлично над bash
, таким образом, было бы хорошо при выполнении его как bash
аргумент:
bash ForLoopAlphabetTest.sh
Теперь, Вы имеете bash
хижина на сценарии, таким образом, Вы могли сделать исполняемый файл сценария (chmod u+x ForLoopAlphabetTest.sh
), и выполненный это как:
/path/to/ForLoopAlphabetTest.sh
или из каталога сценария:
./ForLoopAlphabetTest.sh
Также обратите внимание, что, Ваш сценарий содержит расширение фигурной скобки {a..z}
, и C-стиль for
конструкция: for (( ... ))
которые также не поддерживаются dash
; таким образом, если Вашей целью является мобильность, необходимо посмотреть на POSIX sh
синтаксисы только.
Ваш сценарий использует три функции оболочки Bash, которые не обеспечиваются всеми оболочками стиля Границы. Как heemayl говорит, можно просто запустить тот скрипт с 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
. Однако это не универсальное (или стандартизированный) функция оболочек стиля Границы, и Тире не поддерживает ее.for
- синтаксис цикла. Хотя на основе арифметического расширения, которое не самостоятельно характерно для Bash (хотя у некоторых очень старых, non-POSIX-compliant оболочки нет его, ни один), C-стиль for
цикл является измом Bash и не является широко портативным к другим оболочкам.Bash широко доступен, особенно в системах GNU/Linux как Ubuntu, и (как Вы видели), также доступно на macOS и многих других системах. Рассмотрение, насколько Вы используете определенные для Bash функции, Вы могли бы просто хотеть использовать их и просто удостовериться, что Вы используете Bash (или некоторая другая оболочка, которая поддерживает функции, которые Вы используете), когда Вы запускаете свои скрипты.
Однако можно заменить их портативными конструкциями, если Вам нравится. Массив и C-стиль for
цикл легко заменить; генерация диапазона букв без расширения фигурной скобки (и без жесткого кодирования их в Вашем сценарии) является одной частью, это немного хитро.
Во-первых, вот сценарий, который печатает все строчные латинские буквы:
#!/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
как новая строка. Но оболочка также использует \
как символ ESC. A \
перед $
предотвратит $
от того, чтобы заставлять расширение произойти (в этом случае, управляйте заменой), но я не хочу предотвращать это, таким образом, я вышел из него с другим \
; это - причина \\
. Второе \
прежде n
не должен быть оставлен потому что, в отличие от этого \$
, \n
не имеет особого значения к оболочке в дважды заключенной в кавычки строке.'
) также важная часть синтаксиса заключения в кавычки оболочки, я просто, оказывается, не использовал их в том сценарии.Это портативно к большинству подобных 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
Это использует a while
- цикл с [
команда для продолжения цикличного выполнения только, когда $i
находится в диапазоне.
Вместо того, чтобы печатать целый алфавит, Ваш сценарий определяет переменную n
и печатает первое $n
строчные буквы. Вот версия Вашего сценария, который не полагается ни на какие определенные для Bash функции и работы над Тире, но требует 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 ]
).