bash/zsh 脚本中“case”语句的奇怪语法的原因是啥?
Posted
技术标签:
【中文标题】bash/zsh 脚本中“case”语句的奇怪语法的原因是啥?【英文标题】:What is the reason for the weird syntax of the "case" statement in a bash/zsh script?bash/zsh 脚本中“case”语句的奇怪语法的原因是什么? 【发布时间】:2011-05-13 09:45:47 【问题描述】:从程序员的角度来看,shell 脚本只是另一种编程语言,人们必须学习并遵守该语言的规则。然而,我不得不承认,这种语法是我所见过的一种相当常用的语言中最奇怪的风格。 shell 是否从它所继承的旧语言中获取了这种语法?语法中是否有特殊含义/含义?
作为一个例子,这是我在 SO 上从another post 获取的一个小 sn-p
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
status)
check_status
;;
*)
echo "Usage: $0 start|stop|restart|status"
exit 1
;;
esac
看到这个,首先我可以看到case
以esac
结尾,这是它的反转形式(如if
以fi
结尾)。其次,我知道每个案例后面都有一个)
。很公平,但是为什么我在每个语句的末尾都需要两个;
?我还要说没有伴随(
的)
是丑陋的。
我正在寻找有关该语言历史方面的更多信息,但出于技术原因我也愿意。
【问题讨论】:
那么你能猜出为什么循环是'for ...;做 ...;完成'而不是'为了......;做 ...;哦?这样做是有充分理由的 - 但是在其他地方使用了类似 Algol 的反向关键字来标记结尾。 @Jonathan 我不是在争论“为什么会这样而不是那样?” (我想我一开始就说清楚了)。我的问题更像是“丑陋的语法从何而来?” 语法来自 Bourne(以 Bourne shell 闻名)。他曾在 Algol 上工作过,并且非常喜欢它以在 Algol 上对一些 shell 语法进行建模。 Algol 使用反向关键字来标记结构的结尾,因此“case ... esac”是合适的。循环不以 'od' 结尾的原因是 Unix 中已经有一个命令 'od' - 八进制转储。因此,改为使用“完成”。 (而且,众所周知,Bourne shell 源代码是用特殊的 C 语言编写的,带有宏,使它看起来像 Algol。这使得它难以维护。) @Jonathan 好,请回答:) 【参考方案1】:每个请求:
那么你能猜出为什么循环是'for ...; do ...; done
'而不是'for ...; do ...; od
'吗?这样做是有充分理由的 - 但在其他地方使用了类似 Algol 的反向关键字来标记结尾。
答案:
语法来自 Bourne(以 Bourne shell 闻名)。他曾在 Algol 上工作过,并且非常喜欢它以在 Algol 上对一些 shell 语法进行建模。 Algol 使用反向关键字来标记结构的结尾,因此“case ... esac”是合适的。循环不以 'od' 结尾的原因是 Unix 中已经有一个命令 'od' - 八进制转储。因此,改为使用“完成”。众所周知,Bourne shell 源代码是用特殊的 C 语言编写的,带有宏,使其看起来像 Algol。这使得它很难维护。
关于主要问题 - 关于为什么在 case
声明中的替代项周围没有左括号(括号) - 我有几个相关的理论。
首先,在编写 Bourne shell 时(1970 年代后期),很多编辑都是用“ed
”,standard text editor 完成的。它没有跳到平衡括号或其他类似符号的概念,因此不需要前导括号。此外,如果您正在编写文档,您可能会使用以下方式整理您的论点:
a) ...blah...
b) ...more...
c) ...again...
左括号通常被省略 - case
语句很适合该模型。
当然,从那时起,我们已经习惯了在您键入右括号时标记匹配的左括号的编辑器,因此旧的 Bourne shell 表示法很麻烦。 POSIX 标准使前导括号是可选的;大多数更现代的类似 POSIX 的 shell(Korn、Bash、Zsh)的实现都将支持这一点,当我不必担心像 Solaris 10 这样 /bin/sh 仍然是忠实 Bourne 的机器的可移植性时,我通常会使用它不允许前导括号的外壳。 (我通常使用#!/bin/ksh
作为shebang。)
【讨论】:
我想说“主要问题”是语法的起源,它是 Algol(在 bash 的***页面上找不到)。无论如何都是好答案:) 有关“原始”Bourne shell 的信息可以在 Sourceforge 的 Heirloom Bourne Shell 项目中找到(以及您最喜欢的搜索引擎可以找到的其他地方)。【参考方案2】:之所以使用;;
,是因为单个;
可以在一行中写多个语句,比如:
restart)
stop; start;;
...
【讨论】:
【参考方案3】:Bash 可以接受匹配的括号:
case "$1" in
(start)
start
;;
(stop)
stop
;;
etc.
【讨论】:
POSIX 和 Korn shell 也允许前导括号。很久以前,ed
('标准文本编辑器' - 在第 7 版 UNIX 手册中是这样说的)没有“反弹光标以匹配左括号”选项,所以左括号不是真的必要的。
Autoconf 和朋友更喜欢在in
之后放置带有适当数量的左括号的评论(例如case "$1" in # ((
)。这有助于无法处理 $()
中的正常 case 语句的 shell 和编辑器,就像新语法一样,但也适用于原始 Bourne shell。
@Dennis 很高兴知道,但这是在不匹配样式流行之前还是之后?
@phunehehe:我不知道你的意思,除非你想知道是否有一个 shell 有 case
/esac
不接受左括号。答案是“是”。最初的 Bourne shell 就是一个例子。
不,我的意思是,bash 从一开始就接受匹配的括号吗?还是后来有人抱怨丑陋时,它作为一个功能出现了?【参考方案4】:
右括号有时用于自然语言的列表中,例如
1) do this
2) do that
颠倒的关键字取自某种形式的 Algol,但实际上对于交互式使用来说是一个非常好的主意。它们清楚地划分了构造的结尾,包括 if/else。
例如,使用类似 C 的语法,在解析后:
if (condition)
command here;
有没有else
来了? rc 是 Plan 9 中的一个 shell,它的语法更类似于 C,它通过提供 if not
而不是 else
来解决这个问题,但它并不漂亮。
使用 Bourne shell 语法,您将拥有 else
或 fi
,并且无需读取额外的输入。
【讨论】:
我明白你关于列表的观点,但其他部分没有多大意义。无论哪种方式,都必须读取附加输入以确定它是else
还是fi
但附加输入是if
的一部分。使用 else
的类 C 语法,如果没有 else
,shell 最终会读取下一个命令,这在交互式 shell 中是相当意外的。以上是关于bash/zsh 脚本中“case”语句的奇怪语法的原因是啥?的主要内容,如果未能解决你的问题,请参考以下文章