脚本与启动
本课程将介绍两个相关主题:Shell 脚本以及 Shell 启动时发生的情况。
你可能已经接触过 shell 脚本,这部分内容很快就能讲完。但 shell 启动机制是一个实用却容易出错的主题,大多数人迟早都会遇到问题,因此值得深入理解。
这节课有多重要?
这节课是课程的核心内容。
Shell 脚本
首先创建一个工作目录并进入其中:
一个 shell 脚本本质上是一组可以非交互式运行的 shell 命令。这些脚本可以是临时的一次性任务,也可以是非常复杂的程序。
Shebang(首行标记)
运行以下命令:
1echo '#!/bin/bash' > simple_script 2echo 'echo I am a script' >> simple_script
你刚刚创建了一个名为 simple_script 的文件,里面包含两行内容。第一行以两个特殊字符开头:井号和感叹号。这个组合通常被称为 shebang 或 hashbang,便于发音。当操作系统接收到一个要作为程序执行的文件时,如果发现文件开头有这两个字符,它就知道这个文件需要由另一个程序(即 解释器,而 shell 通常就被称为解释器)来运行。
现在尝试运行它:
1./simple_script
这应该会失败。在解释原因之前,先理解一下这条命令。
命令开头的 ./ 表示你要从当前工作目录中运行该文件。后面跟着的是要运行的文件名。
类似地,../ 表示你要从当前目录的上一级目录中运行文件。
如下代码:
1mkdir tmp 2cd tmp 3../simple_script # 从 tmp 子目录调用脚本 4cd .. 5rm -rf tmp
将产生与之前相同的输出。
可执行权限
上面的例子中脚本失败的原因是该文件没有被标记为 可执行,因此你会看到“权限被拒绝”的错误提示。
要修复这个问题,请运行:
1chmod +x simple_script 2./simple_script
chmod 命令用于更改文件的权限(或称 模式),从而控制哪些用户或用户组可以对文件进行 读取、写入 或 执行(即作为程序运行)操作。
注意:文件权限和所有权的主题较为复杂,这里不作完整讲解。如果你感兴趣,可以查阅
man chmod。
PATH 环境变量
如果你不加 ./ 直接运行:
1simple_script
你可能会收到“找不到文件”的错误,或者命令仍然能正常运行。
之所以会出现这种差异,是因为 PATH 环境变量的设置方式不同。
运行下面的命令查看你的 PATH 变量:
1echo $PATH
输出结果可能各不相同。例如:
1/home/imiell/perl5/bin:/opt/local/bin:/opt/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin
PATH 是一组由冒号分隔的目录。它可能非常简单,比如:
1/usr/sbin:/usr/bin
这些目录是 bash 查找命令时依次搜索的位置。
那么,如果没有手动设置 PATH,它是如何被配置的?答案是:bash 启动脚本。
但在讨论启动脚本之前,我们如何确保 simple_script 能够无需使用 ./ 前缀直接运行?
1PATH=${PATH}:. 2simple_script
就是这样!第一行将 PATH 设置为原来的值再加上当前目录(.)。这样,bash 会先查找原来 PATH 中的所有目录,最后再尝试当前目录(即本地文件夹),正如我们之前看到的那样。
启动脚本
理解启动脚本和环境变量对于解决许多调试问题至关重要!如果某个功能在一个环境中正常运行而在另一个环境中失败,通常原因就是启动脚本的不同,以及它们对环境的配置差异。
启动过程详解
当 bash 启动时,它会依次执行一系列文件来设置终端中的初始环境。如果你注意到 bash 在给出提示符前“暂停”了一下,那可能是因为启动脚本正在前台执行某个命令。
请看下图:

是的,这确实可能让人困惑。
这张图展示了不同 shell 在不同上下文下的启动脚本顺序。每种上下文通过图中不同的颜色路径表示。
我们将从顶部的 “bash” 气泡开始追踪路径,忽略 “zsh” 和 “sh” 的路径。不过值得注意的是,它们各自拥有独立的路径(如 zsh),而某些情况下(如 sh 和 bash)路径会在某些点交汇。
在图中的每个节点,所选的 shell 会根据条件决定走哪条路径(例如判断是否为 交互式 shell),或者在已确定路径后执行对应的脚本。
注意:交互式 指的是与终端连接运行的进程。如果运行的是脚本且无法输入内容,则为 非交互式。
接下来我们将逐步解析,这会让一切更清晰。
当你运行 bash 时
当你在命令行中运行 bash 时,它会走哪条路径?
-
第一步是判断 bash 是在本地运行还是作为远程服务器上的 shell 运行。
-
接着判断这是 登录 shell 还是 非登录 shell。由于你是直接运行
bash而非登录系统,应选择“非登录”。 -
最后判断 bash 是否处于交互式状态(即能否输入命令,还是在运行脚本)。你正处于交互式 shell,因此选择“交互式”。
-
然后沿着你一路选择的颜色路径继续前进:这些就是 bash 启动时会执行的文件。
-
如果某个文件不存在,系统会直接跳过,不会报错。
小心陷阱
为了进一步增加复杂性,这些脚本之间可能会相互调用,如果你只相信图表而不深入分析,很容易被误导。所以务必保持警惕!
source 内建命令
在理解了内建命令、shell 脚本和环境之后,现在介绍另一个重要的内建命令:source。
1MYVAR=Hello 2echo 'echo $MYVAR' > simple_echo # 编写一个输出变量的脚本 3chmod +x simple_echo # 使脚本可执行 4./simple_echo # 运行脚本,此时无输出,因为 MYVAR 未导出 5source simple_echo # 使用 source 执行脚本,此时能正确输出 MYVAR 的值
从上面的例子可以看出,source 是在当前 shell 环境中执行脚本的。
因此,如果脚本中包含 exit,那么通过 source 执行的脚本会直接退出你所在的 shell,而不是仅仅退出脚本本身!
如果你没完全理解上述脚本在被 source 时的行为,请重新运行一遍,并仔细思考每条命令的作用。
注意:大多数 shell 脚本以
.sh为后缀,但这并非强制要求——操作系统并不关心文件扩展名。
避免启动脚本
有时你需要以最原始的状态启动 bash,跳过所有启动脚本,以便进行调试或测试核心 bash 功能,这非常有用。
要实现这一点,请运行:
1env -i bash --noprofile --norc
env 是一个操作环境的程序。-i 标志的作用是清空当前环境变量,这意味着传给新 bash 的任何导出变量都不会被继承。我们还为 bash 添加了两个标志:--noprofile 表示不加载系统级的 bash 启动文件,--norc 表示不加载用户主目录中的个人启动文件。
最终效果是,你的 shell 只保留极少量的环境变量:
1env
由于环境已被清空,你应该只能看到很少几个变量。
你学到了什么
- 什么是 ‘shebang’
- 如何创建并运行一个 shell 脚本
PATH环境变量的意义- bash 启动时发生了什么
- 内建命令
source的作用
下一步
接下来学习 命令替换,它允许你轻松地将命令的输出捕获到变量中,供后续使用。
练习题
1)在上方终端中,回顾 bash 启动时经过的所有脚本。阅读并尝试理解它们的功能。如果有些部分不明白,可以通过查阅 man bash 来弄清楚。
2)查看图中其他存在的文件,按第 1 题的方式逐一分析。
Quiz
Bash脚本必须以 .sh 为后缀。正确还是错误?