Bash 变量分成环境变量和自定义变量两类。
环境变量是 Bash 环境自带的变量,进入 Shell 时已经定义好了,可以直接使用。它们通常是系统定义好的,也可以由用户从父 Shell 传入子 Shell。
env命令或printenv命令,可以显示所有环境变量。
1 | # `printenv 环境变量名`可以输出单个环境变量的值,变量前不用加$ |
下面是一些常见的环境变量。
BASHPID:Bash 进程的进程 ID。BASHOPTS:当前 Shell 的参数,可以用shopt命令修改。DISPLAY:图形环境的显示器名字,通常是:0,表示 X Server 的第一个显示器。EDITOR:默认的文本编辑器。HOME:用户的主目录。HOST:当前主机的名称。IFS:词与词之间的分隔符,默认为空格。LANG:字符集以及语言编码,比如zh_CN.UTF-8。PATH:由冒号分开的目录列表,当输入可执行程序名后,会搜索这个目录列表。PS1:Shell 提示符。PS2: 输入多行命令时,次要的 Shell 提示符。PWD:当前工作目录。RANDOM:返回一个0到32767之间的随机数。SHELL:Shell 的名字。SHELLOPTS:启动当前 Shell 的set命令的参数,参见《set 命令》一章。TERM:终端类型名,即终端仿真器所用的协议。UID:当前用户的 ID 编号。USER:当前用户的用户名。很多环境变量很少发生变化,而且是只读的,可以视为常量。由于它们的变量名全部都是大写,所以传统上,如果用户要自己定义一个常量,也会使用全部大写的变量名。
注意,Bash 变量名区分大小写,HOME和home是两个不同的变量。
自定义变量是用户在当前 Shell 里面自己定义的变量,仅在当前 Shell 可用。一旦退出当前 Shell,该变量就不存在了。
set命令可以显示所有变量(包括环境变量和自定义变量),以及所有的 Bash 函数。
Bash 提供一些特殊变量。这些变量的值由 Shell 提供,用户不能进行赋值。
$?
$?为上一个命令的退出码,用来判断上一个命令是否执行成功。返回值是0,表示上一个命令执行成功;如果不是零,表示上一个命令执行失败。
1 | $ ls doesnotexist |
$$
$$为当前 Shell 的进程 ID。
1 | # 这个特殊变量可以用来命名临时文件 |
$_
$_为上一个命令的最后一个参数。
1 | $ grep dictionary /usr/share/dict/words |
$!
$!为最近一个后台执行的异步命令的进程 ID。
1 | $ firefox & |
上面例子中,firefox是后台运行的命令,$!返回该命令的进程 ID。
$0
$0为当前 Shell 的名称(在命令行直接执行时)或者脚本名(在脚本中执行时)。
1 | $ echo $0 |
$@
$@表示脚本的参数值,参见脚本一章。
$#
$#表示脚本的参数数量,参见脚本一章。
$-
$-为当前 Shell 的启动参数。
变量声明的语法如下:
1 | # 等号左边是变量名,右边是变量。等号两边不能有空格 |
用户创建变量的时候,变量名必须遵守下面的规则。
读取变量的时候,直接在变量名前加上$就可以了。如果变量不存在,Bash 不会报错,而会输出空字符。
1 | # 读取变量的时候,变量名也可以使用花括号`{}`包围,这种写法可以用于变量名与其他字符连用的情况。 |
如果变量的值本身也是变量,可以使用${!varname}的语法,读取最终的值。
1 | $ myvar=USER |
如果变量值包含连续空格(或制表符和换行符),最好放在双引号里面读取,比如echo "$a"
unset命令用来删除一个变量。
但是这个命令不是很有用。因为即使unset命令删除了变量,还是可以读取这个变量,值为空字符串。
1 | unset NAME |
所以,删除一个变量,也可以将这个变量设成空字符串。
1 | $ foo='' |
上面两种写法,都是删除了变量foo。由于不存在的值默认为空字符串,所以后一种写法可以在等号右边不写任何值。
用户创建的变量仅可用于当前 Shell,子 Shell 默认读取不到父 Shell 定义的变量。为了把变量传递给子 Shell,需要使用export命令。这样输出的变量,对于子 Shell 来说就是环境变量。
export命令用来向子 Shell 输出变量。
1 | NAME=foo |
上面命令执行后,当前 Shell 及随后新建的子 Shell,都可以读取变量$NAME。子 Shell 如果修改继承的变量,不会影响父 Shell。
declare命令可以声明一些特殊类型的变量,为变量设置一些限制,比如声明只读类型的变量和整数类型的变量。
它的语法形式如下。
1 | declare OPTION VARIABLE=value |
declare命令的主要参数(OPTION)如下。
-a:声明数组变量。
-i:声明整数变量。
声明整数变量以后,可以直接进行数学运算。
1 | $ declare -i val1=12 val2=5 |
上面例子中,只要result声明为整数,它的赋值就会自动解释为整数运算。val1和val2其实不必要声明为整数。
注意,一个变量声明为整数以后,依然可以被改写为字符串,但是会赋给意料之外的变量值,不建议。
-f:输出所有函数定义。
输出当前环境的所有函数,包括它们的定义。
-F:输出所有函数名。
输出当前环境的所有函数名,不包括函数定义。
-l:声明变量为小写字母。
声明变量为小写字母,可以自动把变量值转成小写字母。
-u:声明变量为大写字母。
参数声明变量为大写字母,可以自动把变量值转成大写字母。
-p:查看变量信息。
输出已定义的变量的值,对于未定义的变量,会提示找不到。如果不提供变量名,declare -p输出所有变量的信息。
-r:声明只读变量。
可以声明只读变量,无法改变变量值,也不能unset变量。
-x:该变量输出为环境变量。
等同于export命令,可以输出一个变量为子 Shell 的环境变量。
declare命令如果用在函数中,声明的变量只在函数内部有效,等同于local命令。
不带任何参数时,declare命令输出当前环境的所有变量,包括函数在内,等同于不带有任何参数的set命令。
1 | $ declare |
readonly命令等同于declare -r,用来声明只读变量,不能改变变量值,也不能unset变量。
readonly命令有三个参数。
-f:声明的变量为函数名。-a:声明的变量为数组。-p:打印出所有的只读变量。let命令声明变量时,可以直接执行算术表达式。
1 | $ let foo=1+2 |
let命令的参数表达式如果包含空格,就需要使用引号。
1 | $ let "foo = 1 + 2" |
let可以同时对多个变量赋值,赋值表达式之间使用空格分隔。
1 | $ let "v1 = 1" "v2 = v1++" |
这种语法支持的运算符,参考《Bash 的算术运算》一章。
Bash 提供了四个特殊语法,跟变量的默认值有关,目的是保证变量不为空。
${varname:-word}
这个语法的含义是,如果变量varname存在且不为空,则返回它的值,否则返回word。
它的目的是返回一个默认值,比如${count:-nonexistent},表示变量count不存在时返回nonexistent。
${varname:=word}
这个语法的含义是,如果变量varname存在且不为空,则返回它的值,否则将它设为word,并且返回word。
它的目的是设置变量的默认值,比如${count:=0}表示变量count不存在时返回0,且将count设为0。
${varname:+word}
这个语法的含义是,如果变量名存在且不为空,则返回word,否则返回空值。
它的目的是测试变量是否存在,比如${count:+exist}表示变量count存在时返回exist,否则返回空值。
${varname:?message}
这个语法的含义是,如果变量varname存在且不为空,则返回它的值,否则打印出varname: message,并中断脚本的执行。
它的目的是防止变量未定义,比如${count:?"undefined!"}表示变量count未定义时就中断执行,抛出错误,返回给定的报错信息undefined!。
message可以省略,写成${varname:?},则输出默认的信息“parameter null or not set.”。
在脚本中的用法
上面四种语法如果用在脚本中,变量名的部分可以用数字1到9,表示脚本的参数。
1 | filename=${1:?"缺少参数1"} |
上面代码出现在脚本中,1表示脚本的第一个参数。如果该参数不存在,就退出脚本并报错。