我注意到很多问题、答案和评论表示不屑(有时甚至害怕)编写脚本而不是单行代码。所以,我想知道:
何时以及为什么要编写独立脚本而不是“单行”?或相反亦然?
两者的用例和优缺点是什么?
某些语言(例如 awk 或 perl)是否比其他语言(例如 python)更适合单行代码?如果是这样,为什么?
这只是个人喜好问题,还是有好的(即客观的)理由在特定情况下写一个或另一个?这些原因是什么?
定义
one-liner
: 直接键入或粘贴到 shell 命令行中的任何命令序列。通常涉及管道和/或使用诸如sed
、awk
、perl
和/或工具之类的语言grep
或cut
或sort
.定义特征是在命令行上直接执行 - 长度和格式无关紧要。“单行”可能全部在一行,也可能有多行(例如 sh for 循环,或嵌入的 awk 或 sed 代码,带有换行和缩进以提高可读性)。
script
: 任何解释语言的命令序列,保存到文件中,然后执行。脚本可以完全用一种语言编写,也可以是使用其他语言的多个“单行”的外壳脚本包装器。
我有自己的答案(稍后会发布),但我希望这成为关于该主题的规范问答,而不仅仅是我的个人意见。
另一种基于实践经验的回应。
如果它是我可以在提示符下直接编写的“丢弃”代码,我会使用单行代码。例如,我可能会使用这个:
如果我认为代码值得保存,我会使用脚本。此时我会在文件顶部添加描述,可能会添加一些错误检查,甚至可能会将其检入代码存储库以进行版本控制。例如,如果我决定检查一组服务器的正常运行时间是一个有用的功能,我会一次又一次地使用,那么上面的单线可能会扩展为:
概括地说,可以说
单线
脚本
cron
)我在unix.SE上看到了相当多的问题,要求单线执行特定任务。使用我上面的例子,我认为第二个比第一个更容易理解,因此读者可以从中学到更多。一个解决方案可以很容易地从另一个解决方案派生出来,因此为了可读性(对于未来的读者),我们可能应该避免将代码挤在一行中,而不是最微不足道的解决方案。
在以下情况下编写脚本:
在以下情况下写一行:
当所需的一系列命令相当短和/或产生可用作较大管道或命令别名的一部分的结果时,它可能是一个很好的单行。
基本上,我认为单行代码通常是熟悉问题和使用的命令的经验丰富的系统管理员可能会在现场编写的东西,而无需过多思考。
当单行代码变得过长或涉及的不仅仅是非常简单的条件或循环时,通常最好将它们编写为多行脚本或 shell 函数,以提高可读性。此外,如果它是您编写的要反复使用的东西,或者将被其他人使用(并且可能会遇到麻烦)的东西,您应该愿意花时间将解决方案写成清晰(呃),评论说,脚本形式。
在 Python 中,缩进是语法的一部分,因此如果您编写单行代码,您实际上无法充分利用该语言的特性。
Perl 和 awk 单行语句通常是关于使用正则表达式的原始功能。但有些人称正则表达式为只写语言,并非完全没有理由。编写多行脚本允许您在注释中写下特定的正则表达式应该做什么,这将非常有帮助,当您再次查看该脚本时,在其间做了六个月的其他事情之后。
这本质上是一个非常基于意见的问题,因为它涉及衡量可接受的复杂程度以及可读性和紧凑性之间的权衡;所有这些都非常取决于个人判断。甚至选择一种语言也可能取决于个人问题:如果一种特定语言在理论上最适合特定问题,但您不熟悉它,并且已经知道如何用其他语言解决问题,您可以选择熟悉的语言,以便以最少的努力快速完成工作,尽管该解决方案在技术上可能有些低效。
最好的往往是足够好的敌人,这在这里非常适用。
如果您熟悉 Perl,您可能已经听说过 TMTOWTDI:有不止一种方法可以做到。
请注意,这是个人意见;带着一粒盐吃。
如果命令适合,例如 80 个字符,请使用单行。长命令行很难使用。还有重复的问题,见#2
如果您需要多次运行命令,请使用脚本。否则,只要满足条件#1,就使用单线。
我查看并使用单线作为一种临时开发工具来反复编辑,直到它完成我想要的,或者我了解子命令的一些微妙之处是如何工作的。
然后,它要么在我正在调查的主题的文本文件中被记录(和注释),要么被清理成一个放在我个人 bin PATH 中的脚本,可能用于进一步细化、参数化等。通常,即使没有进行其他更改,或者我再也不会使用该脚本,也会将这一行分解为更具可读性。
我将我的 shell 历史记录设置为超过 5000 行,每个终端都有单独的历史记录,每次远程登录等等。我讨厌无法在这些历史记录中找到我几周前开发的单行命令,并且认为我不再需要,因此我的策略。
有时,一整组命令需要做一些事情,比如配置一些硬件;最后,我将它们全部从历史记录中复制出来,进行最低限度的清理,并将它们添加到 shell 脚本
RUNME
中,就像我所做的注释一样,而且它们几乎可以被其他人再次使用。(这就是为什么我发现只提供 GUI 来配置它们的工具如此痛苦的原因。)我发现这是一个令人难以置信的效率提升,因为通常你希望只做一次的事情,然后必须再做 5 次。 .我希望我的团队中的人员为可能需要多次执行的任何操作(即“工作流”或“管道”)以及任何需要记录的一次性任务(通常在我们的案例中,诸如“修改某些数据集中的某些内容以修复错误提供的数据”之类的东西)。除此之外,“单行”或脚本并不重要。
未能正确记录工作流程(作为脚本或同等文件)会使在项目中引入新员工变得更加困难,并且也使人们更难以从项目中转移出来。它还使任何类型的审计都变得困难,并且可能无法追踪错误。
这与用于编程的语言无关。
其他工作场所可能有相似/不同的习俗或期望,甚至可能被记录下来。
在家里,你做任何你想做的事。
例如,当我在 shell 中做一些重要的事情时,我会立即编写脚本,比如在提交对 U&L 问题的答案之前,或者每当我需要在运行命令或命令集之前测试它的各个组件“for真实的”。
我还为重复性任务编写脚本,我真的希望每次都以相同的方式执行,即使它们很简单,比如
为了方便交互式 shell,我使用 shell 函数(很少使用别名),例如,默认情况下为某些命令添加选项(
-F
forls
)或创建某些命令的特殊变体(例如,pman
这里是一个man
仅查看 POSIX 手册的命令) .我必须说我对问题第一部分的含义感到有些震惊。Unix 脚本语言是成熟的编程语言,而编程语言是语言,这意味着它们与人类语言一样具有无限可塑性和灵活性。“无限延展性和灵活性”的标志之一是几乎从来没有“一种正确的方式”来表达某事——有多种方式,这是一件好事!所以,是的,这当然是个人喜好问题,这并没有错。任何说做事只有一种方法的人,
曾几何时,我自己
~/bin
写了很多“有用”的小脚本。但我意识到它们中的大多数在我写它们的那天就被使用了,而且再也没有使用过。所以时间越长,我的bin
目录就越小。我不认为写剧本有什么问题。如果您认为您或其他人可能会再次使用它,请务必编写一个脚本。如果您想为此“适当地设计”一个可支持的脚本,请务必这样做。(即使没有人使用它,编写它也是一种好习惯。)
我不再写脚本的原因(而且,我猜,更喜欢“单行”):
bin
目录混乱确实是有代价的。我不再把东西放在那里,除非它们真的属于那里。看来我对此持少数观点。
术语“脚本”故意混淆,摆脱它。这是您正在那里编写的软件,即使您只使用
bash
和awk
.在团队中,我更喜欢使用由工具(github/gitlab/gerrit)强制执行的代码审查过程来编写软件。这提供了版本控制作为奖励。如果您有多个系统,请添加持续部署工具。如果目标系统很重要,还有一些测试套件。我对这些并不虔诚,但会权衡收益和成本。
如果您有一个管理员团队,那么最简单
vim /root/bin/x.sh
的方式主要是与变更相关的沟通、代码可读性和跨系统分发方面的灾难。通常,一个严重的故障比所谓的“繁重”过程花费更多的时间/金钱。对于任何不值得“繁重”过程的东西,我实际上更喜欢单线。如果您知道如何有效地使用您的 shell 历史,它们可以通过几次按键快速重复使用;在不相关的系统(例如不同的客户)之间轻松粘贴。
(未审查!)单行代码和已审查软件(“脚本”)之间的关键区别:当您执行单行代码时,责任完全在您身上。你永远不能对自己说“我只是在某个地方发现了这个单行代码,我在不理解的情况下运行了它,接下来发生的事情不是我的错”——你知道你正在运行未经审查和未经测试的软件,所以这是你的错。确保它足够小,以至于您可以预见结果-如果您不这样做,那该死的。
有时您需要这种简单化的方法,并且将栏设置在大约一行文本在实践中效果很好(即已审核和未审核代码之间的边界)。我的一些单行字看起来长得吓人,但它们确实对我有效。只要我摸了他们,我不把它强加给更多的初级同事,一切都很好。我需要分享它们的那一刻——我经历了标准的软件开发过程。
我相信大多数时候对“单行”的要求实际上是对“声音片段”的要求,即:
人们想要并要求最短的代码来演示所提出问题的解决方案。
有时,没有办法将演讲简化为“原声片段”,也可能无法将脚本简化为简短的“单行”。在这种情况下,唯一可能的答案是脚本。
而且,一般来说,“原声”永远不能很好地替代整个演讲。因此,“一个班轮”通常只适合传达一个想法或给出该想法的实际示例。然后,在理解了想法之后,应该在脚本中对其进行扩展(应用)。
所以,写一个“单行”来传达一个想法或概念。
将您的通用代码编写为完整的脚本而不是单行代码。
您询问“对脚本的恐惧和蔑视”。这是由于 Q+A 的情况,通常答案是:它需要这一步和这个,但我也可以把它放在一行中(这样你就可以复制粘贴它并将其用作一个很长的简单命令)。
您有时只能猜测 Q 是否可以通过快速而肮脏的复制粘贴解决方案来更好地服务,或者“好的”脚本是否正是 Q 需要理解的内容。
这里的许多优点和缺点都是正确的,但它们仍然没有抓住重点。我什至会说 Q 中的定义没有帮助。
shell历史文件是“一个衬里”,但它们仍然被保存。bash 功能
operate-and-get-next (C-o)
甚至可以让您半自动地重复它们。我几乎看不到提到的功能一词。这是存储“脚本”和“单行”的方式。只需几次击键,您就可以在两种格式之间切换。复合命令通常可以同时满足两者。
history -r file
是从任何文件中读取一个或多个命令行(和注释)的方法,而不仅仅是从默认历史文件中读取。它只需要一些最小的组织。您不应该依赖大的历史文件大小和历史搜索来检索(某些)命令行版本。函数应该以类似的方式定义。对于初学者,
thisfunc() {...}
在您的主目录中放入一个文件“functions”,然后获取它。是的,这是一些开销,但它是通用解决方案。如果您将每个命令行和每个脚本变体存储在单独的文件中,那么(理论上,但客观上)浪费了文件系统块。
单线器本身没有任何快速和肮脏的东西。更令人不悦的是复制粘贴部分,以及我们如何仅依靠命令历史来为我们保存它。
@sitaram:您的一个班轮确实具有非单行功能:shebang line、newline、四个实用程序调用-其中两个是 sed“程序”。我认为原始的 perl 脚本看起来更好,运行速度更快,并且不会因为分散到 60 行而浪费任何字节。perl 还可以让你挤很多。
对我来说,这正是要避免的那种。你想把它(粘贴)在你的命令行上吗?不,然后,如果您将它存储在一个文件中(无论是脚本还是函数),您可能会投入两三个换行字节以使其看起来不错。
10 分钟。后来:我只是想检查我是否可以说“两个 sed 程序”或者它是“两个 sed 替换/调用”。但是西塔拉姆的答案已经消失了。我希望我的回答仍然有意义。