一线vs脚本


25

我注意到许多问题,答案和评论,表示不屑于(有时甚至担心)编写脚本而不是单行代码。所以,我想知道:

  • 什么时候以及为什么我应该编写一个独立的脚本而不是一个“单一代码”?或相反亦然?

  • 两者的用例和利弊是什么?

  • 有些语言(例如awk或perl)是否比其他语言(例如python)更适合单行?如果是这样,为什么?

  • 只是在个人喜好问题上,还是在特定情况下有充分的理由(即客观的理由)写一个或另一个?那是什么原因

定义

  • one-liner直接在shell命令行中键入或粘贴的任何命令序列。常累及管道和/或使用的语言,如sedawkperl,和/或工具,如grepcutsort

    定义特征是在命令行上直接执行-长度和格式无关。“单行代码”可以全部在一行上,也可以有多行(例如sh for loop,或嵌入式awk或sed代码,并带有换行和缩进以提高可读性)。

  • script:使用任何解释语言的任何命令序列,这些命令序列保存到文件中,然后执行。脚本可以完全用一种语言编写,也可以是使用其他语言围绕多个“单行代码”的shell脚本包装。


我有自己的答案(我将在稍后发布),但是我希望这成为有关该主题的规范问答,而不仅仅是我个人的看法。



4
对于Python,单行代码通常不利,因为空格是语法的一部分,包括缩进和换行符。同样,它比大多数其他通用语言更冗长和明确。
wjandrea

2
我必须使用Google进行的所有操作(通常使用某些正则表达式),并且可能以某种形状或变体重复使用,我将保存在云中的脚本文件中(Dropbox,Google Cloud等)。注释包含当我再次需要它时将使用的关键字。它省去了5分钟以上的时间,重新称量了我在各种相似答案之间的选择权,避免了陷阱,或者构建了所需的变体,因为我没有找到我需要的写得很好的确切变体。
user3445853

3
@wjandrea要添加:正则表达式。Perl对它们具有特定的语法,其中包括执行操作等。在python中,您需要使用字符串文字作为参数的import和write函数调用,该调用需要更多字符。大多数单行代码使用大量正则表达式来操纵文本,因此这是一个“大问题”。
Giacomo Alzetta

1
@wjandrea Python对于单行代码来说并不是一件坏事,不是无法编写一个。平均而言,我已经能够将4-5行脚本转换为单行代码。但是正如您提到的,它比其他语言更显式(恕我直言,这是Python的优势之一)。这样一来,他们可以输入比Perl或awk(它们也不需要像Python一样导入某些库)的两倍或三倍字符数,而这正是大多数人抱怨的-一线式的长度。但是话又说回来,恕我直言,争论字符数是一件小事;如果某事有效-它有效
Sergiy Kolodyazhnyy

Answers:


33

根据实际经验的另一种回应。

如果可以“立即扔掉”可以直接在提示符下编写的代码,我将使用单线代码。例如,我可以使用以下代码:

for h in host1 host2 host3; do printf "%s\t%s\n" "$h" "$(ssh "$h" uptime)"; done

如果我认为代码值得保存,我将使用脚本。此时,我将在文件顶部添加一个描述,可能添加一些错误检查,甚至可能将其检查到代码存储库中以进行版本控制。例如,如果我决定检查一组服务器的正常运行时间是我会一次又一次使用的有用功能,则上面的单行代码可能会扩展为:

#!/bin/bash
# Check the uptime for each of the known set of hosts
########################################################################
#
hosts=(host1 host2 host3)

for h in "${hosts[@]}"
do
    printf "%s\t" "$h"
    uptime=$(ssh -o ConnectTimeout=5 -n "$h" uptime 2>/dev/null)
    printf "%s\n" "${uptime:-(unreachable)}"
done

