Input redirection to for and while

The POSIX grammar is written that way. A command is defined as:

command          : simple_command
                 | compound_command
                 | compound_command redirect_list
                 | function_definition

Where a simple command is

simple_command   : cmd_prefix cmd_word cmd_suffix
                 [...]

and both cmd_prefix and cmd_suffix eventually allow redirections. For compound commands, there's only the redirect_list at the end that allows them, nothing to allow it in the front.

It doesn't exactly have to be like this, e.g. Zsh accepts this just fine:

% > output for x in a b c ; do echo $x; done
% cat output 
a
b
c

But that's Zsh being incompatible, since in standard shells the leading redirection forces the for command to be parsed as a regular simple command, meaning that the following is valid and the shell will try to find and run a command literally called for (with five arguments, and stdout redirected). Giving the error you saw:

$ > output for x in a b c
bash: for: command not found

(In Bash in particular, the whole input line is parsed first, so if you give Bash the whole for-do-done from the first example on a single line, it just drops a syntax error for the do, and doesn't execute the for before that.)

If I had to guess, I'd expect the root reason for the definition is that it's always worked like that, and so the standard has codified it like that.

Though note that there has been discussion about changing the standard in this regard. (Probably no-one in their right mind actually relies on > output for ... meaning a command called for instead of the shell loop).


for and while are not commands, they are reserved words that start a loop header. You can use redirection for the whole loop but it must be at the end.

This does not work:

<file for((i=0;i<3;i++)); do read line; echo "$line"; done

This does work:

      for((i=0;i<3;i++)); do read line; echo "$line"; done <file