AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 421633
Accepted
user9303970
user9303970
Asked: 2018-02-04 04:49:33 +0800 CST2018-02-04 04:49:33 +0800 CST 2018-02-04 04:49:33 +0800 CST

太多的shebang(脚本声明)行---有什么方法可以减少它们的数量?

  • 772

我有一个由大约 20 个小.sh文件组成的项目。我将这些命名为“小”,因为通常没有文件有超过 20 行的代码。我采用了模块化方法,因为因此我忠于Unix 哲学,并且我更容易维护项目。

在每个.sh文件的开头,我放了#!/bin/bash.

简单地说,我理解脚本声明有两个目的:

  1. 它们帮助用户回忆执行文件所需的外壳程序(例如,几年后不使用该文件)。
  2. 他们确保脚本仅使用特定的 shell(在这种情况下为 Bash)运行,以防止在使用另一个 shell 时出现意外行为。

当一个项目开始从 5 个文件增长到 20 个文件或从 20 个文件增长到 50 个文件时(不是这种情况,只是为了演示),我们有 20 行或 50行脚本声明。我承认,尽管这对某些人来说可能很有趣,但对我来说使用 20 或 50 而不是每个项目只说 1 个(可能在项目的主文件中)感觉有点多余。

有没有办法通过在某个主文件中使用一些“全局”脚本声明来避免这种所谓的 20 或 50 行或更多的脚本声明行的冗余?

shell-script shebang
  • 6 6 个回答
  • 2037 Views