一概而论

  • 一线

    • 为特定的一次性目的而编写的简单代码(即“几个”语句)
    • 可以在需要时快速轻松地编写代码
    • 一次性代码
  • 脚本

    • 将(可能)使用一次或两次以上的代码
    • 需要多个语句的复杂代码
    • 其他人需要维护的代码
    • 别人能理解的代码
    • 要在无人看管的情况下运行的代码(例如,来自cron

我在unix.SE上看到相当多的问题,要求使用单一代码执行特定任务。通过上面的示例,我认为第二个比第一个更易于理解,因此读者可以从中学到更多。一个解决方案可以很容易地从另一个解决方案派生出来,因此出于可读性考虑(对于将来的读者而言),我们可能应该避免将代码压缩成一行,以解决最琐碎的解决方案。


这是一个很好的实际示例,说明了如何将肮脏的单行代码演变为更健壮的脚本。
安东尼G-莫妮卡

这是一个好的开始,但还有另一种可能性。我有一些方便使用一次或两次以上的东西,但可能不在同一台机器上。我将它们保存在我的工作计算机上始终保持打开状态的文本文件中,以便可以快速将它们复制并粘贴到SSH客户端中(或Windows Server上的命令提示符)。这些不一定是“一个衬里”,我也不需要花很多精力将它们保存为“脚本”文件。我称它们为“食谱”。
Monty Harder

@MontyHarder在这种情况下,我倾向于将它们另存为脚本,然后将其存储在git repo中。然后,我可以通过简单的git clone简单地将它们安装在所需的任何计算机上。那是为了工作。对于个人用途,我永远不会在“食谱”文件中编写代码(例如,我不使用github代码片段)。我将所有脚本保存在自1997年上大学以来一直保存在$ HOME / bin目录中(在SunOS大学中)。我从小说《神经巫师》中得到了一个主意和反对者都保留个人脚本/程序作为武器的
想法

1
尽管与实际讨论肯定相切,但是在示例脚本中,您可以使用set -- host1 host2 host3then for h in "$@"(或仅使用for h),这将使脚本可以POSIX移植。:-)
wchargin

2
我喜欢使代码更易于OP和查看者学习的观点。最好在答案中同时做这两个事情,但是我发现连接一个脚本要比个人解构一个衬里要容易得多。
FreeSoftwareServers

10

在以下情况下编写脚本

  • 需要更多代码
  • 您重视可读性
  • 添加注释以显示代码的作用是必要/有用的
  • 您需要传递参数
  • 您希望脚本在其自己的环境(变量等)中运行
  • 您可能会出于一些更复杂的目的重用/修改代码

在以下情况下写单线

  • 只需要少量的代码
  • 您想访问当前shell中定义的变量
  • 您需要一个快速而肮脏的解决方案

通常,修改单线工作的内容通常很容易。脚本的“传递参数”点可能应该是“您希望将其打包以便以后使用”。我编写了“单行代码”,其中包括定义一个shell函数并对其进行调用。尽管现在我已经考虑过了,但是经过几次迭代后,这个问题已经解决了,我应该将其放入脚本中。
彼得·科德斯

9

当所需的一系列命令很短并且/或者产生的结果可以用作较大的管道​​或命令别名的一部分时,它可能是一个很好的选择。

基本上,我认为单行代码通常是经验丰富的系统管理员,既熟悉问题又使用所使用的命令,他们可能会当场编写而无需过多考虑。

当单行变得太长或涉及到比非常简单的条件或循环更多时,出于可读性考虑,通常最好将它们编写为多行脚本或shell函数。另外,如果您编写的内容要被一遍又一遍地使用,或者被其他人使用(可能会造成麻烦),那么您应该愿意花时间将解决方案写成清晰的内容,脚本形式。

在Python中,缩进是语法的一部分,因此,如果编写单行代码,则字面意义上就无法充分利用语言的功能。

Perl和awk单行通常是关于使用正则表达式的原始功能的。但是有些人将正则表达式称为仅写语言,并非完全没有道理。编写多行脚本可以使您写注释来描述特定正则表达式应该执行的操作,这在六个月之间执行其他操作之后再次查看该脚本时非常有帮助。

这本质上是一个非常基于意见的问题,因为它涉及到衡量可接受的复杂程度以及可读性和紧凑性之间的权衡。所有这些都是个人判断的问题。甚至一种语言的选择也可能取决于个人情况:如果某种特定的语言在理论上对于特定的问题是最佳的,但是您不熟悉该语言,并且已经知道如何使用其他某种语言来解决问题,则可以选择熟悉的语言,以便快速且省力地完成工作,尽管该解决方案在技术上可能效率不高。

