bash,zsh,fish之类的shell语言与上面的脚本语言之间有什么区别,使它们更适合shell?

使用命令行时,shell语言似乎是容易得多。尽管有相反的报道,对我来说,例如使用bash比在ipython中使用shell配置文件要平滑得多。我认为大多数人都同意我的观点,即在Python中,比Bash中的大部分中大型编程要容易得多。我使用Python作为我最熟悉的语言,Perl和Ruby也是如此。

我试图阐明原因,但是除了假设在两者都有关系。

这个问题的原因是我希望开发一种可在两者中使用的语言。如果您知道这种语言,也请发布它。

正如S.Lott解释的那样,这个问题需要澄清。我问的是shell语言和脚本语言的功能。因此,比较不是关于各种交互(REPL)环境的特征,例如历史记录和命令行替换。该问题的另一种表达方式是:

适合于复杂系统设计的编程语言能否同时表达有用的单行代码,这些单行代码可以访问文件系统或控制作业?编程语言是否可以按比例放大和缩小?

#1 楼

我可以想到几个不同之处。只是想在这里进行排序,没有特别的顺序:


Python&Co.被设计为擅长脚本编写。 Bash&Co.被设计为仅擅长脚本编写,绝不妥协。 IOW:Python的设计既擅长脚本编写,也擅长使用非脚本编写,Bash只关心脚本编写。 123,字符串123和文件123完全不同。但是,它们不是静态类型的,这意味着它们必须具有不同的文字,才能使其分开。
示例:

                | Ruby             | Bash    
-----------------------------------------
number          | 123              | 123
string          | '123'            | 123
regexp          | /123/            | 123
file            | File.open('123') | 123
file descriptor | IO.open('123')   | 123
URI             | URI.parse('123') | 123
command         | `123`            | 123


Python&Co.设计为可扩展到10000、100000,甚至100万个行程序,Bash&Co.设计为可扩展到10个字符程序。 ,进程都是一流的对象,在Python中,只有Python对象是一流的,如果要操作文件,目录等,则必须先将它们包装在Python对象中。
Shell编程基本上是数据流编程。没有人意识到这一点,即使是编写shell的人也没有意识到,但是事实证明shell在这方面非常擅长,而通用语言则没有那么多。在通用编程世界中,数据流似乎主要被视为并发模型,而不仅仅是编程范例。通用编程语言无法正常工作。至少,我还没有看到令人信服的实现。有RuSH(Ruby外壳),它试图在Ruby中实现外壳,有rush,这是在Ruby中用于外壳编程的内部DSL,还有Hotwire,它是Python外壳,但是IMO都没有一个与Bash,Zsh,鱼和朋友竞争。

实际上,恕我直言,目前最好的外壳是Microsoft PowerShell,考虑到几十年来,微软一直在使用最糟糕的外壳evar,这是非常令人惊讶的。我的意思是COMMAND.COM?真? (不幸的是,它们仍然有一个笨拙的终端。从那以后,它仍然是“命令提示符”,Windows 3.0?) ,COMMAND.COM,VBScript,JScript),而是从Unix shell开始,然后删除所有向后兼容的内容(例如,用反引号替换命令),并对其进行一些调整以使其对Windows更友好(例如,将现在未使用的反引号用作转义字符而不是反斜杠,反斜杠是Windows中的路径组件分隔符)。之后,就是魔术发生的时候。 Python首先关心大型程序,其次是脚本。 Bash只关心脚本。 PowerShell首先关注脚本,其次是大型程序。对我而言,具有决定性意义的时刻是观看有关Jeffrey Snover(PowerShell的首席设计师)的采访的视频,当采访者问他可以用PowerShell编写的程序有多大时,Snover回答了而没有错:“ 80个字符”。那时我意识到,这终于是Microsoft的一个人,他“获得”了Shell编程(可能与PowerShell不是由Microsoft的编程语言组(即lambda-calculus数学书呆子)或OS组(内核书呆子)开发的事实有关),而是服务器组(即实际使用shell的sysadmins)),我应该认真研究PowerShell。

