Bash Shell多路分支

Bash Shell多路分支,你需要进行一系列比较,但使用 if/then/else 显得又长又啰唆。有没有更简单的方法?

解决方案

可以用 case 语句实现多路分支。

case $FN in
    *.gif) gif2png $FN
        ;;
    *.png) pngOK $FN
        ;;
    *.jpg) jpg2gif $FN
        ;;
    *.tif | *.TIFF) tif2jpg $FN
        ;;
    *) printf "File not supported: %s" $FN
        ;;
esac

与其等价的 if/then/else 形式如下所示。

if [[ $FN == *.gif ]]
then
    gif2png $FN
elif [[ $FN == *.png ]]
then
    pngOK $FN
elif [[ $FN == *.jpg ]]
then
    jpg2gif $FN
elif [[ $FN == *.tif || $FN == *.TIFF ]]
then
    tif2jpg $FN
else
    printf "File not supported: %s" $FN
fi

讨论

case 语句会扩展 case 与 in 之间的单词(包括参数替换)。然后依次尝试匹配各个模式。这可是 shell 了不得的一项特性。它不是简单地比对值,而是进行字符串模式匹配(尽管并非正则表达式)。示例中用到的模式并不复杂:*.gif 匹配以普通字符 .gif 结尾的任意字符串(由 * 表示)。

这里所谓的“普通字符”(literal character)是指仅代表字面含义的字符,与之对应的是“元字符”(metacharacter),这类字符具有某种特殊含义。

代表逻辑 OR 的 | 用于分隔不同模式,无论匹配到其中哪个模式,都会执行相同的处理。在我们给出的例子中,如果 $FN.tif.TIFF 结尾,则匹配模式并执行(假想的) tif2jpg 命令。

case 中并没有 else 或 default 关键词来指示未匹配到任何模式时该执行哪些语句。它使用 * 作为最后的模式,因为该模式什么都能匹配。将其放在最后以作为默认分支,匹配之前尚未被匹配到的内容。

双分号(;;)用于结束与某个模式关联的一组语句。从 bash 4 开始,还可以用另外两种写法结束一组语句。;;& 表示,即便找到匹配,依然尝试匹配下一个模式,如果发现该模式也匹配,则执行与之关联的语句。;& 表示执行流程会“直落而下”,不管模式是否匹配,都执行下一组语句。下面这个多少有些无聊的例子演示了上述特性的用法。

# 使用其他结束标记

case $FN in
    *.gif) gif2png $FN
            ;;&         # 继续向下查找匹配
    *.png) pngOK $FN
            ;;&         # 继续向下查找匹配
    *.jpg) jpg2gif $FN
            ;;&         # 继续向下查找匹配
    *.tif) tif2jpg $FN
            ;&          # 直落而下
    *.* ) echo "two.words"
            ;;
    * ) echo "oneword"
esac

只要 $FN 匹配前 4 个模式中的任意一个,bash 都会执行(假想的)转换命令,并继续向下查找匹配。它会发现第 5 个模式也能够匹配,因此还会显示短语 two.words。

同 C/C++ 和 Java 程序员谈一个题外话:bash 中的 case 类似于 switch 语句,每个模式对应一处分支。但要注意,switch/case 中使用的是 shell 变量(通常包含字符串值),分支中使用的是模式(不仅仅是常量值)。模式以右括号(可不是冒号)作结。C/C++ 和 Java 的 switch 语句中的 break 等同于 bash 中的双分号,default 关键词等同于 bash 中的 * 模式。

匹配过程区分大小写,但可以用 shopt -s nocasematch(bash 3.1 以上版本可用)改变此行为。该选项会影响到 case 和 [[

我们用 esac(反过来拼写“c-a-s-e”,这种写法源于 Algol 68)来结束 case 语句。

酷客网相关文章:

赞(0)

评论 抢沙发

评论前必须登录!