syntax:ccmd:case

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
Last revision Both sides next revision
syntax:ccmd:case [2012/02/16 03:25]
felipe1982 [Examples] ;; is suggested for every case statement
syntax:ccmd:case [2013/04/14 12:33]
thebonsai Don't tread version 4 special - it has been around for a long time now
Line 15: Line 15:
 The ''​case''​-statement can execute commands based on a [[syntax:​pattern | pattern matching]] decision. The word ''<​WORD>''​ is matched against every pattern ''<​PATTERNn>''​ and on a match, the associated [[syntax:​basicgrammar#​lists | list]] ''<​LISTn>''​ is executed. Every commandlist is terminated by ''<​nowiki>;;</​nowiki>'',​ this rule is optional for the very last commandlist (i.e. you can omit the ''<​nowiki>;;</​nowiki>''​ before the ''​esac''​). The ''​case''​-statement can execute commands based on a [[syntax:​pattern | pattern matching]] decision. The word ''<​WORD>''​ is matched against every pattern ''<​PATTERNn>''​ and on a match, the associated [[syntax:​basicgrammar#​lists | list]] ''<​LISTn>''​ is executed. Every commandlist is terminated by ''<​nowiki>;;</​nowiki>'',​ this rule is optional for the very last commandlist (i.e. you can omit the ''<​nowiki>;;</​nowiki>''​ before the ''​esac''​).
  
-:V4: Bash 4 introduces new action terminators. The classic ​behaviour ​using '';;''​ is to execute the matching ​block and then terminate. The '';&''​ terminator causes ''​case''​ to also execute the next block, the '';;&''​ operator ​makes it checking ​the pattern of the next block for a match. Using these terminators,​ a ''​case''​ statement can be configured to "run through" ​all matches, for example.+Bash 4 introduces ​two new action terminators. The classic ​behavior ​using '';;''​ is to execute ​only the list associated with the first matching ​pattern, ​then break out of the ''​case''​ block. The '';&''​ terminator causes ''​case''​ to also execute the next block without testing its pattern. The '';;&''​ operator ​is like '';;'',​ except ​the case statement doesn'​t terminate after executing the associated list - Bash just continues testing ​the next pattern as though the previous pattern didn'​t ​match. Using these terminators,​ a ''​case''​ statement can be configured to test against ​all patterns, or to share code between blocks, for example.
  
-The word ''<​WORD>''​ is expanded using //tilde//, //​parameter//​ and //variable expansion////​arithmetic//,​ //command// and //process substitution//​ and //quote removal//**no word splitting is done**, which meansyou can leave expansions unquoted without problems, like:+The word ''<​WORD>''​ is expanded using //tilde//, //​parameter//​ and //variable expansion////​arithmetic//,​ //command// and //process substitution//​and //quote removal//**No word splitting, brace, or pathname expansion ​is done**, which means you can leave expansions unquoted without problems:
 <​code>​ <​code>​
 var="​test word" var="​test word"
Line 25: Line 25:
 esac esac
 </​code>​ </​code>​
-This is similar to the behaviour ​of the [[syntax:​ccmd:​conditional_expression | conditional expression command ("new test command"​)]] (also no word splitting for expansions).+This is similar to the behavior ​of the [[syntax:​ccmd:​conditional_expression | conditional expression command ("new test command"​)]] (also no word splitting for expansions).
  
-Unlike the C-case-statement,​ only the matching list and nothing else is executed. If more patterns match the word, only the first match is taken. (**Note** the comment about :V4: Bash v4 changes above)+Unlike the C-case-statement,​ only the matching list and nothing else is executed. If more patterns match the word, only the first match is taken. (**Note** the comment about Bash v4 changes above.)
  
-More patterns ​to match for one list to execute ​are separated by ''​|'' ​(pipe symbol).+Multiple ''​|''​-delimited ​patterns ​can be specified ​for a single block. This is a POSIX-compatable equivalent to the ''​@(pattern-list)''​ extglob construct. 
 + 
 +The ''​case''​ statement is one of the most difficult commands ​to indent clearly, and people frequently ask about the most "​correct"​ style. Just do your best - there are many variations of indenting style for ''​case'' ​and no real agreed-upon best practice.
  
 ===== Examples ===== ===== Examples =====
 Another one of my stupid examples... Another one of my stupid examples...
 <​code>​ <​code>​
-read -p "Which fruit do you like most? " ​fruit+printf '%s ' 'Which fruit do you like most?
 +read -${BASH_VERSION+e}r ​fruit
  
-case "$fruit" ​in +case $fruit in 
-  apple) ​             echo "Mmmmh... I like those!" +    apple) 
-                      ;; +        ​echo 'Mmmmh... I like those!' 
-  banana) ​            ​echo "Hm, a bit awry, no?" +        ;; 
-                      ;; +    banana) 
-  orange|tangerine) ​  ​echo "Eeeks! I don't like those!+        ​echo 'Hm, a bit awry, no?' 
-                      echo "​Go ​away!" +        ;; 
-                      exit 1 +    orange|tangerine) 
-                      ;; +        ​echo $'Eeeks! I don\'t like those!\nGo away!' 
-  *)                  echo "​Unknown fruit - sure it isn't toxic?"​ +        exit 1 
-                      ;;+        ;; 
 +    *) 
 +        ​echo "​Unknown fruit - sure it isn't toxic?"​
 esac esac
 </​code>​ </​code>​
  
