Чому в циклі sh немає ";" після "do"?


28

Чому в циклі оболонки немає ;символу після doнаписання в одному рядку?

Ось що я маю на увазі. Коли пишеться в декількох рядках, forцикл виглядає так:

$ for i in $(jot 2)
> do
>     echo $i
> done

І по одному рядку:

$ for i in $(jot 2); do echo $i; done

Все що звалилися лінії отримати ;після них , за винятком для doлінії, і якщо включати ;, це помилка. Хтось, мабуть, чортів набагато розумніший за мене, вирішив, що це правильно зробити з причини, але я не можу зрозуміти, в чому причина. Мені це здається непослідовним.

Те саме з whileпетлями теж.

$ while something
> do
>     anotherthing
> done
$ while something; do anotherthing; done


2
Це узгоджено: після while/ жодної крапки з комою немає for.
— Дмитро Григор’єв

Те, що відразу спадає на думку, - це просто не потрібно. В іншому випадку крапки з комою розрізняють висловлювання.
— Пол Дрейпер,

Бо це було б зайвим. Без нього немає двозначності.
— користувач207421

Відповіді:


29

Це синтаксис команди. Див. Складені команди

for name [ [in [words …] ] ; ] do commands; done

Примітка: do commands

Більшість людей ставлять doі commandsна окремий рядок, щоб полегшити читабельність, але це не обов'язково, ви можете написати:

for i in thing
do something
done

Я знаю, що це питання стосується оболонки, і я пов'язав з посібником по bash. Це не так написано в посібнику з оболонкою, але саме так написано у статті, яку написав Стівен Борн для журналу byte.

Стівен каже:

Список команд є послідовність з одного або декількох простих команд , розділених або закінчуватися символом нового рядка або ;(точка з коми). Крім того, зарезервованим словам, як " робити і робити" , зазвичай передує новий рядок або ;... У свою чергу кожен раз, коли виконується список команд, що виконуються нижче.


5
Також було б цікаво, чому не повинно бути крапки з комою, як for i in thing; do; something; done. Хоча розрив рядка дозволений, крапка з комою не дозволена.
— rexkogitans

@gidds: так, точно. Ось так структурована граматика оболонок. Суб'єкт somethingє предметом і doзастосовується до нього. Так само, як в англійській структурі речень.
— Пітер Кордес

@gidds Буквальний (або якийсь інший фрагмент синтаксису) необхідний, тому що в умові (у whileсинтаксисі) може входити кілька команд , тому вам потрібно щось диференціювати команди умов від команд тіла циклу. Використання doдля їх розділення - це, мабуть, те, що просто здавалося найбільш придатним.
— JoL

@rexkogitans Дійсно, я ніколи не зрозумів, що це синтаксична помилка. Це заголовне питання також дуже підходить і для цього. Мабуть, це стосується того, щоб не пустити порожнє тіло. Однак ви отримуєте дещо іншу помилку синтаксису, створюючи порожнє тіло з новими рядками, і у вашому прикладі немає порожнього тіла.
— JoL

@rexkogitans Точка з комою є частиною синтаксису циклу і відрізняється від його іншої ролі як термінатора списку команд. Новий рядок відрізняється тим, що якщо його інакше не потрібно або очікується, він розглядається як звичайний пробіл. Граматика оболонки безладна.
— чепнер

12

Я не знаю, в чому полягає початкова причина цього синтаксису, але давайте розглянемо той факт, що whileпетлі можуть приймати кілька команд у розділі стану, наприклад

while a=$(somecmd);
      [ -n "$a" ];
do
    echo "$a"
done

Тут doключове слово необхідно, щоб відокремити розділ стану та основне тіло циклу. doце ключове слово , як while, ifі then(і done), і це , здається, відповідно до інших , що він не вимагає крапки з комою або символу нового рядка після нього , але з'являється безпосередньо перед командою.

Альтернатива (узагальнена до ifта while) виглядала б дещо негарно, нам потрібно писати, наприклад

if; [ -n "$a" ]; then
    some command here
fi

Синтаксис forциклів просто подібний.


11

Синтаксис оболонки на основі префіксу. У ньому є пункти, введені спеціальними ключовими словами. Певні пропозиції мають узгоджуватися.

У whileциклі складається одна або кілька команд тестування:

test ; test ; test ; ...

і за допомогою однієї або декількох команд тіла:

body ; body ; body ; ...

Щось повинно сказати оболонці, що починається цикл. Ось мета цього whileслова:

while test ; test ; test ; ...

Але потім справи неоднозначні. Яка команда - початок тіла? Щось повинно вказувати на це, і ось що doробить префікс:

do body ; body ; body ; ...

і, нарешті, щось має вказувати на те, що останнє тіло було видно; спеціальне ключове слово doneробить це.

Ці ключові слова оболонки не вимагають розділення крапкою з комою, навіть у тому ж рядку. Наприклад, якщо ви закриєте кілька вкладених циклів, їх можна просто мати done done done ....

