我注意到很多问题、答案和评论都表达了对编写脚本而不是俏皮话的蔑视(有时甚至是恐惧)。所以,我想知道:
我何时以及为什么应该编写一个独立的脚本而不是“一句台词”?或相反亦然?
两者的用例和优缺点是什么?
是否某些语言(例如 awk 或 perl)比其他语言(例如 python)更适合单行语言?如果是这样,为什么?
这只是个人喜好的问题还是在特定情况下有充分的(即客观的)理由来写其中一个或另一个?这些原因是什么?
定义
one-liner
:键入或粘贴的任何命令序列直接进入shell命令行。通常涉及管道和/或使用诸如sed
、awk
、perl
和/或诸如grep
或cut
之类的工具sort
。命令行上的直接执行是定义特征——长度和格式无关。 “单行”可能全部在一行上,也可能有多行(例如 sh for 循环,或嵌入的 awk 或 sed 代码,带有换行和缩进以提高可读性)。
script
:任何解释语言中的任何命令序列保存到文件中,然后执行。脚本可以完全用一种语言编写,也可以是使用其他语言的多个“单行代码”的 shell 脚本包装器。
我有自己的答案(稍后发布),但我希望这成为关于该主题的规范问答,而不仅仅是我个人的观点。
答案1
另一种基于实践经验的回应。
如果它是我可以在提示符下直接编写的“丢弃”代码,我会使用一行代码。例如,我可能会使用这个:
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
)
我在这里看到了很多问题UNIXSE要求一个俏皮话来执行一项特定任务。通过我上面的例子,我认为第二个比第一个更容易理解,因此读者可以从中学到更多。一种解决方案可以很容易地从另一种解决方案中派生出来,因此为了可读性(对于未来的读者),我们应该避免为除最琐碎的解决方案之外的任何解决方案提供压缩在一行中的代码。
答案2
写一个脚本什么时候:
- 需要更多代码
- 你重视可读性
- 添加注释是必要/有用的,以显示代码的作用
- 你需要传递参数
- 您希望脚本在其自己的环境(变量等)中运行
- 您可能会为了一些更复杂的目的而重用/调整代码
写一个一行什么时候:
- 只需要少量的代码
- 您想要访问当前 shell 中定义的变量
- 你需要一个快速而肮脏的解决方案
答案3
当所需的一系列命令相当短和/或产生可用作较大管道或命令别名的一部分的结果时,它可能是一个很好的单行命令。
基本上,我认为俏皮话通常是经验丰富的系统管理员熟悉问题和所使用的命令可能会在现场写下的东西,而无需太多思考。
当一行变得过长或涉及的不仅仅是非常简单的条件或循环时,通常最好将它们编写为多行脚本或 shell 函数,以提高可读性。另外,如果您编写的内容是要一次又一次使用的,或者是其他人将使用(并且可能会解决问题)的内容,您应该愿意花时间将解决方案写成清晰的(呃)评论,脚本形式。
在 Python 中,缩进是语法的一部分,因此如果您编写一行代码,您实际上无法充分利用该语言的功能。
Perl 和 awk 的单行语句通常涉及使用正则表达式的原始功能。但有些人将正则表达式称为只写语言,这并非完全没有道理。编写多行脚本允许您在注释中编写来描述特定正则表达式应该执行的操作,这在您在六个月内做了其他事情之后再次查看脚本时会非常有帮助。
这本质上是一个非常基于意见的问题,因为它涉及衡量可接受的复杂程度以及可读性和紧凑性之间的权衡;所有这些都很大程度上取决于个人判断。甚至语言的选择也可能取决于个人问题:如果某种特定语言理论上对于特定问题是最佳的,但你不熟悉它,并且已经知道如何使用其他语言解决问题,那么你可以选择熟悉的语言,以便以最小的努力快速完成工作,尽管该解决方案在技术上可能有些低效。
最好的常常是敌人够好了,这在这里非常适用。
如果您熟悉 Perl,您可能已经听说过 TMTOWTDI:有不止一种方法可以做到这一点。
答案4
我查看并使用单行代码作为临时开发工具来重复编辑,直到它满足我的要求,或者我了解子命令的某些微妙之处是如何工作的。
然后,它要么在我正在调查的主题的文本文件中得到注释(和注释),要么被清理成一个脚本,放入我的个人 bin PATH 中,可能用于进一步细化、参数化等。通常,即使没有进行其他更改,或者我再也不会使用该脚本,一行也会被分解以更具可读性。
我将 shell 历史记录设置为超过 5000 行,每个终端以及每次远程登录都有单独的历史记录,等等。我讨厌无法在这些历史中找到我几周前开发的单行命令,并且认为我不再需要,因此我的策略。
有时,需要一整组命令来执行某些操作,例如配置某些硬件;最后,我将它们全部从历史记录中复制出来,进行最低程度的清理,然后将它们添加到 shell 脚本中,RUNME
就像我所做的注释一样,而且这样它们就几乎可以被其他人再次使用了。 (这就是为什么我发现只提供 GUI 来配置它们的工具如此痛苦。)我发现这在效率方面取得了令人难以置信的收益,因为通常您希望只做一次的事情必须再做 5 次。 。