为什么在 while 循环之前不允许 heredoc 重定向

Why are heredoc redirections not allowed before a while loop

背景

POSIX Shell 命令语言允许重定向遵循 复合 命令。 standard 说(强调我的)

2.9.4 Compound Commands

The shell has several programming constructs that are "compound commands", which provide control flow for commands. Each of these compound commands has a reserved word or control operator at the beginning, and a corresponding terminator reserved word or operator at the end. In addition, each can be followed by redirections on the same line as the terminator. Each redirection shall apply to all the commands within the compound command that do not explicitly override that redirection.

出于美学原因,我想在我的 while 循环之前放置一个 heredoc,如

<<'RECORDS' while
foo:bar
baz:quux
...
RECORDS
   IFS=: read -r A B
do
  # do something with A and B
done

因为它使代码更易于理解。但是,它在我尝试过的 shell 中不起作用(bashdash)。我收到错误消息说找不到 "while" 命令,我认为这意味着在领先的 heredoc 之后需要一个 simple 命令而不是 compound命令。

我不能将 heredoc 移到 read 之后,因为那样它会在每次迭代时从新的 heredoc 读取第一行。我知道我可以通过将 heredoc 移动到 done 之后来解决这个问题。我还可以在循环之前使用 exec 打开一个 fd 到一个 heredoc,并添加一个重定向到 read.

我的问题

复合命令前不能进行重定向的原因是什么?是否有 shell 支持它,因为它没有被 POSIX 明确禁止?

Z shell (zsh) 将接受这种非标准语法。 POSIX 标准化现有实践,在 shell 的情况下,参考实现是 Korn shell(ksh88,或只是 ksh)。由于该实现仅支持重定向遵循 标准化的复合语句。

您可以通过多种方式以更便携(也更易于阅读)的形式编写循环。例如:

while
  IFS=: read -r A B
do
    echo $A $B
done <<'RECORDS'
foo:bar
baz:quux
RECORDS

是我做这样的事情最常见的方式。或者您可以将循环包装在一个函数中并将输入重定向到该函数:

loop()
{
    while
      IFS=: read -r A B
    do
        echo $A $B
    done
}

<<'RECORDS' loop
foo:bar
baz:quux
RECORDS

这使您可以按照最初的意愿在数据中混合调用函数(我仍然不太明白为什么您认为这样更清楚:-)) 这两种技术都适用于 bashdashkshksh93yashzsh.