Швидше, крапка з комою знаходиться між ними, ... test ; body ... якщо вони знаходяться на одній лінії. Ця крапка з комою розуміється як термінатор: вона належить до test. Тому, якщо doключове слово вставлено між ними, воно має проходити між крапкою з комою та body. Якби він був з іншого боку крапки з комою, він був би неправильно вбудований у testсинтаксис команди, а не розміщений між командами.

Синтаксис оболонки спочатку був розроблений Стівеном Борном, а його натхненник Алгол . Борн так любив Алгол, що він використав безліч макросів C у вихідному коді оболонки, щоб зробити C схожим на Algol. Ви можете переглянути джерела оболонки, датовані 1979 року, з версії 7 Unix . Макроси є mac.h, і вони використовуються всюди. Наприклад ifзаяви надаються в якості IF... ELSE... ELIF... FI.


Вихідний код вражає.
— keithpjolley

Так, це сила турніру cpp.
— вкрадений

7

Я б заперечував, що doтут не особливо особливе. Ви отримуєте помилку, оскільки ;не можете запустити список команд. Якби це було, перша команда була б порожньою, а граматика не дозволяє пусті команди (змінні призначення або перенаправлення без команди, так, але абсолютно нічого, ні). Це справедливо в будь-якій кількості місць, де очікується список команд:

$ while true; do ; echo foo; done
dash: 1: Syntax error: ";" unexpected
$ while ; true; do; echo; done
dash: 1: Syntax error: ";" unexpected
$ { ; echo foo; }
dash: 1: Syntax error: ";" unexpected
$ if ; true; then echo foo; fi
dash: 1: Syntax error: ";" unexpected

Навіть:

$ ; ;
dash: 1: Syntax error: ";" unexpected

У всіх цих місцях очікується список команд, і тому ведучий ;несподіваний.


Так - те, як я довільно додав "\ n" після "робити", змусило мене думати, що "робити" - це окремий жетон, а не те, що діяло на наступний блок.
— keithpjolley

Як виходить, що не уникнути нового рядка (який, інакше, схоже, поводиться так само, як крапка з комою в синтаксисі оболонки) буде дозволено після того, як do(і ifт.д.), хоча?
— Геннінг Макгольм

@Henning Будь-яка кількість нових рядків дозволена перед (і після) списком команд. Нові рядки та крапки з комою поводяться по-різному в інших аспектах. Наприклад, розширення псевдоніму робиться, коли читається цілий рядок вводу (тобто, до наступного нового рядка), а не на одну команду.
— муру

3

Синтаксис:

for i in [whitespace separated list of values]
do [newline separated list of commands]
done

Будь-який з нових рядків, як у списку команд, так і до того, як "зробити" або "зроблено", можна замінити на ";".

Новий рядок (але не ";") дозволений після "робити" і перед "в", просто для того, щоб все виглядало приємніше, але не було б сенсу вимагати там нового рядка.


3

if подібний за своїми ключовими словами: це досить читабельно, коли команди короткі:

if   test_commands
then true_commands
else false_commands
fi

Одного разу я зрозумів, що я ввожу довільне \nпісля того, doяк усе це має сенс (до тих пір, поки ви не замислюєтеся про те, щоб не мати змоги вкласти довільне \nміж іншими командами та аргументами).
— keithpjolley

Ну do, then, elseНЕ арг - якби вони були, ви б не дозволили використовувати точку з коми перед ними. Це ключові слова оболонки (див. type if then elif else fi)
— glenn jackman

Я здогадуюсь, що я не була такою чіткою, як хотіла бути у відповіді.
— keithpjolley

Моя думка полягала в тому, що "робити" не схоже на будь-яку "іншу команду". Тож воно підкоряється різним правилам.
— glenn jackman

3

Коротка відповідь, оскільки це визначено граматикою оболонки POSIX . Що-небудь міжdo і doneвважається групою висловлювань, тоді як ;є послідовним роздільником:

for_clause       : For name                                      do_group
                 | For name                       sequential_sep do_group
                 | For name linebreak in          sequential_sep do_group
                 | For name linebreak in wordlist sequential_sep do_group

do_group         : Do compound_list Done 

sequential_sep   : ';' linebreak
                 | newline_list

Крім того, у разі ;необхідності стандарт явно вказав би так, і він включатиме sequential_sepпісля Do.


1
@drewbenn Ви маєте на увазі найперший? Це повторюється за допомогою позиційних параметрів. Отже, якщо у вас є сценарій, який називається ./myscript.sh abc, де abc - аргументи, цикл для i; робити відлуння "$ i"; зробив би цикл через abc, хоча я все ще думаю, що для його роботи потрібна крапка з комою
— Сергій Колодяжний,

Гаразд, і я також усунув мої сумніви
— Сергій Колодяжний,

1

Тому що do - це ключове слово, і що з нього випливає - це по суті параметри. Перекладач Баша знає, що далі випливає. Це схоже на те, як немає ';' слідуючи i в команді for для. Ви можете фактично додати новий рядок після i в для та набрати решту в новому рядку, і він буде добре працювати.

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