bash-hackers-wiki/original_source/syntax/ccmd/classic_for.txt
2023-07-04 00:11:36 +02:00

152 lines
4.4 KiB
Plaintext

====== The classic for-loop ======
===== Synopsis =====
<code>
for <NAME>; do
<LIST>
done
</code>
<code>
for <NAME> in <WORDS>; do
<LIST>
done
</code>
alternative, historical and undocumented syntax ((http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html#tag_23_02_09_12))
<code>
for <NAME>; {
<LIST>
}
for <NAME> in <WORDS>; {
<LIST>
}
</code>
===== Description =====
For every word in ''<WORDS>'', one iteration of the loop is performed and the variable ''<NAME>'' is set to the current word. If no "''in <WORDS>''" is present to give an own word-list, then the positional parameters (''"$@"'') are used (the arguments to the script or function). In this case (and only in this case), the semicolon between the variable name and the ''do'' is optional.
If you use the loop-variable inside the for-loop and it can contain spaces, you need to quote it, since normal word-splitting procedures apply.
:!: Like all loops (both ''for''-loops, ''while'' and ''until''), this loop can be
* terminated (broken) by the ''break'' command, optionally as ''break N'' to break ''N'' levels of nested loops
* forced to immediately do the next iteration using the ''continue'' command, optionally as ''continue N'' analog to ''break N''
Bash knows an alternative syntax for the ''for'' loop, enclosing the loop body in ''{<nowiki>...</nowiki>}'' instead of ''do <nowiki>...</nowiki> done'':
<code bash>
for x in 1 2 3
{
echo $x
}
</code>
This syntax is **not documented** and should not be used. I found the parser definitions for it in 1.x code, and in modern 4.x code. My guess is that it's there for compatiblity reasons. This syntax is not specified by POSIX(r).
==== Return status ====
The return status is the one of the last command executed in ''<LIST>'' or ''0'' (''TRUE''), if the item list ''<WORDS>'' evaluates to nothing (i.e.: "is empty"!).
===== Examples =====
==== Iterate over array elements ====
With some array syntax (see [[syntax:arrays]]) you can easily "feed" the for-loop to iterate over all elements in an array (by mass-expanding all elements):
<code bash>
for element in "${myarray[@]}"; do
echo "Element: $element"
done
</code>
Another way is to mass-expand all used indexes and access the array by index:
<code bash>
for index in "${!myarray[@]}"; do
echo "Element[$index]: ${myarray[$index]}"
done
</code>
==== List positional parameters ====
You can use this [[syntax:basicgrammar#shell_function_definitions | function]] to test how arguments to a command will be interpreted and parsed, and finally used:
<code bash>
argtest() {
n=1
for arg; do
echo "Argument $((n++)): \"$arg\""
done
}
</code>
==== Loop through a directory ====
Since pathname expansion will expand all filenames to separate words, regardless of spaces, you can use the for-loop to iterate through filenames in a directory:
<code bash>
for fn in *; do
if [ -h "$fn" ]; then
echo -n "Symlink: "
elif [ -d "$fn" ]; then
echo -n "Dir: "
elif [ -f "$fn" ]; then
echo -n "File: "
else
echo -n "Unknown: "
fi
echo "$fn"
done
</code>
Stupid example, I know ;-)
==== Loop over lines of output ====
To be complete: You can change the internal field separator (IFS) to a newline and thus make a for-loop iterating over lines instead of words:
<code bash>
IFS=$'\n'
for f in $(ls); do
echo $f
done
</code>
This is just an example. In //general//
* it's not a good idea to parse ''ls(1)'' output
* the [[syntax:ccmd:while_loop|while loop]] (using the ''read'' command) is a better joice to iterate over lines
==== Nested for-loops ====
It's of course possible to use another for-loop as ''<LIST>''. Here, counting from 0 to 99 in a weird way:
<code bash>
for x in 0 1 2 3 4 5 6 7 8 9; do
for y in 0 1 2 3 4 5 6 7 8 9; do
echo $x$y
done
done
</code>
==== Loop over a number range ====
Beginning in Bash 4, you can also use "sequence expression" form of [[syntax:expansion:brace|brace expansion]] syntax when looping over numbers, and this form does not create leading zeroes unless you ask for them:
<code bash>
# 100 numbers, no leading zeroes
for x in {0..99}; do
echo $x
done
</code>
<code bash>
# Every other number, width 3
for x in {000..99..2}; do
echo $x
done
</code>
WARNING: the entire list is created before looping starts. If your list is huge this may be an issue, but no more so than for a glob that expands to a huge list.
===== Portability considerations =====
===== See also =====
* [[syntax:ccmd:c_for]]