6 个回答

  • Voted
  1. Best Answer
    Kusalananda
    2018-02-04T05:26:38+08:002018-02-04T05:26:38+08:00

    即使您的项目现在可能仅包含 50 个 Bash 脚本,它迟早会开始积累用其他语言(例如 Perl 或 Python)编写的脚本(因为这些脚本语言具有 Bash 所没有的好处)。

    如果每个脚本中没有正确的-line,那么在不知道要使用什么解释器的情况下使用各种脚本#!将非常困难。是否每个脚本都从其他脚本执行并不重要,这只会将困难从最终用户转移到开发人员身上。这两组人都不需要知道脚本是用什么语言编写的才能使用它。

    没有 -line 且没有显式解释器执行的 Shell 脚本#!会以不同的方式执行,具体取决于哪个 shell 调用它们(参见例如问题Which shell interpreter runs a script with no shebang?特别是Stéphane 的回答),这不是你想要的在生产环境中(您想要一致的行为,甚至可能是可移植性)。

    使用显式解释器执行的脚本将由该解释器运行,无论#!-line 说什么。如果您决定用 Python 或任何其他语言重新实现例如 Bash 脚本,这将进一步导致问题。

    您应该花费这些额外的击键并始终在#!每个脚本中添加一个 -line。


    在某些环境中,每个项目的每个脚本中都有多段样板法律文本。很高兴它只是#!在您的项目中感觉“多余”的一条线。

    • 19
  2. Stephen Harris
    2018-02-04T04:59:15+08:002018-02-04T04:59:15+08:00

    你误解了点#!。在定义的解释器下运行这个程序实际上是对操作系统的一个指令。

    所以一个脚本可以是#!/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 下运行,我们可以在进程列表中明确看到这一点。

    • 11
  3. ctrl-alt-delor
    2018-02-04T08:41:49+08:002018-02-04T08:41:49+08:00

    上.sh

    不好的是.sh文件名的末尾。

    想象一下,您在 python 中重写了其中一个脚本。

    好吧,现在您必须将第一行更改为#!/usr/bin/python3这还不错,因为您还必须更改文件中的所有其他代码行。但是,您还必须将文件名从更改prog.sh为prog.py. 然后你必须在其他脚本中找到任何地方,以及你所有的用户脚本(你甚至不知道存在的那些),并更改它们以使用新脚本。sed -e 's/.sh/.py/g'可能对您认识的人有所帮助。但是现在您的修订控制工具显示了许多不相关文件的更改。

    或者以 Unix 方式进行,并将程序命名为prognot prog.sh。

    上#!

    如果您有正确的#!,并将权限/模式设置为包括执行。那么你不需要知道翻译。电脑会替你做的。

    chmod +x prog #on gnu adds execute permission to prog.
    ./prog #runs the program.
    

    对于非 gnu 系统,请阅读手册chmod(并学习八进制),也许还是要阅读。

    • 9
  4. ilkkachu
    2018-02-04T07:33:53+08:002018-02-04T07:33:53+08:00

    [hashbang 行]确保脚本仅使用某个 shell(在这种情况下为 Bash)运行,以防止在使用另一个 shell 时出现意外行为。

    可能值得指出的是,这是完全错误的。hashbang 行绝不会阻止您尝试将文件提供给不正确的 shell/解释器:

    $ cat array.sh
    #!/bin/bash
    a=(a b c)
    echo ${a[1]} $BASH_VERSION
    $ dash array.sh
    array.sh: 2: array.sh: Syntax error: "(" unexpected
    

    awk -f array.sh(或与等类似)


    但无论如何,模块化的另一种方法是在文件中定义 shell 函数,如果您愿意,每个文件一个函数,然后source是主程序中的文件。这样,您就不需要 hashbang:它们将被视为任何其他注释,并且所有内容都将使用相同的 shell 运行。不利的一面是您需要source带有函数的文件(例如for x in functions/*.src ; do source "$x" ; done),并且它们都使用相同的 shell 运行,因此您不能直接用 Perl 实现替换其中一个。

    • 8
  5. Sergiy Kolodyazhnyy
    2018-02-04T09:48:09+08:002018-02-04T09:48:09+08:00

    有没有办法通过在某个主文件中使用一些“全局”脚本声明来避免这种所谓的 20 或 50 行或更多的脚本声明行的冗余?

    有 - 它被称为可移植性或 POSIX 合规性。努力始终以可移植的、与 shell 无关的方式编写脚本,仅从使用#!/bin/sh或/bin/sh交互使用的脚本中获取它们(或至少是与 POSIX 兼容的 shell,例如ksh)。

    但是,可移植脚本不会让您免于:

    • 需要使用其中一个 shell 的功能(ilkkachu 的回答就是一个例子)
    • 需要使用比 shell 更高级的脚本语言(Python 和 Perl)
    • PEBKAC 错误:您的脚本落入用户 J.Doe 手中,该用户未按照您的预期运行您的脚本,例如他们/bin/sh使用csh.

    如果我可以表达我的观点,通过消除来“避免冗余”#!是错误的目标。目标应该是一致的性能,并且实际上是一个可以工作的工作脚本,并考虑极端情况,遵循某些一般规则,如not-parsing-ls或always-quoting-variables-unless-need-word-splitting。

    它们帮助用户回忆执行文件所需的外壳程序(例如,几年后不使用该文件)。

    他们真的不是为了用户——他们是为了让操作系统运行正确的解释器。在这种情况下,“正确”意味着操作系统找到了 shebang 中的内容;如果它下面的代码用于错误的外壳 - 那是作者的错。至于用户记忆,我认为 Microsoft Word 或 Google Chrome 等程序的“用户”不需要知道程序是用什么语言编写的。作者可能需要。

    再说一次,可移植的脚本甚至不需要记住你最初使用的 shell,因为可移植的脚本应该在任何 POSIX 兼容的 shell 中工作。

    他们确保脚本仅使用特定的 shell(在这种情况下为 Bash)运行,以防止在使用另一个 shell 时出现意外行为。

    他们没有。真正防止意外行为的是编写可移植脚本。

    • 4
  6. Laurence Renshaw
    2018-02-09T17:48:58+08:002018-02-09T17:48:58+08:00

    您需要将其放在#!/bin/bash每个脚本顶部的原因是您将其作为单独的进程运行。

    当您将脚本作为新进程运行时,操作系统会看到它是一个文本文件(而不是二进制可执行文件),并且需要知道要执行什么解释器。如果您不告诉它,操作系统只能使用其默认设置(管理员可以更改)进行猜测。因此,该#!行(当然还有添加可执行权限)将一个简单的文本文件转换为可以直接运行的可执行文件,并且确信操作系统知道如何处理它。

    如果要删除该#!行,则您的选择是:

    1. 直接执行脚本(就像您现在所做的那样),并 希望默认解释器与脚本匹配 - 您在大多数 Linux 系统上可能是安全的,但不能移植到其他系统(例如 Solaris),它可以更改为任何东西(我什至可以将它设置为vim在文件上运行!)。

    2. 使用 执行脚本bash myscript.sh。这很好用,但是您必须指定脚本的路径,而且很混乱。

    3. 使用获取脚本. myscript.sh,它在当前解释器进程中运行脚本(即不作为子进程)。但这几乎肯定需要对您的脚本进行修改,并使它们的模块化/可重用性降低。

    在情况 2 和 3 中,文件不需要(也不应该有)执行权限,给它一个.sh(或.bash)后缀是个好主意。

    但是这些选项都不适合您的项目,正如您所说,您希望保持脚本模块化(=> 独立且自包含),因此您应该将您的#!/bin/bash行放在脚本的顶部。

    在您的问题中,您还说您遵循 Unix 哲学。如果这是真的,那么您应该了解该#!行的真正用途,并在每个可执行脚本中继续使用它!

    • 1

相关问题

  • 在awk中的两行之间减去相同的列

  • 打印文件行及其长度的脚本[关闭]

  • 通过命令的标准输出以编程方式导出环境变量[重复]

  • 按分隔符拆分并连接字符串问题

  • MySQL Select with function IN () with bash array

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    ssh 无法协商:“找不到匹配的密码”,正在拒绝 cbc

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    如何卸载内核模块“nvidia-drm”?

    • 13 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Wong Jia Hau ssh-add 返回:“连接代理时出错:没有这样的文件或目录” 2018-08-24 23:28:13 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST
  • Martin Hope
    Bagas Sanjaya 为什么 Linux 使用 LF 作为换行符? 2017-12-20 05:48:21 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve