Shell 文件包含
source 与 . 的用法、库文件组织、与直接执行的区别。
Key Pointssource(或 .)在当前 shell 执行被包含文件,变量和函数会保留;直接执行则是在子 shell 里跑,什么也带不回来。
source 与 .
Bash
cat > /tmp/lib.sh <<'EOF'
APP_NAME="demo"
log() { printf '[%s] %s\n' "$APP_NAME" "$1"; }
EOF
source /tmp/lib.sh
log "loaded"
. /tmp/lib.sh
log "dot works too"Output
[demo] loaded
[demo] dot works toosource file and . file are identical (POSIX only defines .). The included file needs neither the execute bit nor a shebang.
和直接执行的区别
Bash
cat > /tmp/v.sh <<'EOF'
VAR="from-included"
EOF
bash /tmp/v.sh
echo "after bash: [${VAR:-unset}]"
source /tmp/v.sh
echo "after source: $VAR"Output
after bash: [unset]
after source: from-included| Way | Runs in | Variables survive |
|---|---|---|
source f / . f | Current shell | Yes |
bash f | Child shell | No |
./f | Child shell | No |
组织一个库
Bash
mkdir -p /tmp/proj/lib
cat > /tmp/proj/lib/common.sh <<'EOF'
set -euo pipefail
die() { echo "ERROR: $*" >&2; exit 1; }
need() { command -v "$1" >/dev/null || die "missing command: $1"; }
EOF
cat > /tmp/proj/run.sh <<'EOF'
#!/bin/bash
source "$(dirname "$0")/lib/common.sh"
need bash
echo "all dependencies present"
EOF
bash /tmp/proj/run.shOutput
all dependencies presentsource "$(dirname "$0")/lib/x.sh" is the idiom: it works no matter where the script is invoked from. Hard-coded relative paths break under cron.防重复包含
Bash
cat > /tmp/guard.sh <<'EOF'
[ -n "${__GUARD_SH__:-}" ] && return 0
__GUARD_SH__=1
COUNT=${COUNT:-0}
COUNT=$((COUNT + 1))
EOF
source /tmp/guard.sh
source /tmp/guard.sh
echo "loaded $COUNT time(s)"Output
loaded 1 time(s)source 与 .
Bash
cat > /tmp/lib.sh <<'EOF'
APP_NAME="demo"
log() { printf '[%s] %s\n' "$APP_NAME" "$1"; }
EOF
source /tmp/lib.sh
log "loaded"
. /tmp/lib.sh
log "dot works too"Output
[demo] loaded
[demo] dot works toosource file 与 . file 完全等价(POSIX 只规定了 .)。被包含的文件不需要可执行位,也不需要有 shebang。
和直接执行的区别
Bash
cat > /tmp/v.sh <<'EOF'
VAR="from-included"
EOF
bash /tmp/v.sh
echo "after bash: [${VAR:-unset}]"
source /tmp/v.sh
echo "after source: $VAR"Output
after bash: [unset]
after source: from-included| 方式 | 执行环境 | 变量是否保留 |
|---|---|---|
source f | 当前 shell | 是 |
bash f | 子 shell | 否 |
./f | 子 shell | 否 |
组织一个库
Bash
mkdir -p /tmp/proj/lib
cat > /tmp/proj/lib/common.sh <<'EOF'
set -euo pipefail
die() { echo "ERROR: $*" >&2; exit 1; }
need() { command -v "$1" >/dev/null || die "missing command: $1"; }
EOF
cat > /tmp/proj/run.sh <<'EOF'
#!/bin/bash
source "$(dirname "$0")/lib/common.sh"
need bash
echo "all dependencies present"
EOF
bash /tmp/proj/run.shOutput
all dependencies presentsource "$(dirname "$0")/lib/x.sh" 是标准写法:不管从哪里调用脚本,都能找到同目录下的库。写死相对路径会在 cron 里失效。防重复包含
Bash
cat > /tmp/guard.sh <<'EOF'
[ -n "${__GUARD_SH__:-}" ] && return 0
__GUARD_SH__=1
COUNT=${COUNT:-0}
COUNT=$((COUNT + 1))
EOF
source /tmp/guard.sh
source /tmp/guard.sh
echo "loaded $COUNT time(s)"Output
loaded 1 time(s)