最好的往往是足够好的敌人,这在这里非常重要。

而且,如果您熟悉Perl,您可能已经听说过TMTOWTDI:做到这一点的方法不止一种。


再加上一个提及“或shell函数”的功能
格伦·杰克曼(Glenn Jackman)

7

请注意,这是个人观点;撒一粒盐。

  1. 如果命令适合80个字符,请使用单线。长的命令行很难使用。还有重复出现的问题,请参阅#2

  2. 如果您需要多次运行命令,请使用脚本。否则,如果满足条件#1,则使用单缸套。

  3. 这是非常主观的,但是是的,我认为shell,Perl或awk是我的单行工具。
  4. 参见#1,#2,#3。

3
我喜欢到目前为止已经看到的答案。我特别喜欢您的第一点-编辑是我打算在答案中提及的内容之一-即使使用^ X ^ E,在您喜欢的编辑器中编辑脚本比在命令中进行编辑也要容易得多,也要愉快得多。线。
cas

2
尽管提出了第4点的建议,但似乎毫无意义。:)
安东尼G-莫妮卡

@cas:使用控制箭头键(或alt + f / alt + b),您可以非常快地以单线移动。尤其是如果您将关键自动重复设置设置为50 /秒,并且延迟时间很短,例如220ms。您也可以在单行内使用control-s / control-r isearch,尽管这可以带您回到其他历史上。如果您对Control-w,alt +退格键,alt + d和Ctrl-y感到满意,那么只需键盘光标移动就可以完成很多工作。
彼得·科德斯

此外,IIRC的某些shell(例如我认为的ZSH)具有一个键绑定,可以在当前命令行上调用编辑器,从而使您可以在自己喜欢的编辑器中编写“单行”,并轻松地将其放入命令历史记录中以进行进一步的升级-箭头和编辑。(相对于脚本中的命令行选项,能够修改它以便重新运行使单行代码变得很棒。)IIRC,bash也具有外部编辑,但默认情况下不绑定任何内容。
彼得·科德斯

@PeterCordes快速移动光标甚至无法与在vi / vim之类的不错的编辑器中可以进行的操作进行比较。顺便说一句,^ X ^ E在bash中调用$ EDITOR或$ VISUAL(以及其他一些shell。我认为zsh也是可配置的)。通过插入换行符和缩进,这是一种将我创建的难以理解的混乱变成可理解的好方法-如果没有它们,很容易迷失在嵌套的花括号或括号中。顺便说一句,我的终端窗口超过250个字符宽,因此即使没有包裹,我的单线纸也可以很长。
cas

7

我查看并使用单行代码作为临时开发工具来重复编辑,直到它完成我想要的操作为止,或者我了解子命令的某些细微之处是如何工作的。

然后,它要么在我正在调查的主题的文本文件中被记录(或注释),要么被整理到脚本中,该脚本被放入我的个人bin路径中,可能用于进一步的优化,参数化等。通常,即使没有进行其他更改,也可以将一行分解为更具可读性的脚本,否则我将不再使用该脚本。

我将外壳程序历史记录设置为超过5000行,每个终端以及每次远程登录时都有单独的历史记录,依此类推。我讨厌在这些历史记录中找不到我几周前制定的单行命令,并且认为我不再需要,因此我制定了策略。

有时,需要一整组命令来做某事,例如配置一些硬件。最后,我将它们全部从历史记录中复制出来,进行最小程度的清理,然后将它们添加到Shell脚本中,RUNME就像我所做的笔记一样,而且还使它们几乎随时可以被其他人再次使用。(这就是为什么我发现只能提供GUI来配置它们的工具的原因。)我发现这是令人难以置信的效率提高,因为通常您只希望执行一次操作,然后必须再执行5次。 。


1
+1俏皮话是伟大的向上箭头和编辑,对执行命令行选项的脚本来控制什么它从它的工作分别做。例如,在循环某些文件的ln单层结构中ln -s,硬链接与软链接的更改与。
Peter Cordes

5

