本章节继续介绍流程控制语句,在前面的while语句,read语句生成了一些简单的菜单并构建了用户选择处理逻辑。使用了了一系列的if命令来识别可能的菜单选项。这种逻辑经常会出现在程序中,Shell提供了们处理多重选择的流程控制机制。
在Bash中,多重选择复合命令是case,用法如下:
case word in
[parrern [| pattern]...) commands ;;]...
esac
使用case简化用户选择处理逻辑:
[sysadmin@ansible bin]$ cat case-menu
#!/bin/bash
#case-menu
clear
echo "
Please Select:
1.Display System Information
2.Display Disk Space
3.Display Home Space Utilization
4.Quit
"
read -p "Enter selection [0-3] > "
case "$REPLY" in
0) echo "Program terminated."
exit
;;
1) echo "Hostname: $HOSTNAME"
uptime
;;
2) df -h
;;
3) if [[ "$(id -u)" -eq 0 ]]; then
echo "Home Space Utilization (ALL Users)"
du -sh /home/*
else
echo "Home Space Utilizaion ($USER)"
du -sh "$HOME"
fi
;;
*) echo "Invalid entry." >&2
exit 1
;;
esac
case命令查看word的值,本例中就是REPLY的值,然后将其与patterns指定的模式匹配。如果找到匹配,则执行与该模式关联的命令,之后不再尝试进行其他匹配。
case使用的模式与路径名扩展使用的模式一样,模式以)结尾。下表描述了case模式示例:
模式 | 描述 |
---|---|
a) | 如果word是a,则匹配 |
[[:alpha:]] | 如果word是单个字母,则匹配 |
???) | 如果word是3个字符,则匹配 |
*.txt) | 如果word是以.txt结尾,则匹配 |
*) | 不管word是什么内容,均可匹配。将该模式作为case命令最后一个模式是一种不错的做法,可以匹配之前的模式无法匹配到的内容,也就是说,能捕获到所有的“漏网之鱼” |
[sysadmin@ansible bin]$ cat case-sample
#!/bin/bash
# case-sample
read -p "enter word > "
case "$REPLY" in
[[:alpha:]]) echo ""$REPLY" is a single alphabetic character." ;;
[ABC][0-9]) echo ""$REPLY" is A, B, or C followed by a digit." ;;
???) echo ""$REPLY" is three characters long." ;;
*.txt) echo ""$REPLY" is a word ending in '*.txt'" ;;
*) echo ""$REPLY" is something else." ;;
esac
也可以使用|作为分隔符,将多个模式组合在一起,形成“逻辑或”(or)关系的条件模式,这在同时处理大小写字母的的时候很有用。
[sysadmin@ansible bin]$ cat case-menu
#!/bin/bash
#case-menu
clear
echo "
Please Select:
B.Display System Information
C.Display Disk Space
D.Display Home Space Utilization
Q.Quit
"
read -p "Enter selection [0-3] > "
case "$REPLY" in
q|Q) echo "Program terminated."
exit
;;
b|B) echo "Hostname: $HOSTNAME"
uptime
;;
c|C) df -h
;;
d|D) if [[ "$(id -u)" -eq 0 ]]; then
echo "Home Space Utilization (ALL Users)"
du -sh /home/*
else
echo "Home Space Utilizaion ($USER)"
du -sh "$HOME"
fi
;;
*) echo "Invalid entry." >&2
exit 1
;;
esac
Bash之前,case只允许在成功的匹配分支上执行一次操作,操作结束后,case命令随之终止。来看一个字符匹配脚本:
[sysadmin@ansible bin]$ cat case-1
#!/bin/bash
# case-1:测试一个字符
read -n 1 -p "Type a character > "
echo
case "$REPLY" in
[[:upper:]]) echo "$REPLY is a upper case." ;;
[[:lower:]]) echo "$REPLY is a lower case." ;;
[[:alpha:]]) echo "$REPLY is a alphabetic." ;;
[[:digit:]]) echo "$REPLY is a digit." ;;
[[:graph:]]) echo "$REPLY is a visible character." ;;
[[:punct:]]) echo "$REPLY is a punctuation symbol." ;;
[[:space:]]) echo "$REPLY is a whitespace character." ;;
[[:xdigit:]]) echo "$REPLY is a hexadecimal digit." ;;
esac
在Bash4.0之前,case无法匹配多个分支。现代版本的Bash添加了;;&语法,允许继续测试下一个模式,上一个代码改写为
[sysadmin@ansible bin]$ cat case-2
#!/bin/bash
# case-2:测试多个字符
read -n 1 -p "Type a character > "
echo
case "$REPLY" in
[[:upper:]]) echo "$REPLY is a upper case." ;;&
[[:lower:]]) echo "$REPLY is a lower case." ;;&
[[:alpha:]]) echo "$REPLY is a alphabetic." ;;&
[[:digit:]]) echo "$REPLY is a digit." ;;&
[[:graph:]]) echo "$REPLY is a visible character." ;;&
[[:punct:]]) echo "$REPLY is a punctuation symbol." ;;&
[[:space:]]) echo "$REPLY is a whitespace character." ;;&
[[:xdigit:]]) echo "$REPLY is a hexadecimal digit." ;;&
esac
[sysadmin@ansible bin]$ case-2
Type a character > a
a is a lower case.
a is a alphabetic.
a is a visible character.
a is a hexadecimal digit.
[sysadmin@ansible bin]$
有了;;&,case就可以继续测试模式,而不再直接终止。case是处理某些特定类型问题的绝佳工具。