脚本执行方式

同一个脚本,不同的执行方式,效果可能完全不同。搞清楚这些区别,避免踩坑。

四种执行方式

假设有个脚本 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 执行

source test.sh
# 或者
. test.sh

注意:.source 是等价的。

这种方式不会启动新的子 Shell,脚本在当前 Shell 里执行。脚本里定义的变量、函数都会保留在当前 Shell。

子 Shell vs 当前 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。

什么时候用 source

加载环境变量

# 配置文件 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  # 关闭调试

exit 退出码

脚本执行完有个退出码,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。