Shell 变量详解(新手详细版)
本文档面向零基础新手,从“变量是什么、怎么定义和使用”讲起,详细说明命名、赋值、引用、引号、作用域、环境变量、特殊变量、变量展开等,并配有大量示例。
一、变量是什么?
1.1 一句话理解
变量就是给一段数据(字符串或数字)起个名字,后面用这个名字就能取出或修改这段数据。
Shell 里变量没有类型声明,本质上都是字符串;在算术运算或比较时,Shell 会按需把字符串当数字用。
1.2 典型用途
- 存一个路径、文件名、配置值;
- 存命令的返回值或输出(命令替换);
- 在脚本里多次使用同一值,改一处即可;
- 和环境变量配合,影响命令行为(如 PATH、HOME)。
二、变量的定义与赋值
2.1 基本语法
变量名=值
注意:
- = 两边不能有空格,否则会被当成“命令 + 参数”。
- 变量名由字母、数字、下划线组成,不能以数字开头。
- 值里若含空格或特殊字符,建议用双引号包起来。
# 正确
name="张三"
count=10
path=/home/user
# 错误:等号两边有空格
# name = "张三"
# 错误:变量名以数字开头
# 1var=1
2.2 值不需要引号的情况
若值里没有空格、#、$、引号等,可以不加引号:
count=0
dir=/tmp
习惯上字符串也加双引号更安全,避免值意外为空或含特殊字符时出错。
2.3 值里有空格或特殊字符
用双引号包起来;双引号内 $变量、$(命令) 会被展开。
msg="hello world"
path="/home/user/my file.txt"
用单引号时,什么都不展开,全部当字面字符:
a='$HOME'
echo $a
# 输出:$HOME(不是家目录路径)
2.4 把命令的输出赋给变量(命令替换)
用 $(命令) 或 命令(反引号),把命令的标准输出当作字符串赋给变量:
today=$(date +%Y-%m-%d)
echo "今天: $today"
list=$(ls)
# list 里是 ls 的整段输出(多行在一个字符串里)
推荐用 $( ):可嵌套、可读性更好;反引号 ` 在嵌套时容易写错。
三、变量的引用(使用)
3.1 基本引用:$变量名
在命令或字符串里写 $变量名,Shell 会把它替换成变量的值:
name="张三"
echo "你好,$name"
# 你好,张三
echo $name
# 张三
3.2 加大括号:${变量名}
${变量名} 和 $变量名 在简单情况下等价;必须用 ${ } 的情况包括:
- 变量名后面紧跟字母、数字、下划线时,避免被当成另一个变量名:
a="hello" echo "${a}world" # helloworld echo "$aworld" # 空(会找变量 aworld) - 做变量展开(默认值、截取、替换等)时,都要写成 ${ }。
建议:习惯写 ${var},更清晰、少出错。
3.3 引用时加双引号
把变量放在双引号里:“$var” 或 “${var}”,这样即使值为空或含空格,也不会被拆成多个词或触发通配:
file="my file.txt"
cat $file
# 会找 my 和 file.txt 两个文件,报错
cat "$file"
# 正确:一个文件 my file.txt
建议:只要把变量当“一个整体”用,就写 “${var}”。
四、只读变量与删除变量
4.1 只读(不可改):readonly 或 declare -r
readonly PI=3.14
# 或
declare -r PI=3.14
PI=3.2
# 报错:只读变量
4.2 删除变量:unset
unset 变量名 会删除该变量,相当于“从未定义过”:
x=1
unset x
echo "${x:-空}"
# 空
注意:unset 不能用于只读变量。
五、环境变量与 export
5.1 普通变量只在当前 Shell 里有效
在脚本或交互 Shell 里定义的变量,只在该 Shell 进程内有效;用这个 Shell 去启动子进程(如再运行一个脚本、一条命令)时,子进程默认拿不到这些变量。
5.2 导出为环境变量:export
export 变量名 或 export 变量名=值 会把变量放进环境,之后从当前 Shell 启动的子进程都能看到它:
export MY_VAR="hello"
# 或先赋值再导出
MY_VAR="hello"
export MY_VAR
子脚本里可以直接用 $MY_VAR。
5.3 常见环境变量
| 变量 | 含义 |
|---|---|
| PATH | 可执行文件搜索路径,多个用 : 分隔 |
| HOME | 当前用户主目录 |
| USER | 当前用户名 |
| SHELL | 当前 Shell 路径 |
| PWD | 当前工作目录 |
| PS1 | 主提示符格式 |
查看所有环境变量:env 或 printenv;查看某一个:echo $PATH。
5.4 一次赋值并导出
export PATH="/opt/bin:$PATH"
这样当前 Shell 和子进程都会用到新的 PATH。
六、特殊变量(Shell 内置)
这些变量由 Shell 自动设置,只读(不能赋值),常用于脚本和函数。
6.1 脚本/命令相关
| 变量 | 含义 |
|---|---|
| $0 | 当前脚本名(或调用的命令名) |
| $1, $2, … | 第 1、2、… 个参数 |
| $# | 参数个数 |
| **$*** | 所有参数(一个字符串,见下) |
| $@ | 所有参数(多个词,透传用 “$@”) |
6.2 进程相关
| 变量 | 含义 |
|---|---|
| $$ | 当前 Shell 的进程 ID(PID) |
| $! | 最后一个后台进程的 PID |
| $? | 上一条命令的退出码(0 表示成功) |
示例:
echo "脚本名: $0"
echo "第1个参数: $1"
echo "参数个数: $#"
echo "上一条命令退出码: $?"
echo "当前进程 PID: $$"
6.3 $* 与 $@ 的区别(简要)
- 不加大引号时,两者行为类似。
- “$@”:每个参数保持为独立的一个词,适合原样传给其他命令。
- *“$“:所有参数拼成一个字符串**。
脚本里透传参数给子命令时,用 “$@”。
七、变量展开(高级用法)
下面都写成 ${变量名…} 形式,用于取默认值、截取、替换等。
7.1 默认值:${var:-默认值}
若 var 未设置或为空,则展开为“默认值”;不改变 var 的值:
unset x
echo "${x:-没有设置}"
# 没有设置
x=""
echo "${x:-空}"
# 空
x="有值"
echo "${x:-默认}"
# 有值
7.2 赋值型默认值:${var:=默认值}
若 var 未设置或为空,则先给 var 赋值为“默认值”,再展开为该值;常用于“未设置就设成某值”:
unset y
: ${y:=100}
echo $y
# 100
(: 是空命令,只做参数展开,不执行别的;这里用来触发对 y 的赋值。)
7.3 报错退出:${var:?错误信息}
若 var 未设置或为空,打印“错误信息”到标准错误并退出(非交互 Shell 通常非 0 退出码):
: ${1:?请提供第1个参数}
7.4 有值才替换:${var:+替换值}
若 var 已设置且非空,展开为“替换值”;否则展开为空:
x=""
echo "结果: ${x:+有}"
# 结果:
x="1"
echo "结果: ${x:+有}"
# 结果: 有
7.5 字符串长度:${#var}
s="hello"
echo ${#s}
# 5
7.6 子串:${var:起始:长度}
从“起始”位置开始取“长度”个字符(起始从 0 开始);省略长度则取到末尾:
s="abcdef"
echo ${s:0:3}
# abc
echo ${s:2:2}
# cd
echo ${s:2}
# cdef
7.7 删除前缀/后缀(模式)
- ${var#模式}:删掉最短匹配的前缀。
- ${var##模式}:删掉最长匹配的前缀。
- ${var%模式}:删掉最短匹配的后缀。
- ${var%%模式}:删掉最长匹配的后缀。
模式可含 *(通配):
path="/home/user/file.txt"
echo ${path#*/}
# home/user/file.txt
echo ${path##*/}
# file.txt
echo ${path%.*}
# /home/user/file
echo ${path%%.*}
# /home/user/file
7.8 替换:${var/旧/新}、${var//旧/新}
- ${var/旧/新}:第一个“旧”换成“新”。
- ${var//旧/新}:所有“旧”换成“新”。
s="hello world"
echo ${s/o/O}
# hellO world
echo ${s//o/O}
# hellO wOrld
7.9 小结表
| 写法 | 含义 |
|---|---|
| ${var:-default} | 空则用 default,不赋值 |
| ${var:=default} | 空则赋值为 default 并展开 |
| ${var:?msg} | 空则报错 msg 并退出 |
| ${var:+value} | 非空则展开 value |
| ${#var} | 长度 |
| ${var:起:长} | 子串 |
| ${var#pat} / ${var##pat} | 删最短/最长前缀 |
| ${var%pat} / ${var%%pat} | 删最短/最长后缀 |
| ${var/旧/新} / ${var//旧/新} | 替换一次/全部 |
八、引号对变量的影响
8.1 双引号 “…”
- $变量、$(命令) 会被展开。
- 空格、多数特殊字符保留为字面(除 $、`、 等)。
name="张三"
echo "你好,$name"
# 你好,张三
8.2 单引号 ‘…’
- 什么都不展开,全部当字面字符;$name 就是四个字符,不是变量值。
echo '$HOME'
# $HOME
8.3 反引号 ...
- 用于命令替换(和 $( ) 等价);里面的 $变量 会展开。推荐用 $( )。
8.4 $’…’(Bash)
- 支持反斜杠转义,如 n 换行、t 制表符:
echo $'第一行n第二行'
九、变量的作用域(简要)
9.1 默认是“全局”
在脚本顶层定义的变量,在整个脚本(包括其调用的函数)里都可见,除非在函数里用 local。
9.2 函数里的 local
local 变量名=值 定义的变量只在当前函数(及它调用的函数)内有效;函数外若有同名变量,不会被修改。
x=1
f() {
local x=2
echo "函数内 x=$x"
}
f
echo "函数外 x=$x"
# 函数内 x=2
# 函数外 x=1
9.3 子 Shell 与管道
在 子 Shell(如 $( )、管道右侧)里对变量的赋值不会影响当前 Shell;当前 Shell 的普通变量也不会自动传进子 Shell,除非用 export 变成环境变量。
十、从键盘读入:read
read 从标准输入读一行,拆成多个“词”赋给变量(默认拆成多个变量或一个变量):
read -p "请输入名字: " name
echo "你好,$name"
- -p “提示”:先输出提示再读。
- -r:不把 当转义(推荐用)。
- read -a arr:读一行,按 IFS 拆成数组 arr(Bash)。
read -r line
echo "你输入了: $line"
十一、整数运算与变量
Shell 变量本质是字符串;做算术时用 $(( 表达式 )),表达式里变量可省略 $:
a=10
b=3
echo $(( a + b ))
# 13
c=$(( a * b ))
echo $c
# 30
也可用 (( 变量++ ))**、(( 变量 += 1 ))** 等改变变量值(详见 Shell运算符.md)。
十二、实用示例汇总
12.1 使用默认值
name=${1:-"访客"}
echo "你好,$name"
12.2 必填参数检查
: ${1:?用法: $0 <文件名>}
file=$1
12.3 取路径中的文件名或目录
path="/home/user/file.txt"
filename=${path##*/}
dir=${path%/*}
12.4 命令结果赋给变量
count=$(wc -l < file.txt)
today=$(date +%Y-%m-%d)
12.5 环境变量参与路径
echo "$HOME/bin"
export PATH="$HOME/bin:$PATH"
十三、常见坑与建议
- 赋值时 = 两边不能有空格:
x=1正确,x = 1会报错。 - 引用时尽量加双引号:“$var”,避免空或含空格时被拆词。
- 能写 ${var} 就写:避免和后面字符连成新变量名。
- 子进程要用的变量需 export:否则子脚本、子命令看不到。
- 只读变量用 readonly:防止被误改;unset 不能删只读变量。
- $? 是上一条命令的退出码:每执行一条命令都会更新,要及时用或存到别的变量。
十四、语法速查表
| 操作 | 写法 |
|---|---|
| 赋值 | var=值、var="带空格" |
| 引用 | $var、${var} |
| 命令替换 | var=$(命令) |
| 默认值 | ${var:-默认}、${var:=默认} |
| 报错退出 | ${var:?信息} |
| 长度 | ${#var} |
| 子串 | ${var:起:长} |
| 删前缀/后缀 | ${var#pat}、${var%pat} |
| 替换 | ${var/旧/新} |
| 只读 | readonly var、declare -r var |
| 导出 | export var |
| 删除 | unset var |
| 读入 | read -r var、read -p "提示" var |
十五、小结
- 变量:名=值(等号无空格);引用 $var 或 ${var};建议加双引号 “${var}”。
- 命令替换:$(命令) 把输出当字符串;引号:双引号展开变量,单引号不展开。
- 环境变量:export 后子进程可见;只读用 readonly;删除用 unset。
- 特殊变量:$0、$1、$#、$@、$?、$$ 等;透传参数用 “$@”。
- 变量展开:${var:-default}、${#var}、${var:起:长}、${var#pat}、${var/旧/新} 等。
多写几段脚本练赋值、引用、默认值、命令替换、export、read,再结合 Shell传递参数.md、Shell运算符.md、Shell数组.md 一起用,变量就能用熟。
文档以 Bash 为准;sh 或其它 Shell 在部分展开、read、export 等上可能略有差异。