我有一个由大约 20 个小文件组成的项目.sh
。我将它们命名为“小”,因为一般来说,没有一个文件的代码超过 20 行。我采用了模块化方法,因为因此我忠于Unix哲学而且我更容易维护该项目。
在每个文件的开头.sh
,我放置了#!/bin/bash
.
简而言之,我理解脚本声明有两个目的:
- 它们帮助用户回忆执行该文件需要什么 shell(例如,在几年没有使用该文件之后)。
- 它们确保脚本仅使用特定 shell(在本例中为 Bash)运行,以防止使用另一个 shell 时出现意外行为。
当一个项目开始从 5 个文件增长到 20 个文件或从 20 个文件增长到 50 个文件时(不是这种情况,只是为了演示),我们有 20 行或 50 行线脚本声明。我承认,尽管这对某些人来说可能很有趣,但对我来说用 20 或 50 来代替说只是觉得有点多余每个项目 1 个(也许在主文件项目)。
有没有办法通过在某个主文件中使用一些“全局”脚本声明来避免这种所谓的 20 或 50 行或更多行脚本声明的冗余?
答案1
即使您的项目现在可能仅由 50 个 Bash 脚本组成,但它迟早会开始积累用 Perl 或 Python 等其他语言编写的脚本(因为这些脚本语言具有 Bash 所没有的优点)。
如果每个脚本中没有正确的#!
-line,它将是极其困难使用各种脚本而不知道要使用什么解释器。执行每个脚本并不重要来自其他脚本,这只会将困难从最终用户转移到开发人员身上。这两类人都不需要知道脚本是用什么语言编写的才能使用它。
没有#!
-line 且没有显式解释器执行的 Shell 脚本以不同的方式执行,具体取决于调用它们的 shell(例如,请参阅问题哪个 shell 解释器运行不带 shebang 的脚本?尤其是史蒂芬的回答),这不是您在生产环境中想要的(您想要一致的行为,甚至可能是可移植性)。
无论 -line#!
说什么,使用显式解释器执行的脚本都将由该解释器运行。如果您决定用 Python 或任何其他语言重新实现 Bash 脚本,这将进一步导致问题。
您应该花费额外的击键次数,并始终#!
为每个脚本添加一行。
在某些环境中,每个项目的每个脚本中都有多段样板法律文本。很高兴它只是一条#!
在您的项目中感觉“多余”的行。
答案2
你误解了重点#!
。它实际上是一个指示到操作系统在定义的解释器下运行该程序。
因此,可以编写一个脚本#!/usr/bin/perl
,生成的程序可以运行,./myprogram
并且可以使用 perl 解释器。类似的#!/usr/bin/python
会导致程序在 python 下运行。
因此该行#!/bin/bash
告诉操作系统在 bash 下运行该程序。
这是一个例子:
$ echo $0
/bin/ksh
$ cat x
#!/bin/bash
ps -aux | grep $$
$ ./x
sweh 2148 0.0 0.0 9516 1112 pts/5 S+ 07:58 0:00 /bin/bash ./x
sweh 2150 0.0 0.0 9048 668 pts/5 S+ 07:58 0:00 grep 2148
因此,尽管我的 shell 是 ksh,但由于该行,程序“x”在 bash 下运行#!
,我们可以在进程列表中明确地看到这一点。
答案3
在.sh
不好的是.sh
文件名末尾的 。
想象一下,你用python重写其中一个脚本。
好吧,现在您必须更改第一行,#!/usr/bin/python3
这并不是那么糟糕,因为您还必须更改文件中的所有其他代码行。但是,您还必须将文件名从 更改prog.sh
为prog.py
.然后,您必须在其他脚本和所有用户脚本(您甚至不知道存在的脚本)中找到所有位置,并更改它们以使用新脚本。sed -e 's/.sh/.py/g'
可能对您认识的人有帮助。但现在您的版本控制工具显示许多不相关文件的更改。
或者以 Unix 方式进行,并将程序命名为prog
not prog.sh
。
在#!
如果您有正确的#!
,请将权限/模式设置为包括执行。那么你就不需要认识口译员了。计算机会为你做这件事。
chmod +x prog #on gnu adds execute permission to prog.
./prog #runs the program.
对于非 gnu 系统,请阅读手册chmod
(并学习八进制),也许无论如何都要阅读它。
答案4
有没有办法通过在某个主文件中使用一些“全局”脚本声明来避免这种所谓的 20 或 50 行或更多行脚本声明的冗余?
有 - 它被称为可移植性或 POSIX 合规性。努力始终以可移植的、与 shell 无关的方式编写脚本,仅从使用#!/bin/sh
或 交互使用时的脚本/bin/sh
(或至少是兼容 POSIX 的 shell,例如ksh
)获取脚本。
但是,可移植脚本并不能帮助您避免:
- 需要使用其中一个 shell 的功能(伊尔卡丘的回答是一个例子)
- 需要使用比 shell 更高级的脚本语言(Python 和 Perl)
- PEBKAC 错误:您的脚本落入用户 J.Doe 手中,该用户运行您的脚本而不是你有意这样做,例如他们
/bin/sh
使用csh
.
如果我可以表达我的观点,通过消除来“避免冗余”#!
是错误的目标。目标应该是持续的性能和实际上是一个工作脚本有效并考虑极端情况,遵循某些一般规则,例如不解析 ls或者总是引用变量,除非需要分词。
它们帮助用户回忆执行该文件需要什么 shell(例如,在几年没有使用该文件之后)。
它们实际上不是为用户服务的 - 它们是为操作系统运行正确的解释器而设计的。在这种情况下,“正确”意味着操作系统找到了 shebang 中的内容;如果下面的代码是错误的 shell - 那是作者的错。至于用户记忆,我认为 Microsoft Word 或 Google Chrome 等程序的“用户”不需要知道程序是用什么语言编写的;作者可能需要。
话又说回来,可移植编写的脚本可能甚至不需要记住您最初使用的 shell,因为可移植脚本应该在任何兼容 POSIX 的 shell 中工作。
它们确保脚本仅使用特定 shell(在本例中为 Bash)运行,以防止使用另一个 shell 时出现意外行为。
他们不这样做。真正防止意外行为的是编写可移植脚本。