什么是bash/zsh脚本中"case"语句奇怪语法的原因?

58

从程序员的角度来看,Shell脚本只是另一种编程语言,需要学习并遵守该语言的规则。然而,我必须承认,这种语法是我见过的在一个相当常用的语言中最奇怪的风格。Shell脚本是从哪个旧语言继承了这个语法呢?这个语法有特殊的含义或意义吗?

作为例子,这里是从另一个问题中摘取的一个小片段。

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        stop
        start
        ;;
    status)
        check_status
        ;;
    *)
        echo "Usage: $0 {start|stop|restart|status}"
        exit 1
        ;;
esac

看到这个代码,首先我注意到caseesac结尾,这是它的反向形式(就像iffi结尾一样)。其次,我明白每个case都后跟一个)。很好,但我为什么需要在每个语句结尾处加上两个;?我还想说,没有伴随()很丑陋。

我正在寻找有关该语言历史方面的更多信息,但我也愿意接受技术方面的原因。

4个回答

47

根据请求:

  • 那么你能猜到为什么循环是 'for ...; do ...; done' 而不是 'for ...; do ...; od' 吗?这背后有一个合理的原因 - 但是Algol样式的反向关键字用于其他地方。

回答:

  • 语法来自Bourne(Bourne shell的创始人)。他曾在Algol上工作过,并喜欢它,以至于模仿了Algol的一些shell语法。Algol使用反向关键字标记构造的结束,因此'case ... esac'是适当的。循环不以'od'结尾的原因是Unix中已经有一个命令'od'-八进制转储。因此,使用'done'代替。

根据声誉,Bourne shell源代码是用特殊的C语言和宏编写的,使其看起来像Algol。这使得维护变得困难。

关于主要问题-关于为什么case语句中的备选项没有开放括号(圆括号)-我有几个相关的理论。

首先,在Bourne shell编写时(1970年代末),大部分编辑都是使用'ed'标准文本编辑器完成的。它没有跳过平衡括号或其他类似符号的概念,因此没有前导括号的要求。此外,如果您正在编写文档,则可能会使用以下方式整理您的参数:

a) ...blah...
b) ...more...
c) ...again...

左括号经常被省略 - case语句可以很好地适应这种模式。

当然,自那以后,我们已经习惯了编辑器在键入右括号时标记匹配的左括号,因此旧的Bourne shell符号是一个麻烦。 POSIX标准使前导左括号变为可选项; 大多数类似于POSIX的现代shell实现(Korn,Bash,Zsh)都将支持该选项,并且在不必担心可移植性到诸如Solaris 10之类的机器时,我通常使用它。 (我通常通过使用#!/bin/ksh作为shebang来处理这个问题。)


2
我认为“主要问题”是语法的起源,即Algol(在bash的维基百科页面上找不到)。无论如何,回答很好 :) - phunehehe
2
可以在Sourceforge的Heirloom Bourne Shell项目(以及其他可通过您喜欢的搜索引擎找到的地方)找到有关“原始”Bourne shell的信息。 - Jonathan Leffler

34

使用 ;; 的原因是单个 ; 可以用于在一行中编写多条语句,例如:

restart)
   stop; start;;
...

13

Bash 可接受匹配的括号:

case "$1" in
    (start)
        start
        ;;
    (stop)
        stop
        ;;

    etc.

1
POSIX和Korn shell也允许前导括号。很久以前,ed(“标准文本编辑器”-在第7版UNIX手册中是这样说的)没有“将光标弹回匹配的开括号”选项,因此开括号并不是必需的。 - Jonathan Leffler
@Dennis 很高兴知道,但那是在不匹配风格流行之前还是之后? - phunehehe
@phunehehe:除非您想知道是否曾经有一个不接受开括号的case/esac的shell,否则我不知道您的意思。对此的答案是“是的”。原始的Bourne shell就是一个例子。 - Dennis Williamson
不,我的意思是,bash从一开始就接受匹配的括号吗?还是后来有人抱怨难看才加入了这个功能? - phunehehe
@phunehehe:这个文档在2.0(1996年)和2.05b(2002年)之间的某个时候发生了变化。我不知道它在文档编写之前是否被接受。CHANGES 上似乎对此没有提及。 - Dennis Williamson
显示剩余3条评论

3

右括号有时会在自然语言中的列表中使用,比如:

1) do this
2) do that

逆转关键词来源于Algol某种形式,但实际上对于交互使用是一个非常好的想法。它们清晰地标示了构造的结尾,包括if/else。
例如,在类似C语法的情况下,在解析完以下内容后:
if (condition)
    command here;

是否有一个else语句要来或不要来?Plan 9中的shell rc提供了if not而不是else,但这不太美观。

使用Bourne shell语法,您可以使用elsefi,无需阅读其他输入。


我理解你关于列表的观点,但是else部分并不太有意义。无论如何,必须读取额外的输入来确定它是一个“else”还是“fi”。 - phunehehe
但是这个额外的输入是if部分的一部分。使用类C语法的else语句,如果没有else,shell最终会读取下一个命令,这在交互式 shell 中是相当意外的。 - jilles

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接