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 [2012/06/10 10:47]
ormaaj [Up, down, up, down...] Clearer example and shorter explanation, since arithmetic doesn't need to be re-explained here.
syntax:ccmd:c_for [2013/07/15 05: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 ((However, as with all arithmetic, it is possible to have command substitutions contained within. Note that command substitutions are processed before arithmetic, so any control flow such as the ternary operator, or short circuiting, have no effect on the command substitution'​s evaluation. Rather, the expression resulting from the command substitution'​s output plus surrounding text ends up processed as arithmetic. See also the above bug.)) 
  
-**It operates as follows:​** ​The [[syntax:​arith_expr | arithmetic expression]] ''<​EXPR1>''​ is evaluated, then ''<​EXPR2>''​ is checked, if it'true, then the loop body is executed. After the first iterationthe ''<​EXPR3>''​ is evaluated, ''<​EXPR2>''​ is checked again, if it's true, the loop body is executedetc...+The C-style for-loop is a [[syntax/​basicgrammar#​compound_commands | compound command]] derived from the equivalent ksh88 feature, which is in turn derived from the C "​for"​ keyword. Its purpose is to provide a convenient way to evaluate arithmetic expressions in a loop, plus initialize any required arithmetic variables. ​It is one of the main "loop with a counter"​ mechanisms available in the language.  
 + 
 +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. 
 + 
 +  * ''<​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 exampleincrementing a 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).
  
-  * ''​<​EXPR1>​'' ​is to **initialize** before the first run +:!: Like all loops (Both types of ''​for''​-loop, ''​while''​ and ''​until''​),​ this loop can be: 
-  * ''​<​EXPR2>​'' ​is to **check** if the loop should be ran +  * Terminated (broken) by the [[commands/​builtin/​continuebreak | break]] builtin, optionally as ''​break N''​ to break out of ''​N''​ levels of nested loops. 
-  * ''​<​EXPR3>​'' ​is to **change** conditions after every loop run+  * Forced immediately to the next iteration using the [[commands/​builtin/​continuebreak | continue]] builtin, optionally as the ''​continue N'' ​analog ​to ''​break N''​.
  
-It's very simple. Another ​equivalent ​loop using a while-construct with [[syntax:​ccmd:​arithmetic_eval | arithmetic ​expressions ​compound command]] would be:+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... ====
Line 108: Line 147:
  
 <​code>​ <​code>​
-for (( incr=1, times=${2:​-4}; (n+=incr) % ${1:-5} || (incr*=-1, --times);)) do+for (( incr = 1, n=0, times = ${2:-4}, step = ${1:-5}; (n += incr) % step || (incr *= -1, --times);))do
     printf '​%*s\n'​ "​$((n+1))"​ "​$n"​     printf '​%*s\n'​ "​$((n+1))"​ "​$n"​
 done done
 </​code>​ </​code>​
-<​code>​ +<​code>​ ~ $ bash <(xclip -o)
- ~ $ bash <(xclip -o)+
  1  1
   2   2
Line 134: Line 172:
  1  1
 </​code>​ </​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 ===== ===== Bugs =====
  
-* There appears to be a bug as of Bash 4.2p10 in which command lists cannot ​be distinguished from the for loop's arithmetic argument delimiter (both semicolons),​ so command substitutions within the C-style for loop expression ​cannot ​contain more than one command. ​This appears specific to Bash. ksh93 and zsh behave correctly. It's also pretty obscure and useless - you probably shouldn'​t be doing that anyway.+  ​//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>​
  
-//EDIT//: Chet says it'll be fixed in the next version. :-) 
 ===== See also ===== ===== See also =====