控制台命令的复杂性,1979-2020年

我的爱好在一台显示器上打开McIlroy的“ UNIX哲学”,而在另一台显示器上阅读法力。

麦克罗伊原则的第一条常被改写为“做一件事情,但要做得好。” 这是他的话的缩写,“创建能做一件事的程序。” 对于新作品,请创建新程序,而不要通过添加新的“功能”使旧程序复杂化。”

麦克罗伊举了一个例子:

对于外部人员来说,UNIX编译器不发布清单似乎令人感到惊奇:打印效果更好,并且使用单独的程序可以更灵活地配置。

如果打开的帮助ls,则以开头。

ls [-ABCFGHLOPRSTUW@abcdefghiklmnopqrstuwx1] [file ...]

即,一个字母的标志ls包括除{jvyz}14个大写字母@和和之外的所有小写字母1这是22 + 14 + 2 = 38个单字符选项。

在Ubuntu 17中,的帮助ls不会显示常规摘要,但您会看到ls有58个选项(包括--help--version)。

让我们看看这是一种特殊情况还是正常状态。让我们列出一些常用命令,按使用频率排序。



该表显示了各种v7 Unix(1979),slackware 3.1(1996),ubuntu 12(2015)和ubuntu 17(2017)命令的命令行选项数量。参数越多,单元格越暗(对数刻度)。

我们看到,多年来,选项的数量急剧增加:通常,记录从左到右变暗(更多选项),而且在任何情况下记录都不会变浅(选项更少)。

麦克罗伊(McIlroy )长期以来一直谴责团队选择数量,规模和整体功能的增加1

, , Linux … []. , , . , , … Unix : « ? ?» , - , . , , , . … , .

具有讽刺意味的是,命令行选项越来越多的原因之一是McIlroy的另一句话:“编写用于处理文本流的程序,因为它是通用接口”(ls例如)。