通过使参数为静态类型来解决数字2。因此,您只需编写CMD.EXE,PowerShell便知道它是字符串,数字还是文件,因为cmdlet(在PowerShell中称为shell命令)声明了其参数类型。这具有非常深远的影响:与Unix不同,在Unix中,每个命令都负责解析自己的参数(shell基本上将参数作为字符串数组传递),而PowerShell中的参数解析则由shell完成。 cmdlet向外壳指定所有选项,标志和参数,以及它们的类型和名称以及文档(!),然后可以在一个集中位置执行参数解析,制表符补全,IntelliSense,内联文档弹出窗口等。 (这不是革命性的,PowerShell设计人员承认像DIGITAL命令语言(DCL)和IBM OS / 400命令语言(CL)这样的外壳是现有技术。对于曾经使用过AS / 400的任何人,应该听起来都很熟悉在OS / 400中,您可以编写一个shell命令,如果您不知道某些参数的语法,则可以简单地将其省略并按F4键,这将带来一个带有标签字段的菜单(类似于HTML表单) ,下拉菜单,帮助文本等。这仅是因为操作系统知道所有可能的参数及其类型。)在Unix Shell中,此信息通常重复三遍:在命令本身的参数解析代码中, 123用于制表符补全的脚本和在联机帮助页中。

数字4通过以下方式解决:PowerShell对强类型对象进行操作,其中强对象包括文件,进程,文件夹等。

数字5特别有趣,因为PowerShell是我唯一的外壳知道,编写它的人实际上知道外壳实际上是数据流引擎,并故意将其实现为数据流引擎。

关于PowerShell的另一个好处是命名约定:所有cmdlet都命名为bash-completion,此外,还为特定操作和特定对象提供了标准化名称。 (同样,这对于OS / 400用户来说应该很熟悉。)例如,与接收某些信息有关的所有事情都称为Action-Object。在(子)对象上运行的所有事物都称为Get-Foo。因此,与Bar-ChildItem等效的是ls(尽管PowerShell还提供了内置别名Get-ChildItemls-实际上,只要有意义,它们都提供Unix和dir别名以及缩写(在这种情况下为CMD.EXE))。
但是IMO的杀手级功能是强类型对象管道。尽管PowerShell是从Unix Shell派生的,但有一个非常重要的区别:在Unix中,所有通信(通过管道和重定向以及通过命令参数)都使用无类型,无结构的字符串完成。在PowerShell中,它们都是强类型化的结构化对象。它是如此强大,以至于我严重怀疑为什么没有其他人想到它。 (嗯,它们已经普及了,但是它们从未流行起来。)在我的shell脚本中,我估计最多只有三分之一的命令可以用作另外两个在同一文本格式上不一致的命令之间的适配器。 。这些适配器中的许多适配器在PowerShell中都不可用,因为cmdlet交换结构化对象而不是非结构化文本。而且,如果您查看这些命令的内部,则它们几乎包括三个阶段:将文本输入解析为内部对象表示形式,操作对象,将它们转换回文本。再次,第一阶段和第三阶段基本上消失了,因为数据已经作为对象进入了。自适应类型系统。

无论如何,我不想将其变成PowerShell商业广告。尽管有许多事情与Windows或特定的实现有关,而与概念无关,但对于PowerShell而言,有很多事情并没有那么出色。 (例如,它是在.NET中实现的事实,这意味着如果由于某些其他应用程序需要将.NET框架保存在文件系统缓存中,那么第一次启动该外壳可能会花费几秒钟的时间。您经常在不到一秒钟的时间里就完全使用了shell,这是完全不可接受的。)

我要说的最重要的一点是,如果您想查看脚本语言和shell中的现有工作,您不应止步于Unix和Ruby / Python / Perl / PHP系列。例如,已经提到了Tcl。 Rexx将是另一种脚本语言。 Emacs Lisp将是另一个。在shell领域中,有一些已经提到的大型机/中型shell,例如OS / 400命令行和DCL。另外,Plan9的遥控器。

评论