-===== Portability considerations =====+Here's a practical example showing a common pattern involving a ''​case''​ statement. If the first argument is one of a valid set of alternatives,​ then perform some sysfs operations under Linux to control a video card's power profile. Otherwise, show a usage synopsis, and print the current power profile and GPU temperature. 
 +<code bash> 
 +# Set radeon power management 
 +function clk { 
 + typeset base=/​sys/​class/​drm/​card0/​device 
 + [[ -r ${base}/​hwmon/​hwmon0/​temp1_input && -r ${base}/​power_profile ]] || return 1
  
 + case $1 in
 + low|high|default)
 + printf '​%s\n'​ "temp: $(<​${base}/​hwmon/​hwmon0/​temp1_input)C"​ "old profile: $(<​${base}/​power_profile)"​
 + echo "​$1"​ >​${base}/​power_profile
 + echo "new profile: $(<​${base}/​power_profile)"​
 + ;;
 + *)
 + echo "​Usage:​ $FUNCNAME [ low | high | default ]"
 + printf '​%s\n'​ "temp: $(<​${base}/​hwmon/​hwmon0/​temp1_input)C"​ "​current profile: $(<​${base}/​power_profile)"​
 + esac
 +}
 +</​code>​
  
-===== See also =====+A template for experiments with ''​case''​ logic, showing shared code between blocks using '';&'',​ and the non-short-circuiting '';;&''​ operator: 
 +<code bash> 
 +#​!/​usr/​bin/​env bash
  
 +f() {
 +    local -a "​$@"​
 +    local x
 +
 +    for x; do
 +        case $x in
 +            $1)
 +                local "​$x"'​+=(1)'​ ;;&
 +            $2)
 +                local "​$x"'​+=(2)'​ ;&
 +            $3)
 +                local "​$x"'​+=(3)'​ ;;
 +            $1|$2)
 +                local "​$x"'​+=(4)'​
 +        esac
 +        IFS=, local -a "​$x"'​=("​${x}:​ ${'"​$x"'​[*]}"​)'​
 +    done
 +
 +    for x; do
 +        echo "​${!x}"​
 +    done
 +}
 +
 +f a b c
 +
 +# output:
 +# a: 1,4
 +# b: 2,3
 +# c: 3
 +</​code>​
 +
 +===== Portability considerations =====
 +
 +  * Only the '';;''​ delimiter is specified by POSIX.
 +  * zsh and mksh use the '';​|''​ control operator instead of Bash's '';;&''​. Mksh has '';;&''​ for Bash compatability (undocumented).
 +  * ksh93 has the '';&''​ operator, but no '';;&''​ or equivalent.
 +  * ksh93, mksh, zsh, and posh support a historical syntax where open and close braces may be used in place of ''​in''​ and ''​esac'':​ ''​case word { x) ...; };''​. This is similar to the alternate form Bash supports for its [[syntax/​ccmd/​classic_for | for loops]], but Bash doesn'​t support this syntax for ''​case..esac''​.
 +===== See also =====
 +  * [[http://​pubs.opengroup.org/​onlinepubs/​9699919799/​utilities/​V3_chap02.html#​tag_18_09_04_05|POSIX case conditional construct]]
  
  • syntax/ccmd/case.txt
  • Last modified: 2017/02/08 15:04
  • by fgrose