您经常会遇到以#!/bin/bash开头的shell脚本,#!称为shebang或hashbang。shebang在shell脚本中扮演着重要的角色,特别是在处理不同类型的shell时。

在本教程中,我们将说明什么是shebang,它在shell脚本编写中如何发挥作用。以及如何为shell脚本指定解释器。我们将会使用bash与zsh这两个shell进行对比。所有shell编程的教程都可以在诸多Linux发行版中使用,比如Ubuntu,Debian,Fedora,Redhat,CentOS,Suse,Arch,Gentoo等

shell脚本中的shebang

shebang的编写是由井号#和感叹号!组成 。这个字符组合在脚本的第一行中使用时具有特殊的含义。它用于指定默认情况下运行指定脚本的解释器。

因此,如果脚本的第一行是!/bin/bash,这意味着解释器应该是bash shell。如果第一行是!/bin/zsh,这意味着要使用的解释器是 Z shell。

因为#用于shell脚本中的注释,但在这里#!有特殊含义。

shebang在shell脚本中的作用

shebang对于脚本不是强制性的。如果你写一个像这样的简单脚本:

echo "Namaste, folks!"

并授予它执行权限并使用.运算符,它将由您的登录时所使用的shell运行,即你当前使用shell。那么为什么shell脚本在脚本的开头包含#!/bin/bash行呢?

因为有多种shell可用于Linux和UNIX系统。虽然这些shell大多具有共同的语法,但它们确实有不同的语法或不同选项的处理方式。

这就是为什么在脚本中指定正确的shell解释器变得很重要,否则某些脚本在不同的shell中运行时可能会产生不同的结果。让我用一个简单的例子向你展示这一点。

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

我编写了一个包含Linux发行版数组的shell脚本示例。然后在脚本的第二行打印发行版数组中的第二个元素。

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

这时没有添加shebang行来指定任何shell解释器。这意味着当我执行此脚本时,它将由默认shell(在我的情况下为bash,echo $0打印当前使用的shell)运行:

myfreax.com@swift:~$ echo $0
bash

输出如下:

myfreax.com@swift:~$ ./arrays.sh 
Distro at index 2 is: SUSE

在索引2处打印SUSE,因为在Bash和许多其他编程和脚本语言中,数组索引从0开始。但是在Z shell中情况并非如此。在Z shell中,数组索引从1开始。

我们可以把Z shell安装自己系统上,Linux是允许系统同时存在多个shell。现在我们将在脚本添加shebang行以指定该脚本默认shell改为由Z shell运行。

#!/bin/zsh

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

输出如下:

myfreax.com@swift:~$ ./arrays.sh 
Distro at index 2 is: Fedora

现在你应该注意到不同之处。这是相同的脚本,但添加了shebang行却有所不同。

这就是为什么用shebang操作符指定正确的shell解释器很重要的原因。作为系统管理员,您可能会编写shell脚本并记住特定的shell,无论是bash、ksh还是zsh。但是您不能确定将要运行的脚本的系统与您的系统具有相同的默认shell。

如果您明确指定shell,则Shebang将被忽略

因为shebang指定了运行脚本的shell解释器。但是,您可以明确指定shell运行脚本,但在这种情况下,shebang行将被忽略。

myfreax.com@swift:~$ bash ./arrays.sh

shebang是如何工作的?

当您在脚本的第一行使用shebang时,您是在告诉shell使用指定的命令运行脚本。这就好象,#!/bin/zsh相当于:

/bin/zsh script_name

你已经知道,如果脚本的第一行以shebang开头,这意味着你指定了shell解释器。这只是部分正确。但这是shebang目的。有时候shebang行不必具有shell的可执行文件。它可以是任何东西。

例如,我将#!/bin/zsh替换为#!/bin/cat,cat命令将成为shell的解释器。

#!/bin/cat

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

这意味着现在这个脚本将使用cat命令运行并显示脚本的内容。它将输出脚本所有内容。只要它指向一个可执行命令,它就会工作。如果你随便放一些不存的命令,它会抛出错误。我将shebang行更改为:

#!/home/myfreax

显然,它不指向任何命令的可执行文件,因此它会引发解释器错误。

myfreax.com@swift:~$ cat arrays.sh 
#!/home/www.myfreax.com

distros=("Ubuntu" "Fedora" "SUSE" "Debian")
echo "Distro at index 2 is: ${distros[2]}"
myfreax.com@swift:~$ ./arrays.sh 
bash: ./arrays.sh: /home/abhishek: bad interpreter: Permission denied

结论

至此,你已经shebang行的在脚本的作用。并且你可以在shebang行中指定任意命令。如果您有任何问题或建议,请随时发表评论。