Shell 文件包含

source 与 . 的用法、库文件组织、与直接执行的区别。

Beginner
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 too

source 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
WayRuns inVariables survive
source f / . fCurrent shellYes
bash fChild shellNo
./fChild shellNo

组织一个库

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.sh
Output
all dependencies present
source "$(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)

Friends & Partners