Для цикла с алфавитом

Это отлично работает на 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

Я не могу решить проблему. Какие-либо предложения?

2 ответа

Решение

Предположительно, вы запускаете скрипт как:

sh ForLoopAlphabetTest.sh

В Ubuntu sh связан с 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, Ваша строка хэшбэнг вверху (#!/bin/bash) указывает bash но эффективен, только если вы выполняете сценарий, как объяснил Химайл. Если вы передадите имя скрипта sh, sh не будет автоматически звонить bash, но просто запустит скрипт. Это потому, что когда ваш скрипт действительно запущен, строка hashbang не имеет никакого эффекта.

Другой вариант, если вам нужно написать полностью переносимые скрипты, которые не зависят от функций Bash, - это изменить ваш скрипт так, чтобы он работал без них. Используемые вами функции 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

Это переносимо для большинства 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 петля с [ Команда продолжить цикл только тогда, когда $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 ]).

Другие вопросы по тегам