Bash case 语句功能强大且易于编写。当您重新访问旧的 Linux 脚本时,您会很高兴使用了case语句而不是长if-then-else语句。
案例陈述
大多数编程语言都有自己的 a switchorcase语句版本。它们根据变量的值指导程序执行的流程。通常,为变量的每个预期可能值定义了一个执行分支,并 为所有其他值定义了一个包罗万象或 默认分支。
逻辑功能类似于一长串if-then语句,其中一个else语句捕获之前未由其中一个if语句处理的所有内容。
执行case 尝试匹配的 表达式 与子句之一。它通过依次查看每个子句来尝试找到匹配的模式来做到这一点。子句中的模式是字符串,但是——与直觉相反——这并不意味着我们不能使用数值作为表达式。
一般情况
case语句的一般形式是这样的:
case expression in pattern-1)statement ;;pattern-2) statement;;...pattern-N) statement ;;*) statement ;;
esac
一个case语句必须与开始case的关键字和结束与esac关键字。
表达式被评估并与每个子句中的模式进行比较, 直到找到匹配项。
匹配子句中的一个或多个语句被执行。
双分号“ ;;”用于终止子句。
如果匹配模式并且执行了该子句中的语句,则忽略所有其他模式。
条款的数量没有限制。
星号“ *”表示默认模式。如果表达式与case语句中的任何其他模式不匹配,则执行 default 子句。
一个简单的例子
这个脚本告诉我们一个虚构商店的营业时间。它使用date带有+"%a"格式字符串的命令来获取缩短的日期名称。这存储在DayName变量中。
#!/bin/bashDayName=$(date +"%a")echo "Opening hours for $DayName"case $DayName inMon)echo "09:00 - 17:30";;Tue)echo "09:00 - 17:30";;Wed)echo "09:00 - 12:30";;Thu)echo "09:00 - 17:30";;Fri)echo "09:00 - 16:00";;Sat)echo "09:30 - 16:00";;Sun)echo "Closed all day";;*);;
esac
将该文本复制到编辑器中,并将其另存为名为“open.sh”的文件。
我们需要使用该chmod命令使其可执行。在阅读本文时,您需要对创建的所有脚本执行此操作。
chmod +x open.sh
我们现在可以运行我们的脚本。
./open.sh
截取屏幕截图的那天恰好是星期五。这意味着该DayName 变量包含字符串“Fri”。这与“Fri)”子句的“Fri”模式相匹配。
请注意,子句中的模式不需要用双引号括起来,但如果用双引号括起来也无妨。但是,如果模式包含空格,则必须使用双引号。
默认条款已留空。任何与前述条款之一不匹配的内容都将被忽略。
该脚本有效且易于阅读,但它冗长且重复。我们可以case 很容易地缩短这种类型的 语句。
在一个子句中使用多个模式
case语句的一个非常巧妙的特性是您可以在每个子句中使用多个模式。如果表达式匹配这些模式中的任何一个,则执行该子句中的语句。
这是一个脚本,它告诉您一个月有多少天。只能有三个答案:2 月是 30 天、31 天或 28 天或 29 天。所以,虽然有 12 个月,但我们只需要三个条款。
在此脚本中,系统会提示用户输入月份的名称。为了使模式匹配不区分大小写,我们使用shopt带有-s nocasematch选项的命令。输入是否包含大写、小写或两者的混合都没有关系。
#!/bin/bashshopt -s nocasematchecho "Enter name of a month"
read monthcase $month inFebruary)echo "28/29 days in $month";;April | June | September | November)echo "30 days in $month";;January | March | May | July | August | October | December)echo "31 days in $month";;*)echo "Unknown month: $month";;
esac
二月有一个属于自己的条款,所有其他月份根据它们是否有 30 天或 31 天共享两个条款。多模式子句使用管道符号“|” 作为分隔符。默认情况下捕获拼写错误的月份。
我们将其保存到名为“month.sh”的文件中,并使其可执行。
chmod +x month.sh
我们将多次运行脚本并表明使用大写或小写都没有关系。
./month.sh
因为我们告诉脚本忽略大写和小写的差异,所以任何拼写正确的月份名称都由三个主要子句之一处理。默认条款会捕获拼写错误的月份。
在 case 语句中使用数字
我们也可以使用数字或数值变量作为表达式。此脚本要求用户输入 1…3 范围内的数字。为了清楚表明每个子句中的模式都是字符串,它们被用双引号括起来。尽管如此,脚本仍然将用户的输入与适当的子句匹配。
#!/bin/bashecho "Enter 1, 2, or 3: "
read Numbercase $Number in"1")echo "Clause 1 matched";;"2")echo "Clause 2 matched";;"3")echo "Clause 3 matched";;*)echo "Default clause matched";;
esac
将其保存到名为“number.sh”的文件中,使其可执行,然后运行它:
./number.sh
在 for 循环中使用 case 语句
甲case语句试图图案匹配单个表达式。如果你有很多表达式要处理,你可以把case语句放在一个for循环中。
这个脚本执行的ls命令来获得文件的列表。在for循环中,文件通配(与正则表达式类似但不同)依次应用于每个文件以提取文件扩展名。这存储在Extension字符串变量中。
该case语句使用Extension变量作为它试图与子句匹配的表达式。
#!/bin/bashfor File in $(ls)do# extract the file extensionExtension=${File##*.}case "$Extension" insh)echo " Shell script: $File";;md)echo " Markdown file: $File";;png)echo "PNG image file: $File";;*)echo "Unknown: $File";;esac
done
将此文本保存到名为“filetype.sh”的文件中,使其可执行,然后使用以下命令运行它:
./filetype.sh
我们的极简文件类型识别脚本有效。
使用 case 语句处理退出代码
一个行为良好的程序会在它终止时向 shell 发送一个退出代码。传统方案使用零退出代码值来指示无问题执行,使用一个或多个值来指示不同类型的错误。
许多程序只使用零和一。将所有错误条件集中到一个单一的退出代码中会使识别问题变得更加困难,但这是常见的做法。
我们创建了一个名为“go-geek”的小程序,它会随机返回零或一的退出代码。下一个脚本调用go-geek. 它使用$?shell 变量获取退出代码,并将其用作case语句的表达式。
真实世界的脚本会根据生成退出代码的命令的成功或失败进行适当的处理。
#!/bin/bashgo-geekcase $? in"0")echo "Response was: Success"echo "Do appropriate processing in here";;"1")echo "Response was: Error"echo "Do appropriate error handling in here";;*)echo "Unrecognised response: $?";;
esac
将其保存到名为“return-code.sh”的脚本中并使其可执行。你需要用一些其他的命令来代替我们的go-geek命令。您可以尝试cd进入一个不存在的目录以获得 1 的退出代码,然后将您的脚本编辑cd到一个可访问的目录以获得 0 的退出代码。
多次运行脚本会显示case语句正确识别的不同退出代码。
./return-code.sh
易读性有助于可维护性
回到旧的 Bash 脚本并弄清楚它们如何做它们所做的事情,特别是如果它们是由其他人编写的,是具有挑战性的。修改旧脚本的功能更加困难。
该case语句为您提供了具有清晰和简单语法的分支逻辑。这是双赢。