Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
syntax:ccmd:c_for [2011/10/29 02:21]
ormaaj Add a more complicated example. Add portability note.
syntax:ccmd:c_for [2013/07/15 03:56] (current)
ormaaj [Bugs]
Line 17: Line 17:
 <​code>​ <​code>​
 # alternative,​ historical and undocumented syntax # alternative,​ historical and undocumented syntax
- 
 for (( <​EXPR1>​ ; <​EXPR2>​ ; <​EXPR3>​ )) { for (( <​EXPR1>​ ; <​EXPR2>​ ; <​EXPR3>​ )) {
   <​LIST>​   <​LIST>​
Line 24: Line 23:
  
 ===== Description ===== ===== Description =====
-The C-style for-loop is, as the name says, derived from the C-keyword "​for",​ but in Bash, it only allows **[[syntax:​arith_expr | arithmetic expressions]]** as header contents, not arbitrary commands. 
  
-**It operates as follows:​** ​The [[syntax:​arith_expr ​arithmetic expression]] ''<​EXPR1>''​ is evaluatedthen ''<​EXPR2>'' ​is checked, if it's true, then the loop body is executedAfter the first iteration, the ''<​EXPR3>'' ​is evaluated''<​EXPR2>'' ​is checked again, if it's true, the loop body is executed, etc...+The C-style for-loop is a [[syntax/​basicgrammar#​compound_commands ​compound command]] derived from the equivalent ksh88 featurewhich is in turn derived from the C "​for"​ keywordIts purpose ​is to provide a convenient way to evaluate arithmetic expressions in a loopplus initialize any required arithmetic variables. It is one of the main "loop with a counter"​ mechanisms available in the language
  
-  * ''<​EXPR1>''​ is to **initialize** before ​the first run +The ''<​nowiki>​((;;​))</​nowiki>'' ​syntax at the top of the loop is not an ordinary [[syntax/​ccmd/​arithmetic_eval | arithmetic compound command]], but is part of the C-style for-loop'​s own syntax. The three sections separated by semicolons are [[syntax:​arith_expr | arithmetic expression]] contexts. Each time one of the sections ​is to be evaluated, ​the section is first processed for: brace, parameter, command, arithmetic, and process substitution/​expansion as usual for arithmetic contexts. When the loop is entered for the first time, ''<​EXPR1>''​ is evaluated, then ''<​EXPR2>''​ is evaluated and checked. If ''<​EXPR2>''​ is true, then the loop body is executed. After the first and all subsequent iterations, ''<​EXPR1>''​ is skipped, ​''<​EXPR3>''​ is evaluated, then ''<​EXPR2>''​ is evaluated and checked again. This process continues until ''<​EXPR2>''​ is false.
-  * ''<​EXPR2>''​ is to **check** if the loop should be ran +
-  * ''<​EXPR3>''​ is to **change** conditions after every loop run+
  
-It's very simpleAnother equivalent ​loop using while-construct ​with [[syntax:​ccmd:​arithmetic_eval | arithmetic ​expressions ​compound command]] would be:+  * ''<​EXPR1>''​ is to **initialize variables** before the first run. 
 +  * ''<​EXPR2>''​ is to **check** for a termination condition. This is always the last section to evaluate prior to leaving the loop
 +  * ''<​EXPR3>''​ is to **change** conditions after every iteration. For example, incrementing ​counter. 
 + 
 +:!: If one of these arithmetic expressions in the for-loop is empty, it behaves as if it would be 1 (**TRUE** in arithmetic context). 
 + 
 +:!: Like all loops (Both types of ''​for''​-loop,​ ''​while''​ and ''​until''​),​ this loop can be: 
 +  * Terminated (broken) by the [[commands/​builtin/​continuebreak | break]] builtin, optionally as ''​break N''​ to break out of ''​N''​ levels of nested loops. 
 +  * Forced immediately to the next iteration using the [[commands/​builtin/​continuebreak | continue]] builtin, optionally as the ''​continue N''​ analog to ''​break N''​. 
 + 
 +The equivalent ​construct ​using a [[syntax/​ccmd/​while_loop | while loop]] and the [[syntax:​ccmd:​arithmetic_eval | arithmetic ​expression ​compound command]] would be structured as:
 <​code>​ <​code>​
 (( <​EXPR1>​ )) (( <​EXPR1>​ ))
Line 40: Line 46:
 done done
 </​code>​ </​code>​
 +The equivalent ''​while''​ construct isn't exactly the same, because both, the ''​for''​ and the ''​while''​ loop behave differently in case you use the [[commands/​builtin/​continuebreak | continue]] command.
  
-If one of these arithmetic expressions in the for-loop is empty, it behaves as if it would be 1 (**TRUE** in arithmetic context). 
  
-:!: Like all loops (both ''​for''​-loops''​while'' ​and ''​until''​), this loop can be +==== Alternate syntax ==== 
-  * 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, Ksh93, Mksh, and Zsh also provide an alternate syntax for the ''​for''​ loop - enclosing ​the loop body in ''​{<​nowiki>​...</​nowiki>​}'' ​instead of ''​do ​<​nowiki>​...</​nowiki>​ done''​:
  
-Bash knows an alternative syntax for the ''​for''​ loop, enclosing the loop body in ''​{<​nowiki>​...</​nowiki>​}''​ instead of ''​do <​nowiki>​...</​nowiki>​ done'':​ 
 <​code>​ <​code>​
 for ((x=1; x<=3; x++)) for ((x=1; x<=3; x++))
Line 54: Line 59:
 } }
 </​code>​ </​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).+ 
 +This syntax is **not documented** and shouldn'​t ​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 compatibility ​reasons. ​Unlike the other aforementioned shells, Bash does not support the analogous syntax for [[syntax/​ccmd/​case#​portability_considerations | case..esac]].
  
 ==== Return status ==== ==== Return status ====
  
-The return status is the one of the last command executed from ''<​LIST>''​ or ''​FALSE''​ if any of the arithmetic expressions ​fail. +The return status is that of the last command executed from ''<​LIST>''​or ''​FALSE''​ if any of the arithmetic expressions ​failed.
- +
  
 +===== Alternatives and best practice =====
 +<div center round todo 60%>​TODO:​ Show some alternate usages involving functions and local variables for initialization.</​div>​
 ===== Examples ===== ===== Examples =====
 +
 ==== Simple counter ==== ==== Simple counter ====
  
 A simple counter, the loop iterates 101 times ("​0"​ to "​100"​ are 101 numbers -> 101 runs!), and everytime the variable ''​x''​ is set to the current value. A simple counter, the loop iterates 101 times ("​0"​ to "​100"​ are 101 numbers -> 101 runs!), and everytime the variable ''​x''​ is set to the current value.
  
-  * it **initializes** ''​x = 0''​ +  * It **initializes** ''​x = 0''​ 
-  * before ​every iteration it **checks** if ''​x ​<= 100''​ +  * Before ​every iteration it **checks** if ''​x ​≤ 100''​ 
-  * after every iteration it **changes** ''​x++''​+  * After every iteration it **changes** ''​x++''​
  
 <​code>​ <​code>​
Line 78: Line 85:
  
 ==== Stepping counter ==== ==== Stepping counter ====
-This is the very same counter (compare it to the simple counter example above), but the **change** that is made is a ''​x = x + 10''​. That means, it will count from 0 to 100, but with a **step of 10**.+ 
 +This is the very same counter (compare it to the simple counter example above), but the **change** that is made is a ''​x ​+= 10''​. That means, it will count from 0 to 100, but with a **step of 10**.
 <​code>​ <​code>​
-for ((x = 0 ; x <= 100 ; x = x + 10)); do+for ((x = 0 ; x <= 100 ; x +10)); do
   echo "​Counter:​ $x"   echo "​Counter:​ $x"
 done done
Line 87: Line 95:
 ==== Bits analyzer ==== ==== Bits analyzer ====
  
-This is a bit more complex, but really just little ​bit.+This example loops through the bit-values of Byte, beginning from 128, ending at 1. If that bit is set in the ''​testbyte'',​ it prints "''​1''",​ else "''​0''"​ => it prints the binary representation of the ''​testbyte''​ value (8 bits). 
 +<​code>​ 
 +#​!/​usr/​bin/​env bash 
 +# Example written for http://​wiki.bash-hackers.org/​syntax/​ccmd/​c_for#​bits_analyzer 
 +# Based on TheBonsai'​s original. 
 + 
 +function toBin { 
 + typeset m=$1 n=2 x='​x[(n*=2)>​m]'​ 
 + for ((x = x; n /= 2;)); do 
 + printf %d $(( m & n && 1)) 
 + done 
 +
 + 
 +function main { 
 + [[ $1 == +([0-9]) ]] || return 
 + typeset result 
 + if (( $(ksh -c '​printf %..2d $1' _ "​$1"​) == ( result = $(toBin "​$1"​) ) )); then 
 + printf '%s is %s in base 2!\n' "​$1"​ "​$result"​ 
 + else 
 + echo 'Oops, something went wrong with our calculation.' >&​2 
 + exit 1 
 + fi 
 +
 + 
 +main "​${1:​-123}"​ 
 + 
 +# vim: set fenc=utf-8 ff=unix ft=sh : 
 +</​code>​
  
-It loops through the bit-values of a Byte, beginning from 128, ending at 1. If that bit is set in the ''​testbyte'',​ it prints "''​1''",​ else "''​0''"​ =it prints the binary representation of the ''​testbyte''​ value (8 bits).+<div hide
 <​code>​ <​code>​
 testbyte=123 testbyte=123
-for (( n = 128 ; n >= 1 ; n = n / 2 )); do+for (( n = 128 ; n >= 1 ; n /2 )); do
   if (( testbyte & n )); then   if (( testbyte & n )); then
-    ​echo -n "1"+    ​printf %d 1
   else   else
-    ​echo -n "0"+    ​printf %s 0
   fi   fi
 done done
-echo # final linefeed+echo
 </​code>​ </​code>​
 +</​div>​
 +
 Why that one begins at 128 (highest value, on the left) and not 1 (lowest value, on the right)? It's easier to print from left to right... Why that one begins at 128 (highest value, on the left) and not 1 (lowest value, on the right)? It's easier to print from left to right...
 +
 +We arrive at 128 for ''​n''​ through the recursive arithmetic expression stored in ''​x'',​ which calculates the next-greatest power of 2 after ''​m''​. To show that it works, we use ksh93 to double-check the answer, because it has a built-in feature for ''​printf''​ to print a representation of any number in an arbitrary base (up to 64). Very few languages have that ability built-in, even things like Python.
  
 ==== Up, down, up, down... ==== ==== Up, down, up, down... ====
  
-And finally, a really complicated example. ​This one counts up and down from 0 to 9, 10 times. Remember, each part of the C-style for loop can contain arbitrary arithmetic. Several arithmetic expression concepts are illustrated here such as C-style multiple-assignment (also used in languages like Python), use of multiple variables, the comma operator for delimiting side-effectful expressions,​ boolean short-circuiting,​ parens for grouping, and a few of the increment operators. Here, ''​i'' ​is used to keep track of the count, ​''​j''​ is the increment, which can be either ​or -1, and ''​k'' ​is the number of 0-9 counts completed. +This counts up and down from ''​0''​ to ''​${1:-5}''​''​${2:-4}''​ timesdemonstrating more complicated arithmetic expressions with multiple variables.
-<​code>​for (( i = j = k = 1; i % 9 || (j *= -1k++ <= 10); i += j )); do printf "​$i";​ done +
-12345678987654321012345678987654321012345678987654321012345678987654321012345678987654321012345678</​code>​+
  
-The resultand therefore also truth value of a comma-delimited arithmetic expression list is that of the last expression of the list. In the middle expression of this for loopif ''​i ​9''​ is nonzeroit'​s ​the last expression performed and thus the entire expression is true. If it's zero, the short-circuit isn't tripped, and thus the truth of the expression is determined by the last expression of the following list: ''<nowiki>k++ <= 10</nowiki>''​. The parentheses are required because commas have the lowest precedence.+<​code>​ 
 +for (( incr = 1n=0, times = ${2:-4}step = ${1:-5}; (n += incr) step || (incr *= -1--times);​));​ do 
 +    printf ​'%*s\n' ​"​$((n+1))"​ "​$n"​ 
 +done 
 +</code> 
 +<code> ~ $ bash <(xclip -o) 
 + 1 
 +  2 
 +   3 
 +    4 
 +     5 
 +    4 
 +   3 
 +  2 
 + 1 
 +
 + 1 
 +  2 
 +   3 
 +    4 
 +     5 
 +    4 
 +   3 
 +  2 
 + 1 
 +</code>
  
 ===== Portability considerations ===== ===== Portability considerations =====
  
-C-style for loops aren't POSIX. They are available in Bash, ksh93, and zsh. All 3 have essentially the same syntax and behavior.+  * C-style for loops aren't POSIX. They are available in Bash, ksh93, and zsh. All 3 have essentially the same syntax and behavior. 
 +  * C-style for loops aren't available in mksh. 
 + 
 +===== Bugs ===== 
 + 
 +  * //Fixed in 4.3//. <​del>​There appears to be a bug as of Bash 4.2p10 in which command lists can't be distinguished from the for loop's arithmetic argument delimiter (both semicolons),​ so command substitutions within the C-style for loop expression can't contain more than one command.</​del>​
  
 ===== See also ===== ===== See also =====
  • syntax/ccmd/c_for.1319854908.txt
  • Last modified: 2011/10/29 02:21
  • by ormaaj