Для циклу з алфавітом


12

Це прекрасно працює на 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
Це працює на Ubuntu.
Пілот6

Я не можу змусити його працювати 16.04 bash 4.3 як сценарій. Але це спрацьовує, якщо я скопіюю його в термінал.
denski

Відповіді:


25

Імовірно, ви запускаєте сценарій як:

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}і forконструкцію в стилі C : for (( ... ))які також не підтримуються dash; тож якщо ваша мета - портативність, вам слід переглянути shлише синтаксиси POSIX .


Дякую. Чи є спосіб уникнути відсутності тире поняття масиву?
denski

3
@denski Якщо ви хочете написати портативні скрипти, якими можна керувати /bin/shв будь-якій операційній системі Unix, ви не зможете використовувати масиви. Bash (і деякі інші оболонки) додали їх, оскільки вони дуже зручні і не завжди можуть бути легко замінені на більш портативний код. Однак, особливо для вашого сценарію, ви можете робити це без проблем і без використання будь-яких особливостей bash. Вас цікавить, як це зробити?
Елія Каган

Якщо ви запропонували прочитати, це було б корисно. Дякую.
denski

1
@denski Я опублікував відповідь, яка містить деякі посилання та приклади. У своєму попередньому коментарі тут я згадував, що ви використовували масиви та С-стиль для циклів, але не згадував про ваше використання розширення дужок. Моя відповідь стосується того, як обійтися без усіх трьох. Зауважте, що ця відповідь (тобто, геемай, а не моя) є основним рішенням вашої проблеми; моя зосереджується на тому, як ви можете переписати свій сценарій, якщо ви не можете покластися на особливості bash.
Елія Каган

@heemayl Для запису я хотів додати, що ви були правильні у вашому припущенні, що я запускаю сценарії зsh
denski

10

Ваш сценарій використовує три функції оболонки Bash, які надаються не всіма оболонками в стилі Борна. Як говорить heemayl , ви можете просто запустити цей сценарій, bashа не sh. Ваша hashbang лінія у верхній ( #!/bin/bash) специфікує , bashале ефективний тільки якщо ви виконати сценарій, як і heemayl пояснив . Якщо ви передасте ім'я скрипту sh, shне буде автоматично дзвонити bash, а просто запустить сценарій. Це тому, що коли ваш сценарій фактично запущений, рядок хешбангу не впливає .

Ваша інша альтернатива, якщо вам потрібно написати повністю портативні сценарії, які не залежать від можливостей Bash, - це змінити свій сценарій так, щоб він працював без них. Використовувані функції Bash:

Bash широко доступний, особливо в таких системах GNU / Linux, як Ubuntu, і (як ви вже бачили) також доступний на macOS та багатьох інших системах. Враховуючи те, наскільки ви використовуєте функції Bash, ви можете просто скористатися ними та просто переконатися, що ви використовуєте Bash (або іншу оболонку, яка підтримує функції, які ви використовуєте) під час запуску сценаріїв.

Однак ви можете замінити їх портативними конструкціями, якщо вам це подобається. Масив і forцикл у стилі C легко замінити; генерування діапазону літер без дугового розширення (і без жорсткого кодування їх у вашому сценарії) - це та частина, яка є трохи хитрою.


По-перше, ось сценарій, який друкує всі малі латинські літери:

#!/bin/sh

for i in $(seq 97 122); do
    printf "\\$(printf %o $i)\n"
done

Це портативно для більшості 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 ]).


1
Це феноменально детальна відповідь, ніж ви за освіту. Я дуже новачок, тому мій спосіб написання сценаріїв - це об'єднання робочих елементів, знайдених в Інтернеті, поки це не працює. Я не сумніваюся, що є 1) кращі та 2) простіші способи робити те, що я створюю за допомогою сценаріїв, і вищесказане допомагає в цьому допомогти.
denski

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.