我希望团队中的人为可能需要多次执行的任何操作(即“工作流”或“管道”)以及任何需要记录的一次性任务(通常是脚本)编写脚本例如“修改某些数据集中的某些内容以修正错误提供的数据”。除此之外,“单线”或脚本并不重要。

无法正确地记录工作流(如脚本或等效文件)会使得在项目上引入新员工更加困难,并且也难以将人员移出项目。此外,它使任何类型的审核都变得困难,并且追踪错误可能是不可能的。

这与用于编程的语言无关。

其他工作场所可能具有相似/不同的习俗或期望,甚至可能被写下来。

在家里,您可以做任何想做的事情。

例如,当我在shell中做一些不平凡的事情时,我会立即编写脚本,例如在提交对U&L问题的答案之前,或者在需要运行某个命令或命令集的各个组件之前“真实”。

我还编写了重复性任务的脚本,即使它们很简单,我也确实想每次都以相同的方式进行操作,例如

  • 更新我的OpenBSD系统和所有已安装的软件包(这归结为三个简短的命令,还有少数几个用于整理工作的命令,以简短的脚本收集),或者
  • 将我的系统备份到异地存储(本质上是一个命令,但是要进行大量的内务处理,并且该脚本还允许我在快照等之间进行区分等操作,并且它需要在不同的系统上以不同的方式工作,并且我是从cron工作运行的),或者
  • 提取邮件(再次,基本上是单个命令,但是我希望它在作为cron作业调用时以及从命令行使用它时表现不同,并且在某些情况下不应尝试获取邮件,并且会写一个记录到文件的简短日志消息)。

我使用shell函数(很少使用别名)是为了在交互式shell中提供方便,例如,默认情况下(-Fls)向某些命令添加选项,或为某些命令创建专门的变体(例如,pman这是man仅查看POSIX手册的命令) 。


5

看来我对此持少数意见。

术语“脚本”是有意混淆的,请摆脱它。即使您仅使用和,这也是您在此处编写的软件bashawk

在一个团队中,我更喜欢编写具有工具(github / gitlab / gerrit)强制执行的代码审查过程的软件。这使版本控制成为奖励。如果您有多个系统,请添加连续部署工具。如果目标系统很重要,则还有一些测试套件。我对这些不是虔诚的,但要权衡收益和成本。

如果您有一个管理团队,那么最简单的事情vim /root/bin/x.sh就是与变更相关的通信,代码可读性和跨系统分发方面的灾难。通常,一种严重的故障要比所谓的“繁重”过程花费更多的时间/金钱。

实际上,我更喜欢单线执行任何不值得“繁重”过程的事情。如果您知道如何有效地使用shell历史记录,可以通过几次击键快速重用它们。轻松粘贴在不相关的系统(例如,不同的客户)之间。

(未审阅!)单行代码与审阅的软件(“脚本”)之间的关键区别:执行单行代码时,责任完全由您承担。您永远不能对自己说:“我只是在某个地方找到了这种单线产品,我在不理解的情况下运行了它,那不是我的错” –您知道您正在运行未经审查和未经测试的软件位,所以这您的错。确保它足够小以至于可以预见结果-如果没有,请予以谴责。

有时您需要这种简单化的方法,并且将条形图设置为大约一行文本在实践中效果很好(即,已审阅代码与未审阅代码之间的边界)。我的一些单线看上去很长,但它们确实为我有效地工作。只要我嘲笑他们,并且不把它推给更多的下级同事,一切都很好。需要共享它们的那一刻-我经历了标准的软件开发过程。


1
优点:我自己比较喜欢“程序”而不是“脚本”。
格伦·杰克曼(Glenn Jackman)

5

我必须说,我对问题第一部分的含义感到有些恐惧。Unix脚本语言是成熟的编程语言,而编程语言是语言,这意味着它们与人类语言一样具有无限的延展性和灵活性。“无限的可延展性和灵活性”的标志之一是,几乎从来没有“正确的方式”来表达某些东西-各种各样的方式,这是一件好事!因此,是的,这当然是个人喜好问题,这没有错。有人说做某事只有一种方法,

曾几何时,我自己~/bin我写“有用”小脚本。但是我意识到他们中的大多数人在我写它们的那一天就被使用了,而且再也没有使用过。因此,经过的时间越长,我的bin目录就越小。

我认为编写脚本没有任何问题。如果您想象自己或其他人可能再次使用它,请编写脚本。如果您要为此“适当地设计”一个可支持的脚本,则一定要这样做。(即使没有人使用过,编写它也是一种好习惯。)

我不再编写脚本的原因(而且,我认为更喜欢“单行”):

  • bin目录混乱确实要付出代价。我不会再把东西放在那里,除非它们真正属于那里。
  • 我使用的脚本语言是语言,我 ; 现在我真的很适应他们。每当我需要时重新键入一个衬里感觉都不像工作。我没有保留,保存和使用脚本的任务,只是为了减轻(重新)键入负担。(除其他外,在某些时候,重新键入的负担变得比记住脚本是什么的存储负担要少。)
  • 重新输入内容感觉不像是负担的另一个原因是:命令历史记录。尽管我每天都使用它们,但我并没有将很多一线脚本包含在脚本中,但是我不必重新输入它们。我只记得他们的历史。这样,它们就像是穷人的脚本或别名。

4

我相信,在大多数情况下,对“单线”的请求实际上是对“音咬”的请求,即:

在新闻业的语境中,声音叮咬的特征是简短的短语或句子,可以捕捉说话者想说的内容的本质,并用于汇总信息...

人们想要并要求提供最短的代码来展示所提出问题的解决方案。

有时,没有办法将语音减少为“声音咬合”,也可能无法将脚本减少为简短的“单线”。在这种情况下,唯一可能的答案是脚本。

而且,总的来说,“咬一口”绝不能替代整个演讲。因此,“一个班轮”通常只对传达一个想法或给出该想法的实际示例有用。然后,在理解了该想法之后,应在脚本中对其进行扩展(应用)。

因此,写一个“单线”来传达一个想法或概念。

将您的常规代码编写为完整脚本,而不是单行代码。


1

您询问“对脚本的恐惧和鄙视”。这是由于Q + A情况造成的,答案通常是这样说的:它需要这一步和这一步,但是我也可以将其放在一行中(因此您可以复制粘贴并将其用作一个很长的简单命令)。

有时,您只能猜测快速和肮脏的复制粘贴解决方案是否可以更好地满足Q的需要,或者“ Q”需要理解的正是“不错的”脚本。

这里有很多优点和缺点是正确的,但是仍然没有抓住重点。我什至会说Q中的定义没有帮助。

外壳历史记录文件是“一个内衬”,但仍被保存。bash功能operate-and-get-next (C-o)甚至可以让您半自动重复它们。

我几乎看不到提到的功能一词。这是存储“脚本”和“一个衬纸”的方式。只需几次击键,您就可以在两种格式之间切换。阿化合物命令常常可以适合两者。

history -r file是从任何文件(而不仅仅是默认历史记录文件)读取一个或多个命令行(和注释)的方法。它只需要一些最小的组织。您不应该依赖大的历史记录文件大小和历史记录搜索来检索(某些)命令行版本。

函数应该以类似的方式定义。对于初学者,请thisfunc() {...}在您的主目录中放入“功能”文件,并提供源文件。是的,这是一些开销,但这是一般的解决方案。

如果将每个命令行和每个脚本变体存储在单独的文件中,则(理论上,但客观上)浪费了文件系统块。

一线客车本身没有任何快速和肮脏的东西。更多的是复制粘贴部分,以及我们如何仅依靠命令历史为我们保存它,这是令人头疼的。


@sitaram:您的一线班轮确实具有无懈可击的功能:shebang行,换行符,四个公用程序调用-其中两个sed为“程序”。我认为原始的perl脚本看起来更好,运行得更快,并且不会因分布在60行上而浪费任何字节。Perl还可以让您挤得更多。

对我来说,这就是要避免的那种班轮。您是否想在命令行上粘贴(粘贴)?不,然后,如果无论如何将其存储在文件中(无论脚本还是函数),您都可能会投资两个或三个换行符字节以使其看起来不错。


10分钟 以后:我只是想检查我是否可以说“两个sed程序”,或者说是“两个sed替换/调用”。但是sitaram的答案不见了。我希望我的答复仍然有意义。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.