同一个脚本,不同的执行方式,效果可能完全不同。搞清楚这些区别,避免踩坑。
假设有个脚本 test.sh:
#!/bin/bash
echo "当前 Shell: $0"
VAR="hello"
./test.sh
需要执行权限,会启动新的子 Shell 进程执行。脚本里的变量不会影响当前 Shell。
/home/user/test.sh
和相对路径类似,只是路径写全了。适合在 cron 任务或 systemd 服务里用。
bash test.sh
sh test.sh
不需要执行权限,但会启动新的子 Shell。脚本里的变量同样不影响当前 Shell。
source test.sh
# 或者
. test.sh
注意:. 和 source 是等价的。
这种方式不会启动新的子 Shell,脚本在当前 Shell 里执行。脚本里定义的变量、函数都会保留在当前 Shell。
这是最关键的区别。看个例子:
# 脚本内容
#!/bin/bash
MY_VAR="test"
用 ./test.sh 执行:
./test.sh
echo $MY_VAR
# 输出空,变量不存在
用 source test.sh 执行:
source test.sh
echo $MY_VAR
# 输出 test
source 执行时,变量留在了当前 Shell。
加载环境变量
# 配置文件 config.sh
export JAVA_HOME=/usr/lib/jvm/java-11
export PATH=$JAVA_HOME/bin:$PATH
# 加载配置
source config.sh
加载函数库
# utils.sh
log_info() {
echo "[INFO] $1"
}
log_error() {
echo "[ERROR] $1" >&2
}
# 在其他脚本里使用
source utils.sh
log_info "开始处理"
激活虚拟环境
source venv/bin/activate
Python 虚拟环境就是用 source 激活的,因为它需要修改当前 Shell 的 PATH。
chmod +x script.sh 给所有用户加执行权限。也可以精细控制:
chmod 744 script.sh # rwxr--r--,只有所有者可执行
chmod 755 script.sh # rwxr-xr-x,所有者可写,其他人可执行
chmod 700 script.sh # rwx------,只有所有者能读写执行
查看权限:
ls -l script.sh
脚本执行时终端被占用,想后台跑怎么办?
./long_task.sh &
加个 & 就行。但关闭终端后脚本会被终止。想持久运行:
nohup ./long_task.sh > output.log 2>&1 &
nohup 让脚本忽略挂断信号,关闭终端也不影响。
脚本出问题时,可以用调试模式:
# 显示执行的每条命令
bash -x test.sh
# 检查语法错误但不执行
bash -n test.sh
# 详细模式
bash -v test.sh
或者在脚本里加:
#!/bin/bash
set -x # 开启调试
# 脚本内容
set +x # 关闭调试
脚本执行完有个退出码,0 表示成功,非 0 表示失败:
./test.sh
echo $? # 查看上一个命令的退出码
脚本里可以主动设置:
#!/bin/bash
if [ -z "$1" ]; then
echo "请提供参数"
exit 1 # 非零退出码表示失败
fi
exit 0 # 成功
退出码在脚本串联时很有用:
./step1.sh && ./step2.sh && ./step3.sh
# 前一个成功才执行下一个
| 执行方式 | 新进程 | 需要权限 | 变量保留 |
|---|---|---|---|
| ./script.sh | 是 | 需要 | 否 |
| /path/script.sh | 是 | 需要 | 否 |
| bash script.sh | 是 | 不需要 | 否 |
| source script.sh | 否 | 不需要 | 是 |
记住:source 在当前 Shell 执行,其他方式都是新开子 Shell。