Bash 变量命名规则与类型使用
一、变量命名
Bash 变量名由 POSIX 可移植字符集 限制,底层由 isname() 函数判断(man bash → "Name" 章节)。
1. 合法字符集
字符类型
是否允许
示例
字母 a-z A-Z
是
var, HOME
数字 0-9
是(但不能首字符)
count1, PORT_8080
下划线 _
是(可首字符)
_hidden, __internal__
连字符 -
否
my-var → 语法错误
点 .
否(会被解析为命令或路径)
app.config → 错误
美元 $
否(变量引用符)
$var 是引用,不是命名
空格 / 制表符
否
my var → 两个 token
其他符号 @ * ? ! 等
否
path@dir → 错误
底层实现 :变量名匹配正则 ^[a-zA-Z_][a-zA-Z0-9_]*$
可通过 declare -p 查看变量属性验证。
2. 首字符严格限制
bash
复制代码
valid_var=1 # ✅ 字母开头
_valid=1 # ✅ 下划线开头(常用于"私有")
1invalid=1 # ❌ 数字开头 → 语法错误
3. 大小写敏感(独立命名空间)
bash
复制代码
VAR=10
var=20
echo $VAR # → 10
echo $var # → 20
注意 :某些系统保留大写变量(如 PATH),建议小写用于自定义局部变量。
4. 禁止使用 Bash 保留字(完整列表)
类别
保留字
控制结构
if, then, else, elif, fi, case, esac, for, while, until, do, done
声明
declare, typeset, local, export, readonly
其他
function, select, time, {, }, [[, ]], ((, ))
bash
复制代码
if=1 # 语法错误
local=1 # 语法错误
可通过 compgen -k 查看所有保留字。
5. 变量名长度限制
理论无限制 (受内存限制)
实际建议 :≤ 32 字符(可读性)
过长会导致 bash: warning: command substitution: ignored null byte 等边缘问题
二、变量类型深度解析(定义、生命周期、继承、销毁)
类型 1:普通变量(User-Defined Variable)
项目
说明
定义
name=value(等号两边不能有空格 )
作用域
当前 Shell 进程
继承
不被子进程继承
生命周期
Shell 退出前存在
是否可修改
是
bash
复制代码
config_file="/etc/app.conf"
temp_dir="/tmp/app_$$" # $$ 是当前 PID
常见错误
bash
复制代码
config file = "a.conf" # 错误:被解析为命令 `config`
config_file = "a.conf" # 错误:等号两边有空格
类型 2:环境变量(Environment Variable)
项目
说明
定义
export NAME=value 或 NAME=value; export NAME
作用域
当前进程 + 所有子进程
继承
通过 env 传递给子 Shell/程序
命名惯例
全大写 + 下划线 (POSIX 约定)
查看
printenv, env, `set
bash
复制代码
export JAVA_HOME="/usr/lib/jvm/java-17"
export PATH="$PATH:$JAVA_HOME/bin"
继承机制详解
bash
复制代码
# parent.sh
export GREETING="Hello"
bash child.sh # child.sh 可直接使用 $GREETING
取消导出
bash
复制代码
export MYVAR=1
unset MYVAR # 完全删除
export -n MYVAR # 仅取消导出(变量仍存在)
系统预定义环境变量(部分)
变量
含义
示例
PATH
可执行文件搜索路径
/usr/bin:/bin
HOME
用户主目录
/home/user
PWD
当前工作目录
/tmp
OLDPWD
上一个目录
/var
SHELL
当前 Shell
/bin/bash
USER/LOGNAME
当前用户
alice
UID
用户 ID
1000
LANG/LC_ALL
语言环境
ja_JP.UTF-8
类型 3:位置变量 & 特殊参数(Positional & Special Parameters)
参数
含义
是否可修改
示例
$0
脚本文件名(含路径)
否
./script.sh
$1--$9
第 1--9 个参数
否
arg1
${10}
第 10 个及以上参数(必须用 {})
否
arg10
$#
参数总数
否
3
$@
所有参数(推荐 ,保留分隔)
否
"a" "b" "c"
$*
所有参数(合并为一个字符串)
否
a b c
$$
当前 Shell PID
否
12345
$!
最后后台进程 PID
否
12346
$?
上个命令退出码(0=成功)
否
0
$-
当前选项标志(如 himBHs)
否
himBH
$@ vs $* 关键区别
bash
复制代码
set -- "a b" "c" "d e"
echo "=== $* ==="
for i in $*; do echo $i; done
# 输出:
# a
# b
# c
# d
# e
echo "=== $@ ==="
for i in $@; do echo $i; done # 错误!与 $* 相同
for i in "$@"; do echo $i; done # 正确!保留原始分隔
# 输出:
# a b
# c
# d e
最佳实践 :永远使用 "$@" 传递参数。
移位操作 shift
bash
复制代码
shift # 丢弃 $1,$2 → $1,$3 → $2
shift 2 # 丢弃前两个参数
类型 4:只读变量(Readonly / Constant)
项目
说明
定义
readonly NAME=value 或 declare -r NAME=value
作用域
当前 Shell
继承
值被继承,但非只读 (子进程可修改)
查看
readonly -p
bash
复制代码
readonly BUILD_DATE=$(date -u +%Y%m%d)
readonly MAX_USERS=100
子进程继承行为
bash
复制代码
# parent.sh
readonly SECRET="abc123"
bash -c 'echo $SECRET; SECRET="hacked"; echo $SECRET'
# 输出:
# abc123
# hacked ← 子进程可修改!
结论 :readonly 不跨进程保护 ,仅在当前 Shell 防止误改。
解除只读(不可能)
bash
复制代码
readonly X=1
unset X # 失败
X=2 # 错误:readonly variable
类型 5:局部变量(Local Variable)
项目
说明
定义
local var=value(仅在函数内 )
作用域
当前函数(包括子函数?否!)
生命周期
函数返回时销毁
命名惯例
小写,避免全局冲突
bash
复制代码
myfunc() {
local counter=0
local tmpfile="/tmp/data_$$"
((counter++))
echo "Counter: $counter"
}
作用域边界
bash
复制代码
global_var="outer"
func1() {
local global_var="inner"
echo "func1: $global_var" # inner
func2
}
func2() {
echo "func2: $global_var" # outer
}
func1
# 输出:
# func1: inner
# func2: outer
local 不影响子函数 ,仅当前函数。
错误用法
bash
复制代码
local x=1 # 在全局作用域 → 语法错误(bash 4.0+ 允许,但非标准)
类型 6:状态变量(约定俗成,非语言特性)
项目
说明
定义
自定义,用于流程控制
命名惯例
后缀 _FLAG, _STATUS, 或 0/1 布尔
常见模式
0=成功/假, 非0=失败/真
bash
复制代码
# 布尔标志
DEBUG_ENABLED=1
DRY_RUN=0
# 状态码
EXIT_SUCCESS=0
EXIT_FAILURE=1
EXIT_INVALID_ARG=2
# 字符串状态
CURRENT_STATE="initializing"
推荐判断方式
bash
复制代码
if (( DEBUG_ENABLED )); then
echo "[DEBUG] 操作: $1"
fi
if [ "$CURRENT_STATE" = "running" ]; then
:
fi
三、变量属性详解(declare 内置命令)
选项
含义
示例
-a
数组
declare -a users
-i
整数
declare -i count=5
-l
转小写
declare -l input="ABC" → abc
-u
转大写
declare -u input="abc" → ABC
-r
只读
declare -r CONST=1
-x
导出(等价 export)
declare -x VAR=1
-p
显示属性
declare -p VAR
+x
取消导出
declare +x VAR
bash
复制代码
declare -i number="10a" # 自动转为 10(截断非数字)
echo $number # → 10
四、变量生命周期与销毁
操作
效果
unset var
删除变量
local var
函数退出自动销毁
Shell 退出
所有变量销毁
exec new-shell
替换当前 Shell,所有变量丢失
bash
复制代码
temp=123
unset temp
echo $temp # → (空)
五、变量引用与防注入
1. 引用规则
场景
正确写法
错误写法
普通变量
"$var"
$var(易分词错误)
数组
"${arr[@]}"
$arr
默认值
${var:-default}
$var:-default
赋值默认
${var:=default}
-
长度
${#var}
-
子串
${var:0:3}
-
2. 防注入示例
bash
复制代码
filename="$1"
# 危险:用户输入 "file; rm -rf /"
cp "$filename" /backup/ # 安全
# 危险写法
cp $filename /backup/ # 分词为多个命令
六、实战脚本
bash
复制代码
#!/usr/bin/env bash
# 文件: robust-app.sh
# 描述: 演示所有变量类型的最佳实践
set -euo pipefail # 安全模式
IFS=$'\n\t' # 安全分隔符
# ==================== 环境变量 ====================
export APP_NAME="DataProcessor"
export APP_ENV="${APP_ENV:-development}" # 默认值
export LOG_DIR="/var/log/$APP_NAME"
export MAX_WORKERS=8
# ==================== 常量 ====================
readonly VERSION="3.2.1"
readonly START_TIME=$(date -u +%s)
readonly PID_FILE="/var/run/${APP_NAME}.pid"
# ==================== 状态变量 ====================
DEBUG_MODE=0
PROCESSING_STATUS="idle"
EXIT_CODE=0
# ==================== 函数 ====================
log() {
local level="$1"
local msg="$2"
local timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)
echo "[$timestamp] [$level] $msg" >&2
}
start_processing() {
local input_file="$1"
local thread_count="${2:-$MAX_WORKERS}"
# 局部变量
local start_time
local record_count=0
local temp_file
start_time=$(date +%s)
temp_file=$(mktemp "/tmp/${APP_NAME}.XXXXXX")
log "INFO" "开始处理: $input_file (线程: $thread_count)"
# 模拟处理
while IFS= read -r line; do
((record_count++))
echo "$line" >> "$temp_file"
done < "$input_file"
local duration=$(( $(date +%s) - start_time ))
log "INFO" "处理完成: $record_count 条记录,耗时 ${duration}s"
# 清理
rm -f "$temp_file"
}
# ==================== 主逻辑 ====================
main() {
(( DEBUG_MODE )) && set -x # 调试模式
log "INFO" "启动 $APP_NAME v$VERSION (PID: $$)"
if (( $# < 1 )); then
log "ERROR" "用法: $0 <输入文件> [线程数]"
EXIT_CODE=1
return
fi
local input_file="$1"
if [[ ! -f "$input_file" ]]; then
log "ERROR" "文件不存在: $input_file"
EXIT_CODE=2
return
fi
PROCESSING_STATUS="running"
start_processing "$input_file" "${2:-}"
PROCESSING_STATUS="completed"
log "INFO" "任务结束,退出码: $EXIT_CODE"
}
# 入口
main "$@"
exit $EXIT_CODE
七、附录:快速参考表
类型
定义命令
作用域
继承
可改
命名建议
普通变量
v=1
当前 Shell
❌
✅
snake_case
环境变量
export V=1
当前 + 子进程
✅
✅
UPPER_SNAKE
位置参数
$1
当前脚本
❌
❌
固定
只读变量
readonly C=1
当前 Shell
值✅ 属性❌
❌
CONSTANT
局部变量
local x=1
当前函数
❌
✅
local_var
状态变量
flag=1
视定义
视 export
✅
is_running