Linux Shell 变量的定义与使用方法
Shell 变量用于存储数据,无需声明类型。支持基本赋值、算术运算及字符串处理。通过 export 可导出为环境变量供子进程使用。位置参数接收命令行输入。提供默认值设置、错误提示及模式匹配扩展功能。作用域默认为全局,函数内可用 local 限制。数组支持一维动态操作。调试可使用 set -x 或 declare。常见陷阱包括空格、引号缺失及未初始化变量。结合 Java 对比理解动态类型特性,有助于掌握脚本编写规范与最佳实践。

Shell 变量用于存储数据,无需声明类型。支持基本赋值、算术运算及字符串处理。通过 export 可导出为环境变量供子进程使用。位置参数接收命令行输入。提供默认值设置、错误提示及模式匹配扩展功能。作用域默认为全局,函数内可用 local 限制。数组支持一维动态操作。调试可使用 set -x 或 declare。常见陷阱包括空格、引号缺失及未初始化变量。结合 Java 对比理解动态类型特性,有助于掌握脚本编写规范与最佳实践。

在 Linux 系统中,Shell 脚本是自动化任务、系统管理、部署运维的核心工具之一。变量作为脚本中最基础也是最重要的元素之一,掌握其定义与使用方法,是每一个 Linux 用户和开发者必须跨越的第一道门槛。
在 Shell 环境中,变量(Variable)是用来存储数据的一个命名容器。你可以把它想象成一个贴了标签的盒子,里面可以放字符串、数字、命令输出等信息。当你需要重复使用某个值或动态改变程序行为时,变量就派上了用场。
与编译型语言如 C++ 或 Java 不同,Shell 是一种解释型脚本语言,它的变量无需声明类型,也不强制初始化——这既是它的灵活性所在,也是容易出错的地方。
示例:
name="Alice"
age=25
echo "Hello, $name! You are $age years old."
输出:
Hello, Alice! You are 25 years old.
在 Shell 中,变量赋值使用 = 符号,注意等号两边不能有空格!
# 正确写法
username="root"
# 错误写法(会导致语法错误)
# username ="root"
# ❌ 报错:command not found
_my_var=100 # ✅ 合法
_my_private=secret # ✅ 合法
2nd_try=fail # ❌ 非法!不能以数字开头
MY_VAR=value # ✅ 合法,但建议用于常量或环境变量
字符串可以用单引号 ' 或双引号 " 包裹:
\n, \t)name="Bob"
msg1='Hello $name' # 输出:Hello $name
msg2="Hello $name" # 输出:Hello Bob
echo "$msg1"
echo "$msg2"
虽然 Shell 变量默认是字符串类型,但你可以通过特定语法进行数值计算。
$(( )) 进行整数运算a=10
b=3
sum=$((a + b))
product=$((a * b))
mod=$((a % b))
echo "Sum: $sum" # 输出:13
echo "Product: $product" # 输出:30
echo "Mod: $mod" # 输出:1
expr 命令(较老的方式)result=$(expr 5 + 3)
echo $result # 输出:8
⚠️ 注意:
expr对空格敏感,操作符两侧必须有空格!
在 Shell 中,变量默认是全局变量,可以在当前 Shell 及其子进程中访问。若想限制变量作用域,可使用 local 关键字(仅在函数内有效)。
#!/bin/bash
global_var="I am global"
my_function(){
local local_var="I am local"
echo "Inside function: $global_var"
echo "Inside function: $local_var"
}
my_function
echo "Outside function: $global_var"
echo "Outside function: $local_var" # 输出为空,因为 local_var 已销毁
环境变量是被导出(export)给子进程使用的全局变量。常见的环境变量如 PATH, HOME, USER 等。
printenv
# 或
env
MY_APP_HOME="/opt/myapp"
export MY_APP_HOME
# 或者一步到位:
export JAVA_HOME="/usr/lib/jvm/java-11-openjdk"
export SECRET_KEY="abc123"
bash -c 'echo "Child shell sees: $SECRET_KEY"'
# 输出:Child shell sees: abc123
Shell 脚本可以通过 $1, $2, …, $9, ${10}, ${11} 等接收命令行传入的参数。
#!/bin/bash
echo "Script name: $0"
echo "First arg: $1"
echo "Second arg: $2"
echo "Number of args: $#"
echo "All args: $*"
运行:
./myscript.sh apple banana cherry
输出:
Script name: ./myscript.sh
First arg: apple
Second arg: banana
Number of args: 3
All args: apple banana cherry
| 变量名 | 含义 |
|---|---|
$0 | 脚本名称 |
$1~$9 | 第 1 到第 9 个参数 |
${10}+ | 第 10 个及以上参数需加大括号 |
$# | 参数个数 |
$* | 所有参数作为一个字符串 |
$@ | 所有参数作为独立字符串(推荐用于遍历) |
$? | 上一条命令的退出状态码(0 表示成功) |
$$ | 当前 Shell 进程 ID |
$! | 最后一个后台进程的 PID |
示例:
#!/bin/bash
echo "PID: $$"
sleep 5 &
bg_pid=$!
echo "Background job PID: $bg_pid"
wait $bg_pid
echo "Job finished with status: $?"
Shell 提供了强大的变量扩展功能,让你可以在不调用外部命令的情况下处理字符串。
${var:-default}如果变量未设置或为空,则使用默认值:
echo ${USER_NAME:-"Guest"} # 若 USER_NAME 未定义,则输出 Guest
${var:=default}如果变量未设置或为空,则赋值并返回:
echo ${COUNT:=0} # 若 COUNT 不存在,则设为 0 并输出 0
${var:?message}如果变量未设置或为空,则打印错误并退出:
echo ${REQUIRED_VAR:?"Required variable is missing!"} # 若 REQUIRED_VAR 为空,脚本终止并报错
str="hello_world.txt"
echo ${str:0:5} # 输出:hello
echo ${str:6} # 输出:world.txt
echo ${str: -3} # 输出:txt (注意空格)
filename="document_v2.pdf"
echo ${filename%.pdf} # 输出:document_v2
echo ${filename#*_} # 输出:v2.pdf
echo ${filename/doc/DOC} # 输出:DOCument_v2.pdf
| 阶段 | 说明 |
|---|---|
| Shell 启动 | 变量定义前不可用 |
| 定义变量 var=value | 变量进入当前 Shell 环境 |
| 是否 export? | 决定是否为环境变量 |
| 成为环境变量 | 可被子进程继承 |
| 仅当前 Shell 可用 | 非导出变量,子进程不可见 |
| 函数或子进程中不可见 | local 变量仅限函数内 |
| 脚本结束或 unset 删除 | 变量销毁 |
set -x 开启调试模式#!/bin/bash
set -x
name="Debug Mode"
echo "Hello $name"
set +x # 关闭调试
输出:
+ name='Debug Mode'
+ echo 'Hello Debug Mode'
Hello Debug Mode
+ set +x
declare 查看变量属性declare -p PATH # 输出:declare -x PATH="/usr/local/bin:/usr/bin:..."
typeset 或 declare 定义带类型的变量(Bash 特有)declare -i num=10 # 整型
declare -r const="immutable" # 只读
declare -a arr=("a" "b" "c") # 数组
# 尝试修改只读变量会报错
const="new value" # ❌ bash: const: readonly variable
Shell 支持一维数组,索引从 0 开始。
fruits=("apple" "banana" "cherry")
nums=(1 2 3 4 5)
echo ${fruits[0]} # apple
echo ${fruits[1]} # banana
echo ${fruits[@]} # 所有元素
echo ${#fruits[@]} # 数组长度:3
for fruit in "${fruits[@]}"; do
echo "I like $fruit"
done
fruits+=("orange")
fruits[${#fruits[@]}]="grape"
为了帮助 Java 开发者更快上手 Shell 变量,我们做一个对比表格:
| 特性 | Shell 变量 | Java 变量 |
|---|---|---|
| 类型声明 | 无类型,动态 | 必须声明类型(int, String 等) |
| 初始化 | 可选 | 局部变量必须初始化 |
| 作用域 | 默认全局,可用 local 限制 | 由 {} 块决定 |
| 命名规范 | 推荐小写,避免大写 | 驼峰命名法(camelCase) |
| 字符串拼接 | 直接连接或使用 "$a$b" | 使用 + 或 StringBuilder |
| 环境变量 | 通过 export 导出 | 通过 System.getenv() 读取 |
| 数组 | 一维,动态 | 多维,固定长度或使用 ArrayList |
| 调试 | set -x, echo | IDE 断点、日志框架 |
下面是一个 Java 类,模拟 Shell 变量的基本行为——动态类型、字符串存储、支持默认值等。
import java.util.HashMap;
import java.util.Map;
public class ShellVariableSimulator {
private Map<String, String> variables = new HashMap<>();
// 设置变量
public void set(String key, String value) {
variables.put(key, value);
}
// 获取变量,支持默认值
public String get(String key, String defaultValue) {
return variables.getOrDefault(key, defaultValue);
}
// 获取变量,无默认值则返回 null
public String get(String key) {
return variables.get(key);
}
// 模拟 ${var:-default} 语法
public String getDefault(String key, String fallback) {
String val = variables.get(key);
return (val == null || val.isEmpty()) ? fallback : val;
}
// 模拟 ${var:=default}
public String setDefault(String key, String fallback) {
if (!variables.containsKey(key) || variables.get(key).isEmpty()) {
variables.put(key, fallback);
}
return variables.get(key);
}
// 模拟 ${var:?message}
public String require(String key, String errorMessage) throws RuntimeException {
String val = variables.get(key);
if (val == null || val.isEmpty()) {
throw new RuntimeException(errorMessage);
}
return val;
}
// 打印所有变量
public void printAll() {
variables.forEach((k, v) -> System.out.println(k + " = " + v));
}
// 示例运行
public static void main(String[] args) {
ShellVariableSimulator sim = new ShellVariableSimulator();
sim.set("USER", "john_doe");
sim.set("TEMP_DIR", "");
System.out.println("USER: " + sim.get("USER", "guest")); // john_doe
System.out.println("HOST: " + sim.get("HOST", "localhost")); // localhost
System.out.println("TEMP_DIR or /tmp: " + sim.getDefault("TEMP_DIR", "/tmp")); // /tmp
sim.setDefault("LOG_LEVEL", "INFO");
System.out.println("LOG_LEVEL: " + sim.get("LOG_LEVEL")); // INFO
try {
sim.require("REQUIRED_CONFIG", "配置项 REQUIRED_CONFIG 不能为空!");
} catch (RuntimeException e) {
System.out.println("错误:" + e.getMessage());
}
System.out.println("\n--- 所有变量 ---");
sim.printAll();
}
}
📌 运行结果:
USER: john_doe
HOST: localhost
TEMP_DIR or /tmp: /tmp
LOG_LEVEL: INFO
错误:配置项 REQUIRED_CONFIG 不能为空!
--- 所有变量 ---
USER = john_doe
TEMP_DIR =
LOG_LEVEL = INFO
这个模拟器虽然简化了很多 Shell 特性(比如数组、算术扩展、命令替换等),但它清晰地展示了 Shell 变量'动态'、'字符串为中心'、'默认值机制'的核心思想。
有时候你需要通过一个变量的名字去访问另一个变量的值,这就叫'间接引用'。
${!var} 语法name="Alice"
ref="name"
echo ${!ref} # 输出:Alice
prefix="user_"
for i in {1..3}; do
var_name="${prefix}${i}"
declare "$var_name=Person$i"
done
echo $user_1 # Person1
echo $user_2 # Person2
echo $user_3 # Person3
不再需要的变量应及时清理,尤其是大型脚本中,避免命名冲突或内存浪费。
temp_data="some sensitive info"
echo $temp_data
unset temp_data
echo $temp_data # 输出为空
也可以一次删除多个变量:
unset var1 var2 var3
下面我们编写一个简单的 Shell 脚本,从 .env 文件加载配置到环境变量中,类似 Docker Compose 的行为。
.env 文件内容:
DB_HOST=localhost
DB_PORT=5432
DB_USER=admin
DB_PASS=secret123
DEBUG=true
脚本 load_config.sh:
#!/bin/bash
CONFIG_FILE=".env"
if [[ ! -f "$CONFIG_FILE" ]]; then
echo "配置文件 $CONFIG_FILE 不存在!"
exit 1
fi
while IFS='=' read -r key value; do
# 跳过空行和注释
[[ -z "$key" ]] && continue
[[ "$key" =~ ^[[:space:]]*# ]] && continue
# 去除前后空格
key=$(echo "$key" | xargs)
value=$(echo "$value" | xargs)
# 导出为环境变量
export "$key=$value"
echo "Loaded: $key = $value"
done < "$CONFIG_FILE"
echo "✅ 配置加载完成!共加载 $(printenv | grep -E '^(DB_|DEBUG)' | wc -l) 个变量。"
运行效果:
Loaded: DB_HOST = localhost
Loaded: DB_PORT = 5432
Loaded: DB_USER = admin
Loaded: DB_PASS = secret123
Loaded: DEBUG = true
✅ 配置加载完成!共加载 5 个变量。
# 错误 ❌
name ="John"
# 正确 ✅
name="John"
file_path="/home/user/my file.txt"
# 错误:会被当成两个参数
cat $file_path
# 正确:加引号保护空格
cat "$file_path"
# 若 count 未定义,$((count + 1)) 会变成 1,而不是报错
echo $((count + 1))
# 建议先初始化
count=0
echo $((count + 1))
usre_name="Bob" # 拼写错误
echo "Hello $username" # 输出空!
# 建议开启严格模式
set -u # 遇到未定义变量时报错
虽然 Shell 没有像 JUnit 那样的测试框架,但你可以用简单的 if 判断和 exit 来做基本验证。
#!/bin/bash
test_variable_default() {
local test_val=${UNDEFINED_VAR:-"default"}
if [[ "$test_val" != "default" ]]; then
echo "❌ Test failed: expected 'default', got '$test_val'"
exit 1
fi
echo "✅ Test passed: default value works"
}
test_variable_set_default() {
unset TEST_VAR
local result=${TEST_VAR:="fallback"}
if [[ "$result" != "fallback" ]]; then
echo "❌ Test failed: expected 'fallback', got '$result'"
exit 1
fi
echo "✅ Test passed: set-default works"
}
test_variable_require() {
unset REQUIRED
if output=$({ require_var "REQUIRED" "Missing!";} 2>&1); then
echo "❌ Test failed: should have thrown error"
exit 1
else
echo "✅ Test passed: required variable check works"
fi
}
require_var() {
local var_name=$1
local msg=$2
if [[ -z "${!var_name}" ]]; then
echo "$msg" >&2
return 1
fi
}
# 运行测试
test_variable_default
test_variable_set_default
test_variable_require
echo "🎉 All tests passed!"
在现代 DevOps 流程中,Shell 变量无处不在:
例如,在 GitLab CI 中:
deploy_prod:
script:
- echo "Deploying version $CI_COMMIT_TAG to $DEPLOY_ENV"
- ./deploy.sh --env $DEPLOY_ENV --version $CI_COMMIT_TAG
environment:
name: production
这里的 $CI_COMMIT_TAG 和 $DEPLOY_ENV 就是典型的 Shell 环境变量。
频繁使用命令替换会影响脚本性能:
# ❌ 不推荐:每次循环都执行 date 命令
for i in {1..1000}; do
echo "Time: $(date)"
done
# ✅ 推荐:提前缓存结果
now=$(date)
for i in {1..1000}; do
echo "Time: $now"
done
backup_dir, max_retrieslocal 避免污染全局空间unset 释放资源set -u、set -e 提高脚本健壮性${var:-default} 避免空值异常
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online