这个问题的两个部分都很好的答案。关于第2点和第4点:是因为这样的静态类型还是因为强制转换操作?在我所知道的大多数非外壳语言(静态或动态)中,123无法定义两种不同的类型。另外,您是否有解释Powershell自适应类型系统的链接? Google似乎没有向我扔任何我能理解的东西。

–穆罕默德·阿尔卡鲁里(Muhammad Alkarouri)
2010年9月6日下午0:34

好答案!您还记得与Jeffrey Snover的访谈的链接吗?

–毛里西奥·谢弗(Mauricio Scheffer)
2010年11月8日14:43



4个月内13票,然后20小时内84票?我被重新引诱了吗? @Mauricio Scheffer:我浏览了Channel9上的几乎所有视频,甚至回到PS仍被称为Monad的时候,但是对不起我没有跳出来。 @Dave:有一个名为Pash(PoSh + Bash)的项目,但是三年前才发布了一个版本。 BUSH和FISH远离Bourne语法,但是它们仍然处理文本。 @codebliss:问题是关于Python作为一种外壳语言。这里有趣的数字是一个LOC。

–Jörg W Mittag
2011年1月2日,0:11



@Muhammad Alkarouri:另一种查看方式:PowerShell不支持多态或重载。在具有多态性的语言中,该语言根据参数的类型决定执行哪个过程。 (例如,x.ToString()中的in取决于执行ToString()的x的类型。)在PowerShell中,它恰好是双重的:该语言根据过程决定解释参数的类型。还有一种解释是,语法是根据参数类型动态构建的,因此只有一种方法可以解析标记。

–Jörg W Mittag
2011年1月2日,0:47

@Alexandru:PowerShell仍然是一个Shell(因此得名),因此使执行程序变得容易(尽管根据可能需要引用的参数并不是很轻松)。因此,您可以像在其他任何地方一样使用ImageMagick,而无需首先编写/查找cmdlet或将其包装的PowerShell函数。但是PowerShell原生的实现可能要酷得多,例如通过传递图像对象并将图像过滤器实现为cmdlet。我仍在考虑编写允许Load-Image foo.bmp | Apply-Blur -Radius 5 |保存图片x.png。

–乔伊
13年2月15日在13:03

#2 楼

这是文化的。伯恩(Bourne)炮弹已有25年历史;它是最早的脚本语言之一,并且是满足Unix管理员中心需求的第一个好的解决方案。 (即,一种“胶水”将所有其他实用程序结合在一起并执行典型的Unix任务,而不必每次都编译该死的C程序。)怪异的规则和标点符号声明样式(在1970年代有用,当计数每个字节时)使非管理员难以渗透。但这确实完成了。通过对其后代(ksh,bash,zsh)的改进,可以解决这些缺陷和不足,而不必重新理解其背后的思想。管理员坚持使用核心语法是因为,很奇怪,没有别的方法可以更好地处理简单的东西。

对于复杂的东西,Perl出现并演变为一种半管理员,半应用语言。但是,事情变得越复杂,就越被视为一种应用程序而不是管理员工作,因此,尽管事实证明正确的怪胎往往会导致“寻找程序员”而不是“管理员”,但商人倾向于都是。所以这就是重点所在,Perl应用程序功能的演进改进导致了……好吧,Python和Ruby。 (这是一个过分的简化,但是Perl是这两种语言的几种灵感之一。)

结果?专业化。管理员倾向于认为现代解释型语言对于每天付给他们的酬劳而言过于繁重。总体而言,他们是对的。他们不需要物体。他们不在乎数据结构。他们需要命令。他们需要胶水。没有什么比Bourne shell概念更好地执行命令了(除了Tcl,这里已经提到过)。伯恩(Bourne)就足够了。

程序员-如今必须越来越多地学习关于devop的知识-着眼于Bourne shell的局限性,并想知道有人怎么能忍受它。但是他们所知道的工具虽然固然倾向于Unixish风格的I / O和文件操作,但并不能达到目的。我已经在Ruby中编写了诸如备份脚本和文件重命名一次之类的东西,因为我比bash知道得更好,但是任何专门的管理员都可以在bash中做同样的事情-可能行数更少,开销也更少,但是无论哪种方式,它都一样有效。 -但是技术的发展,就像其他一切的发展一样,往往会停滞不前。除非差异被视为破坏交易的挫败感,否则“更好”的解决方案不会成功。 Bourne类型的脚本可能会让您感到沮丧,但是对于一直使用它的人以及它所打算的工作,它总是可以完成工作。

