2

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
a
b
c

output the same as

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

?

Why does a for-loop not require explicitly specifying

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

  • ; ?

Thanks.

Tim
  • 101,790

2 Answers2

7

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.

Peschke
  • 4,148
  • 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 02 '18 at 09:02
4

Because the POSIX grammar allows it.

The format for the for loop is as follows:

for name [ in [word ... ]]
do
    compound-list
done

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.

Kusalananda
  • 333,661