我有一个由大约 20 个小.sh
文件组成的项目。我将这些命名为“小”,因为通常没有文件有超过 20 行的代码。我采用了模块化方法,因为因此我忠于Unix 哲学,并且我更容易维护项目。
在每个.sh
文件的开头,我放了#!/bin/bash
.
简单地说,我理解脚本声明有两个目的:
- 它们帮助用户回忆执行文件所需的外壳程序(例如,几年后不使用该文件)。
- 他们确保脚本仅使用特定的 shell(在这种情况下为 Bash)运行,以防止在使用另一个 shell 时出现意外行为。
当一个项目开始从 5 个文件增长到 20 个文件或从 20 个文件增长到 50 个文件时(不是这种情况,只是为了演示),我们有 20 行或 50行脚本声明。我承认,尽管这对某些人来说可能很有趣,但对我来说使用 20 或 50 而不是每个项目只说 1 个(可能在项目的主文件中)感觉有点多余。
有没有办法通过在某个主文件中使用一些“全局”脚本声明来避免这种所谓的 20 或 50 行或更多的脚本声明行的冗余?
即使您的项目现在可能仅包含 50 个 Bash 脚本,它迟早会开始积累用其他语言(例如 Perl 或 Python)编写的脚本(因为这些脚本语言具有 Bash 所没有的好处)。
如果每个脚本中没有正确的-line,那么在不知道要使用什么解释器的情况下使用各种脚本
#!
将非常困难。是否每个脚本都从其他脚本执行并不重要,这只会将困难从最终用户转移到开发人员身上。这两组人都不需要知道脚本是用什么语言编写的才能使用它。没有 -line 且没有显式解释器执行的 Shell 脚本
#!
会以不同的方式执行,具体取决于哪个 shell 调用它们(参见例如问题Which shell interpreter runs a script with no shebang?特别是Stéphane 的回答),这不是你想要的在生产环境中(您想要一致的行为,甚至可能是可移植性)。使用显式解释器执行的脚本将由该解释器运行,无论
#!
-line 说什么。如果您决定用 Python 或任何其他语言重新实现例如 Bash 脚本,这将进一步导致问题。您应该花费这些额外的击键并始终在
#!
每个脚本中添加一个 -line。在某些环境中,每个项目的每个脚本中都有多段样板法律文本。很高兴它只是
#!
在您的项目中感觉“多余”的一条线。你误解了点
#!
。在定义的解释器下运行这个程序实际上是对操作系统的一个指令。所以一个脚本可以是
#!/usr/bin/perl
,生成的程序可以运行,./myprogram
并且可以使用 perl 解释器。类似#!/usr/bin/python
的会导致程序在 python 下运行。所以该行
#!/bin/bash
告诉操作系统在 bash 下运行这个程序。这是一个例子:
因此,尽管我的 shell 是 ksh,但由于该
#!
行,程序“x”在 bash 下运行,我们可以在进程列表中明确看到这一点。上
.sh
不好的是
.sh
文件名的末尾。想象一下,您在 python 中重写了其中一个脚本。
好吧,现在您必须将第一行更改为
#!/usr/bin/python3
这还不错,因为您还必须更改文件中的所有其他代码行。但是,您还必须将文件名从更改prog.sh
为prog.py
. 然后你必须在其他脚本中找到任何地方,以及你所有的用户脚本(你甚至不知道存在的那些),并更改它们以使用新脚本。sed -e 's/.sh/.py/g'
可能对您认识的人有所帮助。但是现在您的修订控制工具显示了许多不相关文件的更改。或者以 Unix 方式进行,并将程序命名为
prog
notprog.sh
。上
#!
如果您有正确的
#!
,并将权限/模式设置为包括执行。那么你不需要知道翻译。电脑会替你做的。对于非 gnu 系统,请阅读手册
chmod
(并学习八进制),也许还是要阅读。可能值得指出的是,这是完全错误的。hashbang 行绝不会阻止您尝试将文件提供给不正确的 shell/解释器:
awk -f array.sh
(或与等类似)但无论如何,模块化的另一种方法是在文件中定义 shell 函数,如果您愿意,每个文件一个函数,然后
source
是主程序中的文件。这样,您就不需要 hashbang:它们将被视为任何其他注释,并且所有内容都将使用相同的 shell 运行。不利的一面是您需要source
带有函数的文件(例如for x in functions/*.src ; do source "$x" ; done
),并且它们都使用相同的 shell 运行,因此您不能直接用 Perl 实现替换其中一个。有 - 它被称为可移植性或 POSIX 合规性。努力始终以可移植的、与 shell 无关的方式编写脚本,仅从使用
#!/bin/sh
或/bin/sh
交互使用的脚本中获取它们(或至少是与 POSIX 兼容的 shell,例如ksh
)。但是,可移植脚本不会让您免于:
/bin/sh
使用csh
.如果我可以表达我的观点,通过消除来“避免冗余”
#!
是错误的目标。目标应该是一致的性能,并且实际上是一个可以工作的工作脚本,并考虑极端情况,遵循某些一般规则,如not-parsing-ls或always-quoting-variables-unless-need-word-splitting。他们真的不是为了用户——他们是为了让操作系统运行正确的解释器。在这种情况下,“正确”意味着操作系统找到了 shebang 中的内容;如果它下面的代码用于错误的外壳 - 那是作者的错。至于用户记忆,我认为 Microsoft Word 或 Google Chrome 等程序的“用户”不需要知道程序是用什么语言编写的。作者可能需要。
再说一次,可移植的脚本甚至不需要记住你最初使用的 shell,因为可移植的脚本应该在任何 POSIX 兼容的 shell 中工作。
他们没有。真正防止意外行为的是编写可移植脚本。
您需要将其放在
#!/bin/bash
每个脚本顶部的原因是您将其作为单独的进程运行。当您将脚本作为新进程运行时,操作系统会看到它是一个文本文件(而不是二进制可执行文件),并且需要知道要执行什么解释器。如果您不告诉它,操作系统只能使用其默认设置(管理员可以更改)进行猜测。因此,该
#!
行(当然还有添加可执行权限)将一个简单的文本文件转换为可以直接运行的可执行文件,并且确信操作系统知道如何处理它。如果要删除该
#!
行,则您的选择是:直接执行脚本(就像您现在所做的那样),并 希望默认解释器与脚本匹配 - 您在大多数 Linux 系统上可能是安全的,但不能移植到其他系统(例如 Solaris),它可以更改为任何东西(我什至可以将它设置为
vim
在文件上运行!)。使用 执行脚本
bash myscript.sh
。这很好用,但是您必须指定脚本的路径,而且很混乱。使用获取脚本
. myscript.sh
,它在当前解释器进程中运行脚本(即不作为子进程)。但这几乎肯定需要对您的脚本进行修改,并使它们的模块化/可重用性降低。在情况 2 和 3 中,文件不需要(也不应该有)执行权限,给它一个
.sh
(或.bash
)后缀是个好主意。但是这些选项都不适合您的项目,正如您所说,您希望保持脚本模块化(=> 独立且自包含),因此您应该将您的
#!/bin/bash
行放在脚本的顶部。在您的问题中,您还说您遵循 Unix 哲学。如果这是真的,那么您应该了解该
#!
行的真正用途,并在每个可执行脚本中继续使用它!