Запрещена ли точка с запятой после имени в " 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 для точного описания того, где символы и могут быть заменены.


Примечания

  1. по-видимому, это первый вопрос SO, который пары shell и язык-адвоката. Нет праздное любопытство, которые, возможно, были более соответствующий.

  2. на bash руководство не совсем ясно о новых строках; то, что он говорит:

    в большинстве случаев a список в описании команды может быть отделена от остальной части команды одной или несколькими новыми строками, за которыми может следовать новая строка вместо точки с запятой.

    это дает понять, что точка с запятой, предшествующая done может быть заменен переводом строки, но кажется, не упоминает, что такое же преобразование может быть выполнено на точке с запятой, предшествующей do.

  3. и 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 и широко и намеренно поддерживается как расширение.