评论


我认为这可能会启发某些未来的技术,例如将管道(|)作为基本概念。就像Javascript被重新命名并用多种现代语言重新命名,然后是node.js一样,shell脚本也可能发生同样的情况。

–谢尔盖·奥尔山斯基(Sergey Orshanskiy)
14年6月22日在4:15

#3 楼

Shell语言必须易于使用。您要键入一次性的命令,而不是小型程序。即您要输入

ls -laR /usr


不是

shell.ls("/usr", long=True, all=True, recursive=True)


这(也)表示shell语言不是真的注意参数是选项,字符串,数字还是其他。

另外,shell中的编程构造是一个附加组件,甚至不是始终内置的。即考虑一下(ba)sh中if和[的组合,用于生成序列的seq等。即管道,文件重定向,进程/作业控制等。

评论


感谢您的回答,但是在我看来这些差异并不是语言固有的。设计一个库非常容易,因此在Python中您可以说shell.ls('usr',flags ='laR')。甚至可以删除shell位和flags =位。我同意,诸如重定向和过程控制之类的内容在Shell中已得到优化,因为它们与外壳相关。

–穆罕默德·阿尔卡鲁里(Muhammad Alkarouri)
2010-09-03 16:56

您仍然需要做适当的报价,并保持平衡。并且您在这里提供了一个简单的参数,例如“ ls --sort = time -R / bin / usr”呢?

– Ivo van der Wijk
2011年1月2日,21:17

您关于运行命令的观点适用于Python,但并非适用于所有正在考虑的语言:例如,`ls -laR / usr`将在perl中实现。

–直觉
2012年10月31日19:16



那么您就不会使用perl,而只是通过perl执行shell命令。

– Ivo van der Wijk
2012年11月8日在8:42

无耻的插件,我写了一个python库,希望可以简化python的使用:github.com/houqp/shell.py

–houqp
2014年5月1日20:52

#4 楼


如果您知道这种语言,也请发布。


Tcl是一种这样的语言。主要是因为它被设计为主要是CAD程序的shell解释器。这是一位铁杆Python程序员*的经验,了解tcl的设计方式: >对我来说,我已经编写并一直使用和改进的tcl shell(当然是以tcl编写)作为我的自制路由器上的主要Linux登录shell:纯tcl readline就像tcl一般而言,其语法与传统shell的相似之处有关:


最基本的tcl语法是command argument argument...。没有别的了。这与bash,csh甚至是DOS shell相同。
裸字被视为字符串。这再次类似于传统的shell,允许您编写:open myfile.txt w+而不是open "myfile.txt" "w+"
由于1和2 tcl的基础最终几乎没有多余的语法。您编写的代码标点更少:puts Hello而不是printf("Hello");。在编写程序时,您不会感觉到那么多伤害,因为您花了大量时间思考编写什么。当您使用外壳程序复制文件时,您不会只是键入而不得不键入(",);一次又一次变得很烦人。我,我是tcl的铁杆程序员

评论


很好!我忘记了Tcl。很久以前,我对它进行了简短的介绍,而我只是忘了它对于我使用了很长时间的某些Cisco路由器来说是一个完美的外壳接口。看起来Tcl是它们之间最接近的最佳点。我将切换到tclsh几天,以了解它的运行情况。

–穆罕默德·阿尔卡鲁里(Muhammad Alkarouri)
2010年9月3日于17:03

现在有点不高兴了。 Tcl不如其他语言那么成功,您能说为什么吗?我记得对数学命令(可能是OOP)不满意,但这是很久以前的事了。

–穆罕默德·阿尔卡鲁里(Muhammad Alkarouri)
2010-09-3 17:06

首先,我想指出tclsh是一个真正的基本shell:没有上下箭头历史记录,没有制表符完成等。我建议您至少使用rlwrap tclsh将其包装在readline中。甚至更好,使用上面链接的我的代码作为您的外壳。第二点,我认为这是因为大多数程序员对看起来不像C的任何东西都不满意。有趣的是,Netscape最初将Tcl实现为浏览器脚本语言,但是管理部门希望它看起来像Java语法(这又是另一种语言类似C的语法)。

– slebetman
2010-09-3 17:28

发布评论后,我确实查看了您的代码,并将使用它。谈到类似C的语言不足,您可能以前曾遇到过。

–穆罕默德·阿尔卡鲁里(Muhammad Alkarouri)
2010年9月3日于22:07

我记得TCL的问题是政治上的。

–讨厌
2011年1月2日,下午1:38

#5 楼

谁说不是?看看Zoidberg。 REPL(读取Eval打印循环)构成了糟糕的外壳,因为每个命令在语法上都必须正确,并且运行程序从原来的开始:

/>
foo arg1 arg2 arg3


甚至不要让我开始尝试进行重定向。

因此,您需要一个能够理解命令和重定向以及要将命令绑定在一起的语言。我认为zoid(Zoidberg外壳)做得很好。

评论


要尝试zoid。您知道吗,有多少人用作他们的主要外壳?

–乔尔·伯格(Joel Berger)
10-10-26在0:39

@乔尔我对此表示怀疑。它的最新更新于2006年在CPAN上进行。它无处不在,功能完善并且得到维护。诸如zoid的贝壳很有趣,但是我总是回到bash或ksh。

– Chas。欧文斯
2010-10-26 12:42

是的,我还看到它没有得到维护。我想知道它可能需要什么样的修改,我会对使用具有这种功能的外壳感兴趣。

–乔尔·伯格(Joel Berger)
2010-10-26 12:56

我实际上已经修复了一些主要错误(我是新的维护者!)。查看Zoidberg,了解您所有基于Perl的Shell需求!

–乔尔·伯格(Joel Berger)
2012年2月11日在18:19

#6 楼

这个线程激发了我接管基于Perl的Shell Zoidberg的维护工作。经过一些修复后,它可以再次使用!查看用户指南或使用您喜欢的CPAN客户端安装Bundle::Zoidberg

#7 楼

不。


不,脚本语言可能不适合shell。


问题是宏语言与所有事物之间的二分法else。

shell与其他旧式宏语言(例如nroff和m4)一起位于类别中。在这些处理器中,所有内容都是一个字符串,并且处理器定义了从输入字符串到输出字符串的映射。

某些边界在所有语言中都是双向的,但是通常很清楚系统类别宏,或者,嗯,我不知道官方用语是什么...我会说“一种真正的语言”。它甚至可能是真正的shell的第二好的选择,但它永远不会成为宏语言。有太多的语法值得尊重。它需要太多的引号。

但是,当您开始在宏语言中编程时,宏语言有其自身的问题,因为必须进行太多的折衷才能摆脱所有语法。键入的字符串不带引号。需要重新引入各种魔术以注入缺少的语法。我曾经在nroff中做了一次代码高尔夫,只是有所不同。真奇怪。宏语言中大型实​​现的源代码令人恐惧。

评论


非常感谢!这实际上是一个缺失点,是对我的“我试图阐明原因但无法做到的准确反应,除了假设两者的字符串处理方式有所不同。”找出有关宏语言的任何研究是否完成将是很有趣的。

–穆罕默德·阿尔卡鲁里(Muhammad Alkarouri)
11年8月30日在9:41

#8 楼

我认为这是一个解析问题。 Shell语言默认情况下假定$命令表示您要运行的命令,Python / Ruby需要您执行system(“ command”)或其他操作。
并不是说它们不合适,只是没有人真正做过到目前为止,至少我是这样认为的。在Ruby中,Rush http://rush.heroku.com/是一个示例尝试,Python具有“ iPython”或类似名称。

评论


感谢您的链接。我使用过ipython,我的第一印象是Rush看起来更好一些,这可能是因为其流畅的界面。我需要多看一点。

–穆罕默德·阿尔卡鲁里(Muhammad Alkarouri)
2010年9月3日在16:59

#9 楼

由于这两种语言都是形式上的编程语言,因此您可以用一种语言进行操作,也可以用另一种语言进行操作。实际上,这是一个设计重点问题。 Shell语言是为交互使用而设计的,而脚本语言则不是。

设计中的基本区别是命令之间的数据存储和变量范围。在Bash等中,您必须跳过圈来存储值(例如,诸如set a='something'之类的命令),而在诸如Python之类的语言中,您只需使用赋值语句(a = 'something')。在外壳语言中使用值时,必须告诉语言您想要变量的值,而在脚本语言中,必须告诉语言何时想要字符串的立即值。在以交互方式使用时会产生效果。
在将ls定义为命令的脚本语言中,

a = some_value

ls a*b  


a是什么意思?这意味着some_value *(无论b是什么)还是
'a'anystring'b'?。在脚本语言中,默认值是存储在内存中的a。)

ls 'a*b'  Now means what the Unix ls a*b means.


以类似Bash的语言

set a=some_value

ls a*b   means what the Unix ls a*b means.

ls $a*b  uses an explicit recall of the value of a.


脚本语言使存储和调用值变得容易,并且很难在其上具有瞬时作用域一个值。 Shell语言可以存储和调用值,但每个命令的作用域范围很小。

评论


谢谢。这就是我在问题中“字符串的处理”所指的含义,但是您对此进行了更好的解释。

–穆罕默德·阿尔卡鲁里(Muhammad Alkarouri)
2011年1月1日于20:26

#10 楼

你问这个问题。并非所有人都同意shell语言是高级的。例如,_Why不会


不久前,一个朋友问我如何递归地在他的PHP脚本中搜索字符串。在那些目录中,他有很多大的二进制文件和模板,这些文件和模板实际上可能会使普通的grep陷入困境。我想不出一种方法来使用grep来实现这一目标,所以我认为最好将find和grep一起使用。在Ruby中对文件搜索进行了重做:我只需要耸耸肩,顺其自然。我说:“扩展起来要容易得多。”它可以跨平台工作。


评论


找 。名称'* .php'|通过避免N次调用grep,对于非大型文件,xargs -d \\ n grep -l'search_string'将更快。 :)

