如何正确运行Shell脚本

by CUNOE, August 8, 2023

起因

我的一位朋友在 Ubuntu 通过 sh test.sh 运行一个 Shell 脚本时解释器弹出如下错误:

> test.sh: 12: Syntax error: "(" unexpected

对于这个问题,我直接让他通过 bash test.sh 来执行这个脚本便正常运行了。

下面将解释为什么在 Ubuntu 通过 sh test.sh 运行一个 Shell 脚本时解释器弹出 Syntax error

在查阅了相关资料后,可以发现现在的 Ubuntu 将 sh 这个命令通过符号链接到 /bin/dash,故这位朋友通过 sh test.sh 运行脚本时实际上是执行了 /bin/dash test.sh 这个命令,而 /bin/dash 这个解释器的语法和该脚本使用的 /bin/bash 解释器的语法并不一致,导致了解释器报语法错误。

当我们遇到这种情况时应该如何解决呢?下面将记录我对执行 Shell 脚本的理解。

运行 Shell 脚本的方式

下面将以 test.sh 脚本为例

通过可执行文件方式执行

这是一种常用的执行方式,你需要为脚本添加可执行权限,执行方式如下:

chmod +x test.sh
./test.sh

这里需要注意不要遗漏了 ./,否则系统会去 PATH 里寻找有没有叫 test.sh 的可执行程序,而只有 /bin/sbin/usr/bin 等目录在 PATH 里,当前脚本所在目录通常不在 PATH 里,所以写成 test.sh 是会找不到命令的,要用 ./test.sh 告诉系统说,就在当前目录找。

通过这种方式执行的 Shell 脚本,系统会读取脚本第一行的 #! 标记后定义的 解释器 来执行该脚本。

不知道什么是 #! 标记的请向下👇🏻翻

作为解释器参数执行

直接运行解释器,其参数就是 Shell 脚本的文件名,如

/bin/bash test.sh

通过这种方式执行的 Shell 脚本,系统将会忽略脚本第一行的 #! 标记后定义的 解释器,而使用你选择的解释器来执行,本例中即为 /bin/bash

Shell 脚本头的 #! (shebang) 标记

基础知识

首先,#! 标记是一个约定的标记。

一个正经的 Shell 脚本一般都会在其第一行的位置添加 #! 标记,并在后方跟随该脚本使用的 解释器 ,例如常见的 /bin/bash/bin/sh

这个标记将在通过 ./test.sh 方式执行时告诉系统内核需要调用的 解释器

如果标记后跟随的是 /bin/bash,则作为可执行文件执行该脚本时实际执行的命令为 /bin/bash test.sh; 如果标记后跟随的是 /bin/cat,则作为可执行文件执行该脚本时实际执行的命令为 /bin/cat test.sh

#!/usr/bin/env 又是什么鬼?

在一些脚本里面,你可能会遇到标记为 #!/usr/bin/env bash 的脚本,这种用法实际上是使用了 Unix 系统里的 env 功能。

在使用 /usr/bin/env bash 时,将会在 PATH 里寻找 bash 解释器,而后根据寻找到的第一个 bash 解释器来执行该脚本。这种使用方法将增加脚本的 可移植性,防止某些系统的 解释器 不在 /bin 文件夹下导致的无法找到 bash 而执行不了脚本。

这种使用方式可以用于编写 pythonnodejs 等脚本,即使用 #!/usr/bin/env python3#!/usr/bin/env node

一定要用 #! 标记吗?

是的,为了自己,也为了他人!