暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Linux shell 脚本中的 #! 是什么?shebang 是什么?

TIAP 2022-09-01
2644

我们在 shell 脚本中,经常看到以下内容作为开头:

    #!/bin/bash

    这里 #! 被称为 shebang 或者 hasbang。shebang 在 shell 脚本中起着重要的作用,特别是在处理不同类型的 shell 时。

    本文将介绍:

    1)什么是 shebang;

    2)它如何在shell脚本中扮演重要角色。


    shell脚本中的shebang是什么

    shebang 是指的符号 #! ,这种字符的组合在脚本第一行中使用时具有特殊意义,它用于指定默认情况下运行给定脚本的解释器。

    所以,如果脚本的第一行是:

      #!/bin/bash

      这意味着解释器应该是 bash shell。如果第一行是:

        #!/bin/zsh

        这表示要使用的解释器是 Z shell


        在这里 #! 的用法当然是特殊的。因为 # 在 shell 脚本中是用于注释,在这里它有特殊的含义。


        为什么 shebang 在 shell 脚本中很重要?

        是这样的,shebang 后跟的 shell 可执行文件对于脚本来说不是强制性的。

        如果我们写了这样一个简单的脚本:

          echo "Hello,TIAP!"

          赋予执行权限,使用 . 运行它,它将由当前登录的 shell 运行:

            $ cat sample 
            echo "Hello,TIAP!"
            $ chmod u+x sample
            $ ./sample
            Hello,TIAP!


            那么,为什么还要在 shell 脚本中添加 #!/bin/bash 这一行作为开头呢?

            因为在 Linux 或者 Unix 系统中,有多个 shell 可用。虽然这些 shell 大多具有相同的语法,但它们还是有一些差别,在某些语法中有不同的处理方式

            所以,我们需要在脚本中指定正确的 shell 解释器。否则的话,某些脚本在不同的 shell 中运行,可能就会产生不同的结果。

            下面我们来举个例子。


            使用 shebang 指定 shell 解释器的重要性

            我写了一个示例脚本,内容是将几个 Linux 发行版本的名称放到一个数组中,然后打印出数组中第2个元素。

              distros=("Ubuntu" "Fedora" "SUSE" "Debian")
              echo "Distro at index 2 is: ${distros[2]}"

              我没有添加 shebang 行来指定任何 shell 解释器。这意味着当我执行这个脚本时,它将由默认shell(在我们的例子中是bash)运行。

              以下为输出:

                $ cat arrays.sh
                distros=("Ubuntu" "Fedora" "SUSE" "Debian")
                echo "Distro at index 2 is: ${distros[2]}"
                $ echo $0
                bash
                $ ./arrays.sh
                Distro at index 2 is: SUSE

                在上面例子中,数组的第2个元素输出显示为 SUSE,因为在 bash 和许多其他编程和脚本语言中,数组索引是从 0 开始的。但是在 Z shell 中不是,Z shell 中数组的索引是从1开始

                我在系统中安装了 Z shell,然后更改脚本,在第一行添加 shebang,并指定脚本由 Z shell 运行:

                  #!/bin/zsh


                  distros=("Ubuntu" "Fedora" "SUSE" "Debian")
                  echo "Distro at index 2 is: ${distros[2]}"

                  以下为输出:

                    $ cat arrays.sh
                    #!/bin/zsh


                    distros=("Ubuntu" "Fedora" "SUSE" "Debian")
                    echo "Distro at index 2 is: ${distros[2]}"
                    $ ./arrays.sh
                    Distro at index 2 is: Fedora


                    看到区别了吧,相同的脚本却有不同的输出,这就是为什么要添加 shebang 来指定解释器的原因。作为系统管理员,在编写脚本的时候知道使用的是哪个 shell,但是不能确定脚本的运行环境是不是与编写环境使用相同的默认shell,所以,需要指定一个 shell。


                    如果运行时指定了 shell,那么 shebang 将会被忽略

                    前文中为什么要强调“默认”shell 呢?因为 shebang 指定了运行脚本的解释器。

                    但是,在运行的时候可以显示的指定 shell,这种情况下,shebang 将会被忽略:

                      $ ./arrays.sh
                      Distro at index 2 is: Fedora
                      $ bash arrays.sh
                      Distro at index 2 is: SUSE

                      shebang 是怎样工作的?

                      当你在脚本的第一行使用 shebang 时,就是在告诉 shell 使用指定的命令运行脚本。

                      基本上,#/bin/zsh 相当于:

                        /bin/zsh script_name


                        我们前文中说过,如果脚本中第一行写了 shebang,这就意味着已经指定了 shell 解释器。

                        这其实是部分正确。事实上,这就是 shebang 存在的目的。但是 shebang 这一行不一定非要有可执行的 shell,它可以是任何东西。

                        比如,我们使用 #!/bin/cat 来代替 #!/bin/zsh/bin/catcat 命令的可执行文件。

                          #!/bin/cat
                          distros=("Ubuntu" "Fedora" "SUSE" "Debian")
                          echo "Distro at index 2 is: ${distros[2]}"


                          那现在这个脚本将使用 cat 命令运行,并显示脚本的内容:

                            $ cat arrays.sh
                            #!/bin/cat


                            distros=("Ubuntu" "Fedora" "SUSE" "Debian")
                            echo "Distro at index 2 is: ${distros[2]}"
                            $ ./arrays.sh
                            #!/bin/cat
                            distros=("Ubuntu" "Fedora" "SUSE" "Debian")
                            echo "Distro at index 2 is: ${distros[2]}"

                            只要它指定的是可执行命令,那么它就会正常运行。如果你放的是其他一些随机的东西,那么就会报错。

                            比如,我在 shebang 一行这样写:

                              #!/home/tiap

                              很显然,它指向的并不是一个可执行文件,因此会抛出一个错误的解释器错误:

                                $ cat arrays.sh 
                                #!/home/tiap
                                distros=("Ubuntu" "Fedora" "SUSE" "Debian")
                                echo "Distro at index 2 is: ${distros[2]}"
                                $ ./arrays.sh
                                bash: ./arrays.sh: home/tiap: bad interpreter: Permission denied

                                最后,我们再来明确几个注意事项:

                                1)在 # ! 之间没有空格,不能这样写:# !/bin/bash;

                                2)大多数系统允许 #! bin/bash 之间有空格,但是,作为一个好的习惯,还是不要#! /bin/bash 之间添加空格;

                                3)#! 必须放在第一行,否则,shebang 将会被认为是一个注释,最好在它之前也不要有空行。


                                以上就是本次分享全部内容,欢迎讨论。


                                文章转载自TIAP,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                评论