Bash 变量分成环境变量和自定义变量两类。
环境变量是 Bash 环境自带的变量,进入 Shell 时已经定义好了,可以直接使用。它们通常是系统定义好的,也可以由用户从父 Shell 传入子 Shell。
env命令或printenv命令,可以显示所有环境变量。
env
printenv
$ env # 或者 $ printenv
下面是一些常见的环境变量。
BASHPID
BASHOPTS
shopt
DISPLAY
:0
EDITOR
HOME
HOST
IFS
LANG
zh_CN.UTF-8
PATH
PS1
PS2
PWD
RANDOM
SHELL
SHELLOPTS
set
TERM
UID
USER
很多环境变量很少发生变化,而且是只读的,可以视为常量。由于它们的变量名全部都是大写,所以传统上,如果用户要自己定义一个常量,也会使用全部大写的变量名。
注意,Bash 变量名区分大小写,HOME和home是两个不同的变量。
home
查看单个环境变量的值,可以使用printenv命令或echo命令。
echo
$ printenv PATH # 或者 $ echo $PATH
注意,printenv命令后面的变量名,不用加前缀$。
$
自定义变量是用户在当前 Shell 里面自己定义的变量,必须先定义后使用,而且仅在当前 Shell 可用。一旦退出当前 Shell,该变量就不存在了。
set命令可以显示所有变量(包括环境变量和自定义变量),以及所有的 Bash 函数。
$ set
用户创建变量的时候,变量名必须遵守下面的规则。
变量声明的语法如下。
variable=value
上面命令中,等号左边是变量名,右边是变量。注意,等号两边不能有空格。
如果变量的值包含空格,则必须将值放在引号中。
myvar="hello world"
Bash 没有数据类型的概念,所有的变量值都是字符串。
下面是一些自定义变量的例子。
a=z # 变量 a 赋值为字符串 z b="a string" # 变量值包含空格,就必须放在引号里面 c="a string and $b" # 变量值可以引用其他变量的值 d="\t\ta string\n" # 变量值可以使用转义字符 e=$(ls -l foo.txt) # 变量值可以是命令的执行结果 f=$((5 * 7)) # 变量值可以是数学运算的结果
变量可以重复赋值,后面的赋值会覆盖前面的赋值。
$ foo=1 $ foo=2 $ echo $foo 2
上面例子中,变量foo的第二次赋值会覆盖第一次赋值。
foo
读取变量的时候,直接在变量名前加上$就可以了。
$ foo=bar $ echo $foo bar
每当 Shell 看到以$开头的单词时,就会尝试读取这个变量名对应的值。
如果变量不存在,Bash 不会报错,而会输出空字符。
由于$在 Bash 中有特殊含义,把它当作美元符号使用时,一定要非常小心,
$ echo The total is $100.00 The total is 00.00
上面命令的原意是输入$100,但是 Bash 将$1解释成了变量,该变量为空,因此输入就变成了00.00。所以,如果要使用$的原义,需要在$前面放上反斜杠,进行转义。
$100
$1
00.00
$ echo The total is \$100.00 The total is $100.00
读取变量的时候,变量名也可以使用花括号{}包围,比如$a也可以写成${a}。这种写法可以用于变量名与其他字符连用的情况。
{}
$a
${a}
$ a=foo $ echo $a_file $ echo ${a}_file foo_file
上面代码中,变量名a_file不会有任何输出,因为 Bash 将其整个解释为变量,而这个变量是不存在的。只有用花括号区分$a,Bash 才能正确解读。
a_file
事实上,读取变量的语法$foo,可以看作是${foo}的简写形式。
$foo
${foo}
如果变量的值本身也是变量,可以使用${!varname}的语法,读取最终的值。
${!varname}
$ myvar=USER $ echo ${!myvar} ruanyf
上面的例子中,变量myvar的值是USER,${!myvar}的写法将其展开成最终的值。
myvar
${!myvar}
unset命令用来删除一个变量。
unset
unset NAME
这个命令不是很有用。因为不存在的 Bash 变量一律等于空字符串,所以即使unset命令删除了变量,还是可以读取这个变量,值为空字符串。
所以,删除一个变量,也可以将这个变量设成空字符串。
$ foo='' $ foo=
上面两种写法,都是删除了变量foo。由于不存在的值默认为空字符串,所以后一种写法可以在等号右边不写任何值。
用户创建的变量仅可用于当前 Shell,子 Shell 默认读取不到父 Shell 定义的变量。为了把变量传递给子 Shell,需要使用export命令。这样输出的变量,对于子 Shell 来说就是环境变量。
export
export命令用来向子 Shell 输出变量。
NAME=foo export NAME
上面命令输出了变量NAME。变量的赋值和输出也可以在一个步骤中完成。
NAME
export NAME=value
上面命令执行后,当前 Shell 及随后新建的子 Shell,都可以读取变量$NAME。
$NAME
子 Shell 如果修改继承的变量,不会影响父 Shell。
# 输出变量 $foo $ export foo=bar # 新建子 Shell $ bash # 读取 $foo $ echo $foo bar # 修改继承的变量 $ foo=baz # 退出子 Shell $ exit # 读取 $foo $ echo $foo bar
上面例子中,子 Shell 修改了继承的变量$foo,对父 Shell 没有影响。
Bash 提供一些特殊变量。这些变量的值由 Shell 提供,用户不能进行赋值。
(1)$?
$?
$?为上一个命令的退出码,用来判断上一个命令是否执行成功。返回值是0,表示上一个命令执行成功;如果是非零,上一个命令执行失败。
0
$ ls doesnotexist ls: doesnotexist: No such file or directory $ echo $? 1
上面例子中,ls命令查看一个不存在的文件,导致报错。$?为1,表示上一个命令执行失败。
ls
(2)$$
$$
$$为当前 Shell 的进程 ID。
$ echo $$ 10662
这个特殊变量可以用来命名临时文件。
LOGFILE=/tmp/output_log.$$
(3)$_
$_
$_为上一个命令的最后一个参数。
$ grep dictionary /usr/share/dict/words dictionary $ echo $_ /usr/share/dict/words
(4)$!
$!
$!为最近一个后台执行的异步命令的进程 ID。
$ firefox & [1] 11064 $ echo $! 11064
上面例子中,firefox是后台运行的命令,$!返回该命令的进程 ID。
firefox
(5)$0
$0
$0为当前 Shell 的名称(在命令行直接执行时)或者脚本名(在脚本中执行时)。
$ echo $0 bash
上面例子中,$0返回当前运行的是 Bash。
(6)$-
$-
$-为当前 Shell 的启动参数。
$ echo $- himBHs
(7)$@和$#
$@
$#
$@和$#表示脚本的参数数量,参见脚本一章。
Bash 提供四个特殊语法,跟变量的默认值有关,目的是保证变量不为空。
${varname:-word}
上面语法的含义是,如果变量varname存在且不为空,则返回它的值,否则返回word。它的目的是返回一个默认值,比如${count:-0}表示变量count不存在时返回0。
varname
word
${count:-0}
count
${varname:=word}
上面语法的含义是,如果变量varname存在且不为空,则返回它的值,否则将它设为word,并且返回word。它的目的是设置变量的默认值,比如${count:=0}表示变量count不存在时返回0,且将count设为0。
${count:=0}
${varname:+word}
上面语法的含义是,如果变量名存在且不为空,则返回word,否则返回空值。它的目的是测试变量是否存在,比如${count:+1}表示变量count存在时返回1(表示true),否则返回空值。
${count:+1}
1
true
${varname:?message}
上面语法的含义是,如果变量varname存在且不为空,则返回它的值,否则打印出varname: message,并中断脚本的执行。如果省略了message,则输出默认的信息“parameter null or not set.”。它的目的是防止变量未定义,比如${count:?"undefined!"}表示变量count未定义时就中断执行,抛出错误,返回给定的报错信息undefined!。
varname: message
message
${count:?"undefined!"}
undefined!
上面四种语法如果用在脚本中,变量名的部分可以用到数字1到9,表示脚本的参数。
9
filename=${1:?"filename missing."}
上面代码出现在脚本中,1表示脚本的第一个参数。如果该参数不存在,就退出脚本并报错。
declare命令可以声明一些特殊类型的变量,为变量设置一些限制,比如声明只读类型的变量和整数类型的变量。
declare
它的语法形式如下。
declare OPTION VARIABLE=value
declare命令的主要参数(OPTION)如下。
-a
-f
-F
-i
-l
-p
-r
-u
-x
declare命令如果用在函数中,声明的变量只在函数内部有效,等同于local命令。
local
不带任何参数时,declare命令输出当前环境的所有变量,包括函数在内,等同于不带有任何参数的set命令。
$ declare
(1)-i参数
-i参数声明整数变量以后,可以直接进行数学运算。
$ declare -i val1=12 val2=5 $ declare -i result $ result=val1*val2 $ echo $result 60
上面例子中,如果变量result不声明为整数,val1*val2会被当作字面量,不会进行整数运算。另外,val1和val2其实不需要声明为整数,因为只要result声明为整数,它的赋值就会自动解释为整数运算。
result
val1*val2
val1
val2
注意,一个变量声明为整数以后,依然可以被改写为字符串。
$ declare -i var=12 $ var=foo $ echo $var 0
上面例子中,变量var声明为整数,覆盖以后,Bash 不会报错,但会赋以不确定的值,上面的例子中可能输出0,也可能输出的是3。
var
(2)-x参数
-x参数等同于export命令,可以输出一个变量为子 Shell 的环境变量。
$ declare -x foo # 等同于 $ export foo
(3)-r参数
-r参数可以声明只读变量,无法改变变量值,也不能unset变量。
$ declare -r bar=1 $ bar=2 bash: bar:只读变量 $ echo $? 1 $ unset bar bash: bar:只读变量 $ echo $? 1
上面例子中,后两个赋值语句都会报错,命令执行失败。
(4)-u参数
-u参数声明变量为大写字母,可以自动把变量值转成大写字母。
$ declare -u foo $ foo=upper $ echo $foo UPPER
(5)-l参数
-l参数声明变量为小写字母,可以自动把变量值转成小写字母。
$ declare -l bar $ bar=LOWER $ echo $bar lower
(6)-p参数
-p参数输出变量信息。
$ foo=hello $ declare -p foo declare -- foo="hello" $ declare -p bar bar:未找到
上面例子中,declare -p可以输出已定义变量的值,对于未定义的变量,会提示找不到。
declare -p
如果不提供变量名,declare -p输出所有变量的信息。
$ declare -p
(7)-f参数
-f参数输出当前环境的所有函数,包括它的定义。
$ declare -f
(8)-F参数
-F参数输出当前环境的所有函数名,不包含函数定义。
$ declare -F
readonly命令等同于declare -r,用来声明只读变量,不能改变变量值,也不能unset变量。
readonly
declare -r
$ readonly foo=1 $ foo=2 bash: foo:只读变量 $ echo $? 1
上面例子中,更改只读变量foo会报错,命令执行失败。
readonly命令有三个参数。
let命令声明变量时,可以直接执行算术表达式。
let
$ let foo=1+2 $ echo $foo 3
上面例子中,let命令可以直接计算1 + 2。
1 + 2
let命令的参数表达式如果包含空格,就需要使用引号。
$ let "foo = 1 + 2"
let可以同时对多个变量赋值,赋值表达式之间使用空格分隔。
$ let "v1 = 1" "v2 = v1++" $ echo $v1,$v2 2,1
上面例子中,let声明了两个变量v1和v2,其中v2等于v1++,表示先返回v1的值,然后v1自增。
v1
v2
v1++
这种语法支持的运算符,参考《Bash 的算术运算》一章。
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8