Linux命令source详解(在当前 Shell 环境中执行文件)

 更新时间:2026年08月06日 09:48:04   作者:RisunJan  
在 Linux 中,source 命令(或者它的等价形式 . 命令)用于在当前 Shell 环境中执行一个脚本文件或命令,这使得脚本中的变量、函数等在当前 Shell 会话中可用,而不是在子 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.shbash 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 修改的变量、函数、cdulimitumask全部持续到当前 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 不会自动 source
source 不会自动把当前目录加入 PATH。
./script.shsource ./script.sh 都不需要 PATH 修改。

10. POSIX 标准
POSIX 严格只支持 . filenamesourceBash 扩展
可移植脚本用 .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静态分析
shoptbash 选项(仅 bash)

到此这篇关于Linux命令source(在当前 Shell 环境中执行文件)的文章就介绍到这了,更多相关Linux命令source内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

最新评论