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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4238341/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-17 22:56:57  来源:igfitidea点击:

What is the reason for the weird syntax of the "case" statement in a bash/zsh script?

bashshellcasezsh

提问by phunehehe

Looking from a programmer's point of view then shell script is just another programming language, where one has to learn and conform to the rules of the language. However, I have to admit that this syntax is the weirdest style I have ever seen in a rather commonly used language. Did the shell take this syntax from an older language that it descents from? Is there a special implication / meaning in the syntax?

从程序员的角度来看,shell 脚本只是另一种编程语言,必须学习并遵守语言规则。但是,我不得不承认,这种语法是我在相当常用的语言中见过的最奇怪的风格。shell 是否从它所继承的旧语言中采用了这种语法?语法中是否有特殊含义/含义?

As an example, here is a little snippet that I take from another poston SO

作为一个例子,这是我从另一篇关于 SO 的帖子中摘取的小片段

case "" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        stop
        start
        ;;
    status)
        check_status
        ;;
    *)
        echo "Usage: 
a) ...blah...
b) ...more...
c) ...again...
{start|stop|restart|status}" exit 1 ;; esac

Looking at this, firstly I can see that caseends with esac, which is its reversed form (like ifending in fi). Secondly I understand that each case is followed by a ). Fair enough, but why on earth do I need two ;at the end of each statement? I would also say that the )without an accompanying (is ugly.

看着这个,首先我可以看到以case结尾esac,这是它的反转形式(如if以 结尾fi)。其次,我明白每个案例后跟一个). 很公平,但为什么我;在每个语句的末尾需要两个?我还要说)没有陪伴(是丑陋的。

I'm looking for more information about the historical aspect of the language, but I'm open for technical reasons as well.

我正在寻找有关该语言历史方面的更多信息,但出于技术原因,我也持开放态度。

采纳答案by Jonathan Leffler

Per request:

每个请求:

  • So can you guess why a loop is 'for ...; do ...; done' and not 'for ...; do ...; od'? There was a sound reason for it - but the Algol-like reversed keyword to mark the end was used elsewhere.
  • 那么你能猜出为什么循环是 ' for ...; do ...; done' 而不是 ' for ...; do ...; od' 吗?这样做有一个合理的理由 - 但是在其他地方使用了类似 Algol 的反向关键字来标记结束。

Answer:

回答:

  • The syntax came from Bourne (of Bourne shell fame). He had worked on Algol, and liked it enough to model some of the shell syntax on Algol. Algol uses reversed keywords to mark the ends of constructs, so 'case ... esac' was appropriate. The reason that loops do not end with 'od' is that there was already a command 'od' in Unix - octal dump. So, 'done' is used instead.
  • 语法来自 Bourne(Bourne shell 名气)。他曾在 Algol 上工作过,并且非常喜欢它,可以在 Algol 上模拟一些 shell 语法。Algol 使用反向关键字来标记结构的结尾,因此 'case ... esac' 是合适的。循环不以 'od' 结尾的原因是 Unix 中已经有一个命令 'od' - 八进制转储。因此,改为使用“完成”。

By reputation, the Bourne shell source code was written in idiosyncratic C with macros to make it look like Algol. This made it hard to maintain.

众所周知,Bourne shell 源代码是用特殊的 C 语言编写的,并带有宏,使其看起来像 Algol。这使得维护变得困难。

With respect to the main question - about why no opening bracket (parenthesis) around the alternatives in the casestatement - I have a couple of related theories.

关于主要问题——关于为什么在case声明中的备选方案周围没有左括号(括号)——我有几个相关的理论。

First of all, back when the Bourne shell was written (late 1970s), much editing was done with 'ed', the standard text editor. It has no concept of skipping to a balanced parenthesis or other such notations, so there was no requirement for a leading parenthesis. Also, if you are writing a document, you might well marshal your arguments with:

首先,在编写 Bourne shell 时(1970 年代后期),很多编辑都是使用标准文本编辑器' ed'完成的。它没有跳到平衡括号或其他此类符号的概念,因此不需要前导括号。此外,如果您正在编写文档,您可能会用以下方式整理您的论点:

restart)
   stop; start;;
...

The opening parenthesis is often omitted - and the casestatement would fit into that model quite happily.

左括号经常被省略 - 并且该case语句将非常适合该模型。

Of course, since then, we have grown used to editors that mark the matching open parenthesis when you type a close parenthesis, so the old Bourne shell notation is a nuisance. The POSIX standard makes the leading parenthesis optional; most more modern implementations of POSIX-like shells (Korn, Bash, Zsh) will support that, and I generally use it when I don't have to worry about portability to machines like Solaris 10 where /bin/sh is still a faithful Bourne shell that does not allow the leading parenthesis. (I usually deal with that by using #!/bin/kshas the shebang.)

当然,从那时起,我们已经习惯了编辑器在您键入右括号时标记匹配的左括号,因此旧的 Bourne shell 表示法很麻烦。POSIX 标准使前导括号可选;大多数更现代的 POSIX 类 shell 实现(Korn、Bash、Zsh)都支持这一点,我通常在我不必担心可移植到像 Solaris 10 这样的机器上时使用它,其中 /bin/sh 仍然是忠实的 Bourne不允许使用前导括号的 shell。(我通常通过使用#!/bin/ksh作为shebang来处理它。)

回答by khachik

The reason of using ;;is that a single ;can be used to write multiple statements in one line, like:

使用的原因;;是一个;可以用一行写多条语句,比如:

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

    etc.

回答by Paused until further notice.

Bash can accept matching parentheses:

Bash 可以接受匹配的括号:

1) do this
2) do that

回答by jilles

The closing parenthesis is sometimes used in lists in natural language, like

右括号有时用于自然语言的列表中,例如

if (condition)
    command here;

The reversed keywords were taken from some form of Algol but are in fact a very good idea for interactive use. They clearly demarcate the end of a construct, including if/else.

反向关键字取自某种形式的 Algol,但实际上是交互式使用的一个很好的主意。它们清楚地划分了结构的结尾,包括 if/else。

For example, with a C-like syntax, after this has been parsed:

例如,使用类似 C 的语法,在解析之后:

##代码##

is there an elsecoming or not? rc, a shell from Plan 9 with a more C-like syntax, solves this by providing if notinstead of elsebut it is not pretty.

有没有else来?rc 是来自 Plan 9 的 shell,它具有更像 C 的语法,通过提供if not代替而不是解决了这个问题,else但它并不漂亮。

With Bourne shell syntax, you'll have either elseor fiand there is no need to read additional input.

使用 Bourne shell 语法,您将拥有elsefi并且无需读取额外的输入。