Linux命令source详解(在当前 Shell 环境中执行文件)
快速参考 source(或别名 . 点号)是 Bash 内建命令,用于在当前 Shell 进程中读取并执行指定文件中的命令。与 bash script.sh 的核心区别:source 不会启动子 Shell,因此文件中的变量赋值、函数定义、cd 等会持续影响当前 Shell 环境。这使得 source 成为加载配置文件(.bashrc、.profile)、引用库函数(lib.sh)的常用手段。
关键判断:修改了
.bashrc→ 想立刻生效?执行source ~/.bashrc而不是新开终端。
🔰 简介
| 项目 | 说明 |
|---|---|
| 命令全称 | source(含"源、起源"含义) |
| 同义命令 | .(点号,POSIX 标准) |
| 命令类型 | Bash 内建(非外部命令) |
| 所属 | bash/dash/zsh 等所有 POSIX shell 都支持 |
| 关键行为 | 当前 shell 执行,不启动子进程 |
| 主要用途 | ① 重新加载配置 ② 引入库函数 ③ 局部修改环境 |
| 退出码 | 文件最后一条命令的退出码;文件不存在则 1 |
source vs bash 执行脚本对比:
| 场景 | source script.sh / . script.sh | bash script.sh / ./script.sh |
|---|---|---|
| 启动新进程 | ❌ 不启动 | ✅ 启动子 bash |
| 修改全局变量 | ✅ 影响当前 shell | ❌ 只在子 shell 生效 |
| 函数定义 | ✅ 当前 shell 可用 | ❌ 子 shell 退出后失效 |
| cd 改目录 | ✅ 影响当前 shell | ❌ 退出后回到原目录 |
| export 变量 | ✅ 当前 shell 继承 | ❌ 子 shell 退出后失效 |
| set -e 选项 | ✅ 影响当前 shell | ❌ 子 shell 独立 |
| 性能 | 极快(无 fork) | 较慢(fork+exec) |
| 适用 | 加载配置、库函数 | 独立任务 |
📖 语法
source 文件名 [参数...] . 文件名 [参数...]
两种写法完全等价。
source更易读(POSIX 之外),.兼容性更好(POSIX 标准)。
⚙️ 选项
source是 Bash 内建命令,没有专门的选项标志。--help等支持来自 bash 自身。
| 项 | 说明 |
|---|---|
文件名 | 必需:要执行的脚本路径 |
参数... | 可选:传给脚本的位置参数($1、$2 等会被覆盖) |
source 的"选项"实际上由 被调用文件内部的 set 等控制,与 source 命令本身无关。
💡 实战示例
示例 1:重新加载 .bashrc(最经典用法)
# 1. 编辑 .bashrc $ vim ~/.bashrc # 加入: alias ll='ls -alF' alias gs='git status' export PS1='[\u@\h \W]\$ ' # 2. 当前 shell 不生效(新开终端才生效) # 3. **重新加载**(无需新开终端) $ source ~/.bashrc # 等价 $ . ~/.bashrc # 4. 验证 $ ll total 24 drwx------ 5 alice alice 4096 Jan 15 14:00 . drwxr-xr-x 1 root root 4096 Jan 14 09:00 .. -rw------- 1 alice alice 1234 Jan 15 14:00 .bashrc ... $ gs On branch main Your branch is up to date with 'origin/main'. nothing to commit, working tree clean # alias 已生效
生产环境:修改系统级配置(如 /etc/profile、/etc/bashrc)后,对所有用户新登录才生效;要立刻生效,当前用户执行 source /etc/profile,其他用户必须重新登录。
示例 2:source 与 bash 执行的根本差异
# 准备测试脚本
$ cat /tmp/test_source.sh
#!/bin/bash
export MY_VAR="set by script"
my_func() {
echo "函数被调用: $1"
}
cd /tmp
echo "当前目录: $(pwd)"
# ========== 方式 1:bash 执行(子 shell) ==========
$ pwd
/home/alice
$ bash /tmp/test_source.sh
当前目录: /tmp
$ pwd
/home/alice # ← 没变!子 shell 退出后回到原目录
$ echo $MY_VAR
# ← 空!子 shell 的 export 不会影响父 shell
$ my_func
bash: my_func: command not found # ← 函数没传过来
# ========== 方式 2:source 执行(当前 shell) ==========
$ pwd
/home/alice
$ source /tmp/test_source.sh
当前目录: /tmp
$ pwd
/tmp # ← 真的改了!
$ echo $MY_VAR
set by script # ← 变量生效了
$ my_func hello
函数被调用: hello # ← 函数可用核心结论:source 是当前 shell 执行,副作用会留下;bash 是子 shell 执行,副作用不会留下。
示例 3:source 引入库函数(项目级复用)
# ========== 1. 创建公共库文件 ==========
$ cat /opt/lib/logger.sh
#!/bin/sh
# 公共日志函数库
LOG_FILE="${LOG_FILE:-/var/log/app.log}"
log_info() {
echo "[$(date '+%F %T')] [INFO] $*" | tee -a "$LOG_FILE"
}
log_warn() {
echo "[$(date '+%F %T')] [WARN] $*" | tee -a "$LOG_FILE" >&2
}
log_error() {
echo "[$(date '+%F %T')] [ERROR] $*" | tee -a "$LOG_FILE" >&2
}
log_debug() {
[ -n "${DEBUG:-}" ] && echo "[$(date '+%F %T')] [DEBUG] $*" | tee -a "$LOG_FILE"
}
die() {
log_error "$*"
exit 1
}
# ========== 2. 在主脚本中引用 ==========
$ cat /opt/scripts/deploy.sh
#!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
LIB_DIR="${SCRIPT_DIR}/../lib"
# 引入库
source "${LIB_DIR}/logger.sh"
# 等价
. "${LIB_DIR}/logger.sh"
# 使用库函数
log_info "开始部署..."
log_debug "DEBUG 模式开启"
if ! command -v docker >/dev/null; then
die "未安装 docker"
fi
log_info "部署完成"
exit 0
# ========== 3. 运行 ==========
$ sudo /opt/scripts/deploy.sh
[2024-01-15 14:30:00] [INFO] 开始部署...
[2024-01-15 14:30:05] [INFO] 部署完成
$ cat /var/log/app.log
[2024-01-15 14:30:00] [INFO] 开始部署...
[2024-01-15 14:30:05] [INFO] 部署完成示例 4:source 传位置参数
$ cat /tmp/show_args.sh #!/bin/bash echo "第 1 个参数: $1" echo "第 2 个参数: $2" echo "第 3 个参数: $3" echo "总参数数: $#" echo "所有参数: $@" # 调用 $ source /tmp/show_args.sh apple banana cherry 第 1 个参数: apple 第 2 个参数: banana 第 3 个参数: cherry 总参数数: 3 所有参数: apple banana cherry # 注意:source 后的参数**替换**当前 shell 的 $1/$2/... # 用 `set --` 后 source 可恢复 $ set -- alpha beta gamma $ echo "原参数: $@" 原参数: alpha beta gamma $ source /tmp/show_args.sh x y 第 1 个参数: x 第 2 个参数: y 第 3 个参数: 总参数数: 2 $ echo "当前参数: $@" 当前参数: x y # ← 原参数 alpha/beta/gamma 被覆盖了! # 这是 source 的"副作用",小心使用
示例 5:source 用于环境切换(虚拟环境)
# ========== 1. Python 虚拟环境激活 ========== $ which python3 /usr/bin/python3 $ source venv/bin/activate (venv) $ which python3 /home/alice/project/venv/bin/python3 # ← 切到虚拟环境 $ source venv/bin/deactivate # 退出 $ which python3 /usr/bin/python3 # venv 的 activate 本质上是: $ cat venv/bin/activate ... VIRTUAL_ENV="/home/alice/project/venv" export VIRTUAL_ENV PATH="$VIRTUAL_ENV/bin:$PATH" export PATH # 修改 PATH 后,python3 命令优先找 venv/bin # ========== 2. 自定义环境切换 ========== $ cat ~/env/dev.sh export NODE_ENV=development export API_URL="http://localhost:8080" export DEBUG="*" export PATH="$HOME/.local/bin:$PATH" alias ll='ls -alF' $ cat ~/env/prod.sh export NODE_ENV=production export API_URL="https://api.example.com" unset DEBUG unalias ll 2>/dev/null # 切换 $ source ~/env/dev.sh $ echo $NODE_ENV development $ echo $API_URL http://localhost:8080 $ source ~/env/prod.sh $ echo $NODE_ENV production $ echo $API_URL https://api.example.com # 快速切换环境
示例 6:source 在系统脚本中的应用
# ========== 1. /etc/profile 加载链 ==========
$ cat /etc/profile
# 读所有子配置
for i in /etc/profile.d/*.sh; do
if [ -r "$i" ]; then
. "$i" # source 加载
fi
done
# 修改 /etc/profile.d/myapp.sh 后:
# 当前用户重新登录才生效
# 立即生效:
$ source /etc/profile
# ========== 2. .bashrc 加载链(bash 启动) ==========
$ cat ~/.bashrc
# 通常会先 source 全局配置
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
# 然后加载用户配置
if [ -f ~/.bash_aliases ]; then
. ~/.bash_aliases
fi
# ========== 3. Makefile 加载环境变量 ==========
$ cat Makefile
include .env
# .env 文件中 export 变量
# 但 Makefile 不直接 source .env(会展开变量)
# 改用 shell source
$ cat Makefile
include .env
all:
bash -c "source .env && ./build.sh"
# 或
source .env && ./build.sh
# ========== 4. Docker entrypoint 用 source ==========
$ cat entrypoint.sh
#!/bin/sh
# 等待数据库就绪
. /wait-for-db.sh
# 加载应用配置
source /app/config/env.sh
exec "$@"示例 7:source vs ./script.sh 的 shell 变量作用域
# ========== 详细测试 ========== # 准备脚本 $ cat /tmp/test_var.sh #!/bin/bash # 测试变量在父子 shell 的传递 export EXPORTED_VAR="exported_value" NORMAL_VAR="normal_value" # 不 export GLOBAL_VAR="global_value" # 不 export 也不带 local echo "在脚本中:" echo " EXPORTED_VAR=$EXPORTED_VAR" echo " NORMAL_VAR=$NORMAL_VAR" echo " GLOBAL_VAR=$GLOBAL_VAR" # 测试 1:bash 子 shell 执行 $ echo "--- bash 子 shell ---" $ EXPORTED_VAR="parent" $ NORMAL_VAR="parent" $ GLOBAL_VAR="parent" $ bash /tmp/test_var.sh 在脚本中: EXPORTED_VAR=exported_value # ← 看到的是脚本自己的 NORMAL_VAR=normal_value GLOBAL_VAR=global_value $ echo "回到父 shell:" $ echo " EXPORTED_VAR=$EXPORTED_VAR" # parent(父 shell 的原值) $ echo " NORMAL_VAR=$NORMAL_VAR" # parent $ echo " GLOBAL_VAR=$GLOBAL_VAR" # parent # 全部是父 shell 的原值,**子 shell 没影响父** # 测试 2:source 当前 shell 执行 $ echo "--- source 当前 shell ---" $ EXPORTED_VAR="parent" $ NORMAL_VAR="parent" $ GLOBAL_VAR="parent" $ source /tmp/test_var.sh 在脚本中: EXPORTED_VAR=exported_value NORMAL_VAR=normal_value GLOBAL_VAR=global_value $ echo "回到父 shell:" $ echo " EXPORTED_VAR=$EXPORTED_VAR" # exported_value ← 改了! $ echo " NORMAL_VAR=$NORMAL_VAR" # normal_value ← 改了! $ echo " GLOBAL_VAR=$GLOBAL_VAR" # global_value ← 改了! # 全部被脚本内的赋值**覆盖**
示例 8:source 的安全考量
# ========== 1. 加载未知脚本的风险 ==========
# source 执行**全部命令**,包括 rm、curl 等危险操作
$ source /tmp/unknown.sh
# 等价于把这些命令**粘贴到当前 shell** 执行
# ⚠️ 不要 source 不可信来源的脚本
# ========== 2. 隔离测试 ==========
# 安全做法:先看内容再 source
$ cat /tmp/unknown.sh
$ source /tmp/unknown.sh # 确认无恶意再执行
# 或用 subshell 隔离
$ bash /tmp/unknown.sh # 副作用不影响当前 shell
# ========== 3. shellcheck 静态分析 ==========
$ shellcheck /tmp/unknown.sh
# 显示潜在问题
# ========== 4. 防止 source 破坏 $@ ==========
$ cat /tmp/preserve_args.sh
#!/bin/bash
# 保留原始位置参数
ORIG_ARGS=("$@")
# 临时清空位置参数
set --
# 执行 source 的目标脚本
source /tmp/target.sh
# 恢复
set -- "${ORIG_ARGS[@]}"
# ========== 5. 命名空间隔离 ==========
# source 时函数和变量**会污染**当前 shell
# 解决:用 (subshell) 隔离
$ (source /tmp/script.sh) # 在 subshell 中 source
# 副作用**不会影响**当前 shell
# ========== 6. 避免循环 source ==========
# ~/.bashrc 中常见错误:
$ cat ~/.bashrc
source /etc/profile # ← 如果 /etc/profile 也 source .bashrc 会**死循环**!
# 解决:只 source 必要的文件,避免双向引用⚠️ 注意事项
1. 副作用会留下source 修改的变量、函数、cd、ulimit、umask 等全部持续到当前 shell 结束。
调试:可用 (source script.sh) 在 subshell 中执行,副作用自动丢弃。
2. set -e 的传播source 进来的脚本中 set -e / set -u 会影响当前 shell。
解决:在被 source 的脚本首尾用 (set +e +u; source lib.sh; set -e -u) 隔离。
3. set -- 在 source 中覆盖位置参数
如示例 4 所示,source 后的 $1/$2/.../$@ 会被传进来的参数覆盖。
加载库函数时避免传位置参数给库。
4. 信号处理(trap)
source 进来的 trap 会影响当前 shell。
cat /tmp/lib.sh trap 'echo trapped' INT source /tmp/lib.sh # Ctrl+C 现在会触发 "trapped",且**当前 shell 也被影响**
5. set -e 在 source 函数中的边界set -e 对函数内的失败会终止函数(不退出 shell),但对函数外的命令失败会退出。
source 一个含 set -e 的脚本会修改整个 shell 的 set 状态。
6. export 与 source 顺序export 只在赋值时标记为"传子进程"。当前 shell 的变量可见性不依赖 export。
VAR="x" # 当前 shell 可见 export VAR # 标记为传子进程 echo $VAR # x source /tmp/foo # foo 中也能看到 VAR(无需 export)
7. 错误处理 -e 失效set -e 对 source 中循环内的失败不生效:
set -e source /tmp/script.sh # script.sh 内的 for 循环中失败不会让当前 shell 退出
8. 别名作用域alias 仅在交互式 shell 中展开。source 进来的 alias 在非交互式调用中不展开。
cat /tmp/aliases.sh alias ll='ls -la' bash -c 'source /tmp/aliases.sh; ll' # ll 不展开,alias 失效
9. PATH 不会自动 sourcesource 不会自动把当前目录加入 PATH。./script.sh 与 source ./script.sh 都不需要 PATH 修改。
10. POSIX 标准
POSIX 严格只支持 . filename,source 是 Bash 扩展。
可移植脚本用 .:source .bashrc 在 dash 中不存在。
📝 总结
source(.)是"当前 shell 加载并执行"的标准操作,与子 shell 执行形成对照。本节要点:
- 核心场景:
- ① 重新加载配置(
source ~/.bashrc) - ② 引入库函数(
source lib.sh) - ③ 切换环境(Python venv)
- ① 重新加载配置(
- 与 bash 的区别:source 不启动子 shell,副作用留下
- 使用技巧:
source ~/.bashrc(修改后立即生效)source env/dev.sh(切换环境)source lib.sh(复用函数)(source script.sh)(隔离副作用)
- 风险:source 不可信脚本等于直接执行;会污染 $@、set、trap
最终建议:加载配置 必须用 source;运行独立任务 用 bash 或 ./script.sh;复用库 用 source + lib 目录结构;隔离测试 用 (source script.sh)。
📚 相关命令
| 命令 | 用途 |
|---|---|
. (点号) | POSIX 等价 source |
bash script.sh | 子 shell 执行 |
./script.sh | 子 shell 执行(需执行权限) |
exec command | 替换当前 shell 进程(不返回) |
eval | 拼接并执行字符串 |
set | 设置 shell 选项 |
unset | 取消变量/函数 |
export | 标记变量传子进程 |
declare / typeset | 声明变量属性 |
command -v CMD | 检查命令是否存在 |
shellcheck | 静态分析 |
shopt | bash 选项(仅 bash) |
到此这篇关于Linux命令source(在当前 Shell 环境中执行文件)的文章就介绍到这了,更多相关Linux命令source内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
shell脚本declare命令的用法详解(声明变量的属性和类型)
这篇文章主要介绍了shell脚本declare命令用法(声明变量的属性和类型,declare 命令在一般的脚本编写中并不常用,大多数情况下直接使用简单的变量赋值语句即可满足需求,需要的朋友可以参考下2023-06-06
Apache使用 .htaccess 来实现强制https访问的方法
下面小编就为大家带来一篇Apache使用 .htaccess 来实现强制https访问的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧2017-03-03
shell脚本配合zabbix实现tomcat的故障自愈功能
这篇文章主要介绍了shell脚本配合zabbix实现tomcat的故障自愈,服务实现自愈的方式有通过shell脚本+定时任务的方式,蓝鲸Pass故障自愈平台,shell脚本+zabbix触发器动作,本文给大家详细介绍,需要的朋友可以参考下2022-03-03
使用Shell 脚本实现每隔100行插入一条记录且记录第一列包含行号其他列不变
本文通过实例代码给大家讲解了使用Shell 脚本实现每隔100行插入一条记录且记录第一列包含行号其他列不变的实现方法,非常不错,具有参考借鉴价值,需要的朋友参考下2016-12-12


最新评论