Почему нельзя изменить порядок оператора перенаправления ввода для циклов while?

11

В Bash вы можете переместить операторы перенаправления ввода в начало команды:

cat <<< "hello"
# equivalent to
<<< "hello" cat

Почему вы не можете сделать то же самое для цикла while?

while read -r line; do echo "$line"; done <<< "hello"
# hello

<<< "hello" while read -r line; do echo "$line"; done
# -bash: syntax error near unexpected token `do'

Я нахожу это немного сбивающим с толку, так как вы можете перейти в цикл while. Я что-то не так делаю или это просто дизайнерское решение?

philraj
источник

Ответы:

16

Это всего лишь следствие того, как определяется грамматика. Из спецификации грамматики оболочки POSIX :

command          : simple_command
                 | compound_command
                 | compound_command redirect_list
                 | function_definition
                 ;

И:

simple_command   : cmd_prefix cmd_word cmd_suffix
                 | cmd_prefix cmd_word
                 | cmd_prefix
                 | cmd_name cmd_suffix
                 | cmd_name
                 ;
[...]
cmd_prefix       :            io_redirect
                 | cmd_prefix io_redirect
                 |            ASSIGNMENT_WORD
                 | cmd_prefix ASSIGNMENT_WORD
                 ;
cmd_suffix       :            io_redirect
                 | cmd_suffix io_redirect
                 |            WORD
                 | cmd_suffix WORD
                 ;

Как вы можете видеть, с составными командами перенаправление разрешено только после , но с простыми командами, оно также разрешено и раньше. Таким образом, когда оболочка видит <redirection> foo, fooобрабатывается как простая команда, а не как составная команда, и whileбольше не рассматривается как ключевое слово:

$ < foo while
bash: while: command not found

Следовательно, doэто неожиданно, так как это разрешено только после определенных ключевых слов.

Так что это относится не только к whileциклам, но и к большинству способов установки составных команд с использованием зарезервированных слов:

$ < foo {
bash: {: command not found
$ < foo if
bash: if: command not found
$ < foo for
bash: for: command not found
Мур
источник
Хорошее объяснение, спасибо.
Philraj