I read a command from https://unix.stackexchange.com/a/175845/674

Why can

$ bash -c 'for f do echo "$f";done' bash a b c

output the same as

$ bash -c 'for f in "$@"; do echo "$f";done' bash a b c


Why does a for-loop not require explicitly specifying

  • the set of values of the loop variable, as in "$@" and

  • ; ?



Because the POSIX grammar allows it.

The format for the for loop is as follows:

for name [ in [word ... ]]

First, the list of words following in shall be expanded to generate a list of items. Then, the variable name shall be set to each item, in turn, and the compound-list executed each time. If no items result from the expansion, the compound-list shall not be executed. Omitting:

in word...

shall be equivalent to:

in "$@"

The formal grammar for the for loop looks like

for_clause       : For name                                      do_group
                 | For name                       sequential_sep do_group
                 | For name linebreak in          sequential_sep do_group
                 | For name linebreak in wordlist sequential_sep do_group

For is a grammar token for the string for. sequential_sep is either ; or one or several newlines and linebreak is a single optional newline. The do_group at the end is your do ...; done.

This means that the valid for loops are

  1. Loop over "$@":

    for name do ...; done
  2. Loop over "$@":

    for name; do ...; done
  3. Loop over empty list:

    for name in; do ...; done
  4. Loop over non-empty list:

    for name in word-list; do ...; done

The third form is valid but doesn't do anything. It exists to allow loops whose word-list expands to nothing.

  • Another form that works, although IIRC is not documented is for i ; { echo "$i"; }. Seen that somewhere on one of Stephane's answers – Sergiy Kolodyazhnyy Feb 16 '19 at 8:54
  • @SergiyKolodyazhnyy That would work in bash and zsh, but is not POSIX. Both dash and yash chokes on it. – Kusalananda Feb 16 '19 at 9:04
  • And it works in ksh93 where it probably came from. – Sergiy Kolodyazhnyy Feb 16 '19 at 9:13

From the bash man page:

for name [ [ in [ word ... ] ] ; ] do list ; done

The list of words following in is expanded, generating a list of items. The variable name is set to each element of this list in turn, and list is executed each time. If the in word is omitted, the for command executes list once for each positional parameter that is set (see PARAMETERS below). ...

Then from the parameters section:

A positional parameter is a parameter denoted by one or more digits, other than the single digit 0. Positional parameters are assigned from the shell's arguments when it is invoked, and may be reassigned using the set builtin command. ...

In summary, the in keyword and following semicolon are optional. If you omit the in keyword, the parameters for the for loop are taken from the arguments passed to the shell. Therefore, the two code samples you provide are equivalent.

  • If the positional parameters are set with set within the script, the loop without the list will not loop over the arguments of the script, but over the new positional parameters. – Kusalananda Dec 2 '18 at 9:02

Not the answer you're looking for? Browse other questions tagged or ask your own question.