如果传输了结构化数据或对象,则格式化可以留到最后阶段。但是在纯文本的情况下,格式和内容是混合的;由于格式化只能通过解析内容来完成,因此为了方便起见,命令通常会添加格式化选项。此外,可以在使用者将他的数据结构的知识,并进行格式“编码的”的论据知识cutawksed等(用户也使用他的这些计划是如何与格式化工作的知识,因为它是不同的程序不同,因此用户必须知道,例如,它是如何cut -f4 的不同,从awk '{ print $4 }2)这比在一个序列中将一个或两个参数传递给下一个命令要麻烦得多,而且这将工具的复杂性转移给了用户。

人们有时会说他们不想支持结构化数据,因为那样一来,在通用工具中,人们就必须支持多种格式。但是他们已经必须支持多种格式才能制作出通用工具。某些标准命令无法使用其他命令读取输出,因为它们使用不同的格式。例如,wc -wUnicode无法正确处理它,等等。说“文本”是通用格式与说“二进制”是通用格式相同。

据说,对于命令行工具的这种复杂性没有其他选择。但是,那些说过这些的人从未尝试过替代方法,例如PowerShell。我对PowerShell提出了很多抱怨,但是结构化数据的传输以及轻松地使用结构化数据而不必将元数据留在脑海中,以便我可以将其传输到管道上正确位置的正确命令行工具的能力,并不是我的抱怨3

当您被告知在处理文本时程序应该简单且兼容时,这些人假装文本数据没有解析结构4。在某些情况下,我们可以将所有内容都视为一行,以空格分隔,或者视为具有行和列分隔符的表(其行为当然与其他工具不一致)。这增加了一些麻烦。在某些情况下,将数据序列化为纯文本格式会增加复杂性,因为由于数据结构的原因,简单地序列化为文本随后需要进行大量的解析工作,才能以有意义的方式重新整合数据。

团队现在拥有更多选择的另一个原因是,人们为可以由多个团队的管道实现的功能添加了方便的标记。从Unix v7开始,这种做法一直在进行,ls似乎出现了一个更改排序顺序的选项(尽管可以通过将输出传递到来完成tac)。

随着时间的流逝,纯粹是为了方便起见,添加了其他参数。例如,原本没有参数的命令mv现在可以移动文件并同时创建备份副本(三个选项;两种指定备份的方法(其中一种采用参数,另一种不采用参数)从环境变量读取隐式参数VERSION_CONTROL;另一种选择允许您覆盖默认的备份后缀)。现在mv,仍然存在从不覆盖文件或仅覆盖较新文件的选项。

mkdir另一个以前没有选项的程序。如今,除SELinux或SMACK的安全性选项以及帮助和版本号外,其所有标志都仅为方便起见而添加:设置新目录的权限并创建父目录(如果不存在)。

tail开始时,只有一个选项-number,表示为工作的出发点。然后,我们添加了格式化和选项,以方便格式化。该标志-z用替换行分隔符null。以下是为方便起见添加的其他选项示例:-f在出现新更改时进行打印,-s设置检查更改之间的超时间隔/代码> -f,并且-retry在文件不可用时重试访问。

McIlroy批评开发人员添加了所有这些选项,但是我个人感觉更好。尽管我从未使用过某些参数,但我很少使用其他参数,但这是命令行参数的优点-与图形界面不同,添加这些参数不会使界面混乱。是的,法力和帮助激增,但是在Google和Stackoverflow的时代,无论如何,许多人只是谷歌搜索任何问题,而不是阅读法力。

当然,添加选件会增加维护人员的负担。但这是对他们带来的好处的合理支付。考虑到维护者和用户的数量之比,逻辑上要给前者增加负担,而不是给后者增加负担。这类似于加里·伯恩哈特(Gary Bernhardt)的话,即演练50次是明智的。如果观众是300人,则观看表演所花费的时间与排练所花费的时间之比仍为6:1。对于流行的命令行工具,此比例甚至更高。

有人可能会争辩说,所有这些附加选项给用户带来了额外负担。这不是完全错误的,但是这种复杂性的负担将永远存在。问题是到底在哪里。如果您想像一套命令行工具与一种外壳程序共同构成一种语言,每个人都可以使用该语言编写一种新方法,并且在流行的情况下,该方法会有效地添加到标准库中,并且这些标准由诸如编写用于处理文本流的程序之类的公理确定,因为它具有通用性接口”,如果您将其全部内容考虑在内,该语言将变成不连贯的只写混乱。至少,得益于具有多种选择和功能的工具,Unix用户可以用一大套工具来替换大量不一致的巨大工具,尽管它们从外部彼此不一致,但是它们具有一定的内部一致性。

McIlroy暗示工具箱中缺乏周到性。就像,Unix的创始者应该坐在同一个房间里,仔细思考,直到他们提出了一系列“非常简单”的顺序工具。但是它不会扩展,Unix本身的哲学不可避免地导致我们陷入困境。并不是有人没有思考多久或刻苦。关键是,这种哲学不能超出一个相对较小的团队,并且具有可以容纳在一个房间中的共同文化理解。

如果有人想基于“ Unix哲学”来编写工具,那么对于“简单性”的含义或“做一件事情”的原则,不同的人会有不同的看法5该工具应如何正确工作-不一致会以郁郁葱葱的颜色绽放,结果您将变得非常复杂,类似于PHP之类的极为不一致的语言。人们会因各种怪异和不一致而取笑PHP和JavaScript,但是像语言和标准库一样,任何流行的外壳程序与流行的* nix工具集合在一起,会变得更糟,并且由于不一致而导致随机性变得更加复杂,即使在同一Linux发行版中也是如此。 。不能这样。如果比较Linux,BSD,Solaris,AIX等的发行版,那么用户在切换系统时必须牢记的随机复杂性数量就掩盖了PHP或JavaScript的不一致之处。与它们相比,最荒谬的编程语言是出色设计的真实示例。

为了清楚起见,我并不是说我本人或其他人可以更好地应对70年代的发展,同时考虑到当时可用的知识,并创建一个既有用又优雅的系统。当然,回顾和查找问题很容易。我只是不同意某些Unix鉴赏家(如McIlroy)的评论,这些评论暗示我们已经忘记或不了解简单性的价值。或者肯·汤普森(Ken Thompson),他说C语言和其他语言一样安全,如果我们不希望出现错误,我们只需要编写没有错误的代码即可这种评论暗示多年来没有什么变化。据称,在20世纪70年代,我们以与今天相同的方式构建了系统,并且在五十年的集体经验中,数千万人年没有教给我们什么。而且,如果我们转向Unix的起源和创建者,那么一切都会好起来的。在所有应有的尊重下,我不同意。

应用:内存


尽管McIlroy对膨胀的二进制文件的抱怨不在本文讨论范围之内,但我要注意的是,在2017年,我以300美元的价格购买了具有16 GB RAM的Chromebook。 1兆字节的二进制文件在1979年可能是一个严重的问题,当时标准的Apple II配备了4 KB的内存。 1979年,Apple II的价格为1,298美元,2020年为4,612美元。如今,您可以购买价格不超过此价格的1/15的廉价Chromebook,而其内存却要高出四百万倍。当(便携式!)机器的成本便宜了一个数量级并且拥有四百万倍的内存时,关于内存使用量增长了千倍的投诉似乎有些荒谬。

我喜欢优化,所以我将主页缩小到两个包(如果CDN支持brotli高级,则将是一个包),但这纯粹是出于审美要求,我这样做很有趣。命令行工具的瓶颈是不使用内存,而将工具的内存优化为1兆字节的时间就像将主页缩减为一个程序包一样。也许是一个有趣的爱好,但仅此而已。

表编译方法


命令的使用频率是从github上命令历史记录的公共文件中获得的,不一定与您的个人经历相对应。仅计算“简单”命令,不包括curl,git,gcc(后者具有1000多个选项)和wget之类的实例。简单的概念是相对的。内置的shell命令(例如)cd也未考虑在内。

重复标记不被视为单独的选项。例如,U git blame -Cgit blame -C -Cgit blame -C -C -C不同的行为,但他们都将被视为一个参数,但-C -C他们-C 实际上是不同的参数。

表中的子选项被视为一个选项。例如,它ls支持以下内容:

--format=WORD across -x, commas -m, horizontal -x, long -l, single-column -1, verbose -l, vertical -C

尽管有七个选项format,这仍算作一个选项。

明确表示为无用的选项仍视为选项,例如ls -g,也将其忽略。

同一选项的多个版本被视为一个选项。例如,-A--almost-allls

如果证书表明该选项存在,但实际上不存在,则不会考虑。例如,v7 mv的帮助说:



如果file1和file2在不同的文件系统中,则mv应该复制该文件并删除原始文件。在这种情况下,所有者的名称将成为复制进程的名称,并且与其他文件的任何连接都将丢失。

mv必须接受-f标志作为rm,以禁止显示有关存在不可写目标文件的消息。

但这-f不是表中的标志,因为该选项实际上不存在。

该表于2017年结束,因为随后编写了本文的初稿。直到现在,他们才开始阅读。

关于这个话题



, , -, //.



1.此报价与普通版本略有不同,因为我观看了原始视频据我所知,此报价在互联网上的所有副本(Bing,DuckDuckGo和Google索引)均取自同一个人的相同抄录。由于声音质量较差,因此存在一定的歧义,而且我听到的单词与该人听到的内容略有不同。[回来]

2.由于不同的团队对格式的处理方式不同,因此如何将复杂性传递给用户的另一个示例是时间格式time当然,内置的shell时间与不兼容/usr/bin/time用户必须意识到这一事实并知道如何处理。[回来]

3.例如,对于任何对象,您可以使用ConvertTo-JsonConvertTo-CSV。或“ cmdlet”更改对象属性的显示。您可以编写用于定义首选格式方法的格式配置文件。

另一种看待这一点的方式是通过康韦定律的棱镜。如果我们有一组由不同人员(通常来自不同组织)创建的命令行工具,那么如果有人无法确定标准并强迫人们接受该标准,则这些工具将完全不一致。实际上,这在Windows上效果相当好,而不仅仅是PowerShell。

微软的一个常见抱怨是API的大量交易,通常是出于非技术性的组织原因(例如,请参阅Stephen Sinofsky对远程推文的回复中所述的操作)。这是真的。但是,从天真的用户的角度来看,标准Windows软件通常比* nix传输非文本数据要好得多。 Windows中非文本数据的覆盖范围至少可以追溯到1999年的COM(可能还有OLE和DDE,分别于1990年和1987年发布)。

例如,如果您从支持二进制格式的Foo中复制AB在支持格式的Bar中复制BC然后从Bar复制到支持CD,即使Foo和Baz没有通用的受支持格式,也可以正常工作。

当您剪切或复制某些内容时,该应用程序基本上会“告诉”剪贴板它可以提供数据的格式。当最终应用程序粘贴到应用程序中时,它可能会请求任何可用格式的数据。如果数据已经在剪贴板上,则Windows将提供它。如果不是这种情况,Windows将从源应用程序接收数据,然后将其传输到目标应用程序,并且副本存储一段时间。如果您从Excel中“剪切”,他会说“您”,表示他拥有许多格式的数据。这种系统对于兼容性非常好,尽管它当然不能称为简单或简约。

除了对多种格式的良好支持之外,足够长的时间足以使许多程序开始很好地处理这些功能;在Windows中,开箱即用通常具有良好的剪贴板支持。

假设您复制并粘贴少量文本。在大多数情况下,在Windows或Linux上都不会发生意外。但是现在假设您复制了一些文本,关闭了要复制的程序,然后将其粘贴。许多用户倾向于认为在复制数据时将其存储在剪贴板中,而不是在从中复制数据的程序中。在Windows上,通常是根据这种期望编写软件的(尽管从技术上讲剪贴板API的用户不应该这样做)。这在带有X的Linux上不太常见,对于大多数程序而言,正确的思维模式是复制保存指向仍然属于其复制程序的数据的指针。也就是说,如果程序关闭,则插入将不起作用。当我(非正式地)采访程序员时,他们通常对此感到惊讶,除非他们真的为他们的应用程序使用了复制+粘贴功能。当我采访非程序员时,他们通常会发现这种行为不仅令人惊讶,而且令人困惑。

将剪贴板传输到OS的缺点是复制大量数据非常昂贵。假设您复制了大量的文本,大量的千兆字节或某种复杂的对象,然后不要将其粘贴。实际上,您不希望将此数据从程序复制到OS,以便将其存储在此处并可用。 Windows明智地做到了这一点:如果认为有益,应用程序只能按需提供数据。在我们的情况下,当用户关闭程序时,它可以确定是将数据放在剪贴板上还是将其删除。在这种情况下,许多程序(例如Excel)都可以将数据“保存”到剪贴板或删除它们,这是非常合理的。

其中一些功能可以在Linux上实现。例如,ClipboardManager规范描述了保存机制,GNOME应用程序通常支持它(尽管有一些错误),但是* nix上的情况确实不同于对Windows应用程序的普遍支持,在Windows应用程序中通常实现了功能强大的剪贴板。[回来]

4.另一个例子是现代编译器之上的工具。让我们回头看一下Macilroy的示例,在此示例中,正确的Unix编译器是如此专业,以至于清单是由单独的工具完成的。但是今天,这种情况发生了变化,尽管仍然保留了单独的列表工具。一些流行的Linux编译器实际上有成千上万个选项-而且它们功能极为丰富。例如,现代的众多功能之一clang 就是静态分析。在撰写本文时,有79项静态分析的常规测试和44项实验测试。。如果这些命令是单独的命令,则它们仍将依赖于相同的基本编译器基础结构,并施加相同​​的维护负担-这些静态分析工具使用纯文本并重新定义用于此的整个编译器工具链实际上是不合理的。让他们可以执行静态分析。它们可以是单独的命令,而不是组合为clang,但它们仍将依赖于相同的机制,或者将维护负担和复杂性强加于编译器(编译器应支持在其上工作的工具具有稳定的接口),或者它们将保持不变。打破。

为了简单起见,在文本中执行所有操作听起来很美,但是实际上,如果您想做一个真正有用的工作,则通常不需要数据的文本表示。

相同的clang比1979年存在的所有编译器甚至1979年存在的所有编译器结合起来的功能都更强大,无论它是由单片命令执行还是由成千上万条较小的指令执行。可以轻松地说,在1979年,一切都变得更加简单,我们现代的程序员误入歧途。但是实际上,很难提供一种更简单并且将被所有人真正接受的设计。这样的设计不可能保留所有现有的功能和可配置性,并且像1979年一样简单。[回来]

5.自诞生以来,curl已从支持三种协议转变为40种。这是否意味着它“做40件事”?Unix理念是否要求将其划分为40个单独的命令?取决于问谁。如果每个协议都是自己的团队,由另一个人创建和支持,那么我们将与团队一样陷入困境。尽管所有这些都是文本流,但命令行参数不一致,输出格式不一致,等等。这是否使我们更接近McIlroy倡导的简单性?取决于问谁。[回来]

All Articles