34

I was recently looking at some code that confused me because it works and I didn't expect it to. The code reduces to this example

#!/bin/bash
for var;
do
  echo "$var"
done

When run with command line arguments is prints them

$ ./test a b c
a
b
c

It is this, that is (to me) unexpected. Why does this not result in an error because var is undefined ? Is using this considered 'good practice' ?

2 Answers 2

34

for loops loop on the positional parameters if no in value1 value2... part is specified in all Bourne-like shells.

That was already the case in the Bourne shell from the late 70s, though in the Bourne shell, you'd need to omit that ; (you can also use for i do (except in some old ash versions where you need a newline before the do)).

See What is the purpose of the "do" keyword in Bash for loops? for more information including more surprising variants.

Doing:

for i
do
  something with "$i"
done

is good practice. It's slightly more portable/reliable than the usually equivalent:

for i in "$@"; do
  something with "$i"
done

for which the Bourne shell, ksh88 have some issues under some conditions (like when $# is 0 in some versions of the Bourne shell (which ${1+"$@"} instead of "$@" can work around) or when $IFS doesn't contain the space character in Bourne and ksh88), or when the nounset option is enabled and $# is 0 in some versions of some shells including bash (again with ${1+"$@"} as a work-around).

1
  • 1
    Had to read this thrice before my brain decided to stop editing out the repeated 'loop' at the start
    – Three Diag
    Jan 16, 2018 at 12:04
24

This is the default behavior, yes. It is documented in the help of the for keyword:

terdon@tpad ~ $ help for
for: for NAME [in WORDS ... ] ; do COMMANDS; done
    Execute commands for each member in a list.

    The `for' loop executes a sequence of commands for each member in a
    list of items.  If `in WORDS ...;' is not present, then `in "$@"' is
    assumed.  For each element in WORDS, NAME is set to that element, and
    the COMMANDS are executed.

    Exit Status:
    Returns the status of the last command executed.

So, when you don't give it a list to iterate over, it will default to iterating over $@, the array of positional parameters (a, b and c in your example).

And this behavior is defined by POSIX so yes, it is considered "good practice" as far as that goes.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .