单线 vs 脚本

cas*_*cas 27 scripting conventions

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

  • 我何时以及为什么应该编写独立脚本而不是“单行脚本”?或相反亦然?

  • 两者的用例和优缺点是什么?

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

  • 这只是个人喜好问题还是在特定情况下编写一个或另一个有很好的(即客观的)理由吗?这些原因是什么?

定义

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

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

  • script: 任何解释性语言中的任何命令序列,它们被保存到一个文件中,然后被执行。脚本可能完全用一种语言编写,也可能是使用其他语言的多个“单行”的外壳脚本包装器。


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

roa*_*ima 34

另一种基于实践经验的回应。

如果它是我可以直接在提示符下编写的“扔掉”代码,我会使用单行代码。例如,我可能会使用这个:

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

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

#!/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
Run Code Online (Sandbox Code Playgroud)

概括地说,可以说

  • 单线

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

    • 将(可能)使用不止一次或两次的代码
    • 需要不止“几个”语句的复杂代码
    • 需要由他人维护的代码
    • 其他人可以理解的代码
    • 无人值守运行的代码(例如,来自cron

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

  • 我喜欢让代码对 OP 和观众更易读的观点。最好在一个答案中同时做这两个,但我发现连接脚本与个人解构单行更容易。 (2认同)

dr_*_*dr_ 11

在以下情况下编写脚本

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

在以下情况下编写单行

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


tel*_*coM 9

当所需的一系列命令相当短和/或产生的结果可以用作更大管道的一部分或命令别名时,它可能是一个很好的单行。

基本上,我认为one-liners通常是熟悉问题和使用的命令的经验丰富的系统管理员可能会在现场编写的东西,没有太多思考。

当单行代码变得过长或涉及的不仅仅是非常简单的条件或循环时,为了可读性,通常最好将它们编写为多行脚本或 shell 函数。另外,如果你写的东西会被反复使用,或者会被其他人使用(并且可能会出现问题),你应该愿意花时间把解决方案写成一个清晰的(呃),评论说,脚本形式。

在 Python 中,缩进是语法的一部分,因此如果您编写单行代码,您实际上无法充分利用该语言的功能。

Perl 和 awk 单行程序通常是关于使用正则表达式的原始功能。但有些人称正则表达式为只写语言,并非完全没有道理。编写多行脚本允许您在注释中编写描述特定正则表达式应该做什么的内容,这将在您再次查看脚本时非常有帮助,在此之间做了六个月的其他事情。

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

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

如果您熟悉 Perl,您可能已经听说过 TMTOWTDI:有不止一种方法可以做到。


sch*_*iba 8

请注意,这是个人意见;把它和一粒盐一起吃。

  1. 如果命令在 80 个字符以内,请使用单行。长命令行很难使用。还有重复的问题,见#2

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

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

  • 我喜欢到目前为止我看到的答案。我特别喜欢你的第一点——编辑是我打算在我的回答中提到的事情之一——即使使用 ^X^E,在你最喜欢的编辑器中编辑脚本比在命令上编辑更容易和愉快得多-线。 (3认同)
  • 尽管第 4 点似乎毫无意义,但还是赞成。:) (2认同)

meu*_*euh 7

我查看并使用单行作为临时开发工具重复编辑,直到它完成我想要的,或者我理解子命令的一些微妙之处是如何工作的。

然后它要么在我正在调查的主题的文本文件中被记录(和注释),要么被清理成一个脚本,放在我的个人 bin PATH 中,可能用于进一步细化、参数化等。通常,即使没有进行其他更改,一行也会被分解为更具可读性,或者我再也不会使用该脚本。

我将我的 shell 历史设置为超过 5000 行,每个终端都有一个单独的历史记录,以及每次登录远程等等。我讨厌无法在这些历史记录中找到我几周前开发的单行命令,并且认为我不会再需要,因此我的策略。

有时,一整组命令需要做一些事情,比如配置一些硬件;最后,我将它们全部从历史记录中复制出来,最少地清理它们,并将它们添加到 shell 脚本中,RUNME就像对我所做的事情的注释一样,而且它们几乎可以被其他人再次使用。(这就是为什么我发现只提供 GUI 来配置它们的工具如此痛苦。)我发现这在效率上有惊人的提升,因为通常你期望只做一次的事情必须再做 5 次。 .


Kus*_*nda 5

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

未能正确记录工作流程(作为脚本或等效文件)会使在项目中引入新员工变得更加困难,也使人们更难以从项目中转移出来。它还使任何类型的审计都变得困难,并且可能无法追踪错误。

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

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

在家里,你可以随心所欲。

例如,只要我在 shell 中做一些重要的事情,比如在提交 U&L 问题的答案之前,或者每当我需要在运行之前测试命令或命令集的各个组件时,我就会编写脚本“for真实的”。

我还为重复性任务编写脚本,我真的很想每次都以相同的方式执行这些任务,即使它们很简单,例如

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

我使用 shell 函数(很少使用别名)来方便交互式 shell,例如默认情况下向某些命令添加选项(-Ffor ls)或创建某些命令的特殊变体(例如,pman这是一个man仅查看 POSIX 手册的命令) .


Ste*_*mit 5

我必须说,我对问题第一部分的含义感到有些震惊。Unix 脚本语言是成熟的编程语言,编程语言就是语言,这意味着它们与人类语言一样具有无限的可塑性和灵活性。“无限延展性和灵活性”的标志之一是几乎从来没有“一种正确的方式”来表达某事——有多种方式,这是一件好事!所以,是的,当然这是个人喜好的问题,这没有错。任何说做某事只有一种方式的人,

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

我不认为写剧本有什么问题。如果您认为您或其他人可能会再次使用它,请务必编写一个脚本。如果您想为此“适当地设计”一个可支持的脚本,请务必这样做。(即使没有人使用它,编写它也是一种很好的做法。)

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

  • bin目录混乱确实有代价。我不再把东西放在那里,除非它们真的属于那里。
  • 我使用的脚本语言是语言,我; 我现在真的很容易对付他们。每当我需要的时候重新打一个单行字感觉不像是工作;我不觉得有必要保留、保存和使用脚本来减轻(重新)打字的负担。(除其他外,在某些时候,重新输入的负担变得比记住脚本是什么的记忆负担小。)
  • 重新输入东西感觉不像是负担的另一个原因是:命令历史。尽管我每天都在使用它们,但我还没有将许多单行代码作为脚本载入——但我不必重新键入它们;我只是从历史中回忆他们。那样的话,它们有点像穷人的脚本或别名。


kub*_*zyk 5

看来我对此持少数意见。

术语“脚本”是故意混淆的,摆脱它。这是您在那里编写的软件,即使您仅使用bashawk.

在团队中,我更喜欢编写具有由工具 (github/gitlab/gerrit) 强制执行的代码审查过程的软件。这给了版本控制作为奖励。如果您有多个系统,请添加持续部署工具。和一些测试套件,如果目标系统很重要。我不信奉这些,但权衡利弊。

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

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

(未经审查!)单行程序和经过审查的软件(“脚本”)之间的关键区别:执行单行程序时,责任完全在您身上。你永远不能对自己说“我刚刚在某处找到了这个单行,我在不理解的情况下运行它,接下来的不是我的错” - 你知道你正在运行未经审查和未经测试的软件,所以这你的错。确保它足够小,以便您可以预见结果 - 如果您不这样做,那就该死了。

有时您需要这种简单化的方法,并且在实践中将栏设置在大约一行文本中效果很好(即已审查和未审查代码之间的边界)。我的一些单线看起来很长,但它们确实对我有效。只要我理解它们并且我不把它推给更多的初级同事,一切都很好。在我需要分享它们的那一刻 - 我会经历标准的软件开发过程。