Запрещена ли точка с запятой после имени в " 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
, хотя реализации не настаивают на этом.на
ksh
manpage перечисляет синтаксис как: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 и широко и намеренно поддерживается как расширение.