Запрещена ли точка с запятой после имени в " for NAME do ..."?
в руководстве bash приведен синтаксис для for составной оператор as
for name [ [ in [ word ... ] ] ; ] do list ; done
что означает, что точка с запятой перед do если in предложение опущено. [Примечание 2].
однако в спецификации Posix перечислены только следующие три производства для for_clause:
for_clause : For name linebreak do_group
| For name linebreak in sequential_sep do_group
| For name linebreak in wordlist sequential_sep do_group
;
Для справки:linebreak является возможно пустой последовательностью NEWLINE пока sequential_sep is либо точка с запятой, либо NEWLINE, возможно, с последующей последовательностью NEWLINE:
newline_list : NEWLINE
| newline_list NEWLINE
;
linebreak : newline_list
| /* empty */
;
separator : separator_op linebreak
| newline_list
;
sequential_sep : ';' linebreak
| newline_list
;
насколько я вижу, это запрещает синтаксис for foo; do :; done.
на практике все оболочки, которые я пробовал (bash, dash, ksh и zsh), принимают оба for foo; do :; done и for foo do :; done без жалоб, независимо от Posix или их собственной документации [Примечание 3].
является ли это случайным упущением в грамматике в стандарте Posix, или следует использовать точку с запятой в этом синтаксисе следует ли считать (обычно реализуемым) расширением стандарта?
дополнительное соглашение
в описании XCU for loop, Posix, похоже, настаивает на новых строках:
формат for цикл выглядит следующим образом:
for name [ in [word ... ]]
do
compound-list
done
однако в Томе обоснования ясно, что грамматика должна быть последним словом:
отображается формат с щедрым использованием символов . См. грамматику в грамматике оболочки XCU для точного описания того, где символы и могут быть заменены.
Примечания
по-видимому, это первый вопрос SO, который пары shell и язык-адвоката. Нет праздное любопытство, которые, возможно, были более соответствующий.
-
на
bashруководство не совсем ясно о новых строках; то, что он говорит:в большинстве случаев a список в описании команды может быть отделена от остальной части команды одной или несколькими новыми строками, за которыми может следовать новая строка вместо точки с запятой.
это дает понять, что точка с запятой, предшествующая
doneможет быть заменен переводом строки, но кажется, не упоминает, что такое же преобразование может быть выполнено на точке с запятой, предшествующейdo. -
и
kshиzshкажется, настаивают на том, что послеname, хотя реализации не настаивают на этом.на
kshmanpage перечисляет синтаксис как:for vname [ in word ... ] ;do list ;done(я считаю, что точка с запятой в
;doи;doneпредставляет собой "точку с запятой или символом новой строки". Я не могу найти никакого определенного утверждения на этот счет, но это единственный способ понять синтаксическое описание.)на
zshинструкция показывает:for name ... [ in word ... ] term do list done
где термин по крайней мере, одна новая строка или ;.
1 ответов
хорошо замечено! У меня нет определенного ответа, но вот что говорит об этом исходный код:
это действительно не действует в оригинальный Bourne shell из AT & T UNIX v7:
(shell has just read `for name`):
IF skipnl()==INSYM
THEN chkword();
t->forlst=item(0);
IF wdval!=NL ANDF wdval!=';'
THEN synbad();
FI
chkpr(wdval); skipnl();
FI
chksym(DOSYM|BRSYM);
учитывая этот фрагмент, он не кажется сознательным дизайнерским решением. Это просто побочный эффект точки с запятой, обрабатываемой как часть in группа, которая пропускается полностью, когда нет "in".
Дэш соглашается, что это не действует в Борне, но добавляет его в качестве дополнения:
/*
* Newline or semicolon here is optional (but note
* that the original Bourne shell only allowed NL).
*/
Ksh93 утверждает, что это действительно, но ничего не говорит о контексте:
/* 'for i;do cmd' is valid syntax */
else if(tok==';')
while((tok=sh_lex(lexp))==NL);
Баш не имеет комментариев, но явно добавляет поддержку для этого дела:
for_command: FOR WORD newline_list DO compound_list DONE
{
$$ = make_for_command (, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), , word_lineno[word_top]);
if (word_top > 0) word_top--;
}
...
| FOR WORD ';' newline_list DO compound_list DONE
{
$$ = make_for_command (, add_string_to_list ("\"$@\"", (WORD_LIST *)NULL), , word_lineno[word_top]);
if (word_top > 0) word_top--;
}
в zsh, это просто побочный эффект парсер:
while (tok == SEPER)
zshlex();
где (SEPER is ; или linefeed). Благодаря этому, zsh счастливо принимает этот цикл:
for foo; ;
;
; ; ; ; ;
; do echo cow; done
для меня все это указывает на преднамеренное упущение в POSIX и широко и намеренно поддерживается как расширение.