–直觉
2012年10月31日19:58

问题不是关于编写类似shell的脚本,而是关于shell执行的语言。

–乔尔·伯格(Joel Berger)
13年2月3日于13:59

因此,如果您理解一个语法很容易阅读的60字符脚本,就用一个125字符脚本(忽略了要使该脚本真正可读的其他空格和换行符)就更清晰了。为什么您不想使用python或ruby的一个很好的例子。所有脚本中有99.999%是具有确切规范(或1off作业)的内容,这些内容永远不会扩展。 Powershell甚至具有良好的语法,易于扩展,并且对于脚本的主要工作都非常简洁。

– Voo
13年2月4日在6:04



@Voo“如果您理解语法,这相当容易阅读”……然后您就销毁了自己的参数。除此之外,不仅需要理解语法,而且如果您打算在有竞争者的情况下选择重复使用它,则还必须喜欢它。我不喜欢Powershell的语法,所有的驼峰式外壳都很可怕。 YMMV。

–iain
13年7月14日在19:45

@ alexis,xargs(除非给出相反的标志)和find -exec {} +(而不是-exec {} \;)都将填写每次调用的最大允许参数长度,因此不,它小于N其中N是找到的项目数)。

–查尔斯·达菲(Charles Duffy)
2014年2月24日在2:36

#11 楼

可扩展性和可扩展性? Common Lisp(您甚至可以将CLISP以及可能的其他实现作为UNIX环境中的登录Shell运行)。

#12 楼

对于Windows用户,我还没有感觉到需要Powershell,因为我仍然使用Jpsoft的4NT(现在称为Take Control),它是一个非常出色的Shell,具有许多编程能力。因此,它结合了两全其美的优点。

例如,当您查看IRB(红宝石解释器)时,一定有可能将其扩展为具有更多的单行代码,以执行日常脚本或批量文件管理一分钟的任务。