Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision Next 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/02/01 13:27] ormaaj [Examples] A practical example to show off my video power function. |
||
---|---|---|---|
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. | + | :V4: 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 means, you 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 :V4: 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]] | ||