变量的类别
环境变量
已经在 Bash 环境中定义好的变量,Bash 自带的变量。可以直接使用,通常是系统定义好的。也可以由父 Shell 传入子 Shell。
环境变量很少改变,所以可以视为常量。名称都为大写,若想要自定义环境变量,尽量遵循这种命名规范。
查看已有的环境变量:
$ env
# 或者
$ 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:当前用户的用户名。
查看指定的环境变量:
$ printenv PATH
$ echo $PATH
自定义变量
1、命令行中直接定义,随着当前 Shell 的结束(比如通过:exit 命令,或直接关闭终端窗口)而消失。
2、Shell script 文件中定义变量。相比较生命周期更短,当 Script 执行完毕后,变量就不存在了。可以先执行 Script 文件,在尝试在终端窗口中执行 echo ${varInScript}
进行输出测试。
创建变量
命名
如同其他开发语言一样,命名规则都是基本相同的。命名风格可以使用下面的几种常见的命名方式:
- 下划线:var_name
- 大驼峰:VarName
- 小驼峰:varName
因为我日常主要使用 Java 编程语言,后面编写 Shell 时统一使用小驼峰命名风格。
定义声明
variable=value
,等号的左右不能有空格
值类型
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)) # 变量值可以是数学运算的结果
读取变量
# 定义
$ myVar=C99
# 完整格式
$ echo "Hello ${myVar}"
Hello C99
# 简写(通过美元扩展符)
$ echo "Hello $myVar"
Hello C99
建议使用完整格式:
- 容易识别
- 更好的能被 Bash 读取
为什么说能更好的被 Bash 读取,如下面的例子:
$ echo "$100"
00
我们想输出一百美元的字符串,但输出了00
。因为当读取一个不存在的变量时,Bash 会输出一个空字符串:''
,所以输出了00
。
这种情况下需要在$
符号前添加一个转义符\
进行转义。那么为什么不按照完整的格式读取变量呢?即容易被识别意图,也可以避免这类问题。
还有一种情况,读取的变量的值是另一个变量的值:
$ myVar=C99
$ myVar2=myVar
$ echo ${myVar2}
myVar
输出结果不是最终值,可以:
$ echo ${!myVar2}
C99
${!varName}
其中的 !
即读取终值的意思。
也可以在定义变量时,赋实际值:
$ myVar2=${myVar}
删除变量
$ unset myVar
$ myVar=''
$ myVar=
上面提到,若读取了一个不存在的变量时,会输出''
。所以实际上,即使删除了变量,再次读取时依旧会有一个默认的''
。
导出变量(export 命令)
export 是一个内置命令:
$ type export
export is a shell builtin
在讨论该命令的功能前,强烈建议先看下Linux
类下的环境变量 & 加强理解 Shell
一文。
export 命令用于将变量输出为环境变量,或者将函数输出为环境变量。
用户创建的变量仅可用于当前 Shell,子 Shell 默认读取不到父 Shell 定义的变量。为了把变量传递给子 Shell,需要使用 export 命令。这样的变量,对于子 Shell 来说就是环境变量。
父、子 Shell 的概念在环境变量 & 加强理解 Shell
一文中已进行了描述。这里在进行一次演示:
$ type -t bash
file
# 查看命令行窗口中的 Shell 进程 PID(30142)
$ ps -ef | grep bash
root 1769 30142 0 10:47 pts/0 00:00:00 grep --color=auto bash
root 30142 30138 0 10:28 pts/0 00:00:00 -bash
# 或者通过上面提到的环境变量: BASHPID 查看当前命令行窗口中的 Shell 进程 PID
$ echo ${BASHPID}
30142
# 新启一个 Shell 进程
$ bash
# 查看 Shell 进程信息(PID 为1801的 Shell 进程的 PPID 正为30142)
$ ps -ef | grep bash
root 1801 30142 1 10:47 pts/0 00:00:00 bash
root 2006 1801 0 10:48 pts/0 00:00:00 grep --color=auto bash
root 30142 30138 0 10:28 pts/0 00:00:00 -bash
# 通过环境变量 BASHPID 查看子 Shell 进程 PID
$ echo ${BASHPID}
1801
# 退出当前 Shell 进程
$ exit
exit
# 再次查看 Shell 进程信息
$ ps -ef | grep bash
root 2979 30142 0 10:52 pts/0 00:00:00 grep --color=auto bash
root 30142 30138 0 10:28 pts/0 00:00:00 -bash
语法:
# 第一种方式
NAME=C99
export NAME
# 第二种方式
export NAME=C99
测试:
# 定义一个普通变量 与 一个导出变量
$ MY_VAR1=C99
$ export MY_VAR2=C999
# 启动一个子 Shell 进程
$ bash
# 在子 Shell 进程中分别输出上面的两个变量
$ echo ${MY_VAR1}
$ echo ${MY_VAR2}
C999
特殊变量
Bash 提供的一些特殊变量,这些变量的值由 Bash 进行控制,用户不能改变。
1、$?
:上一个执行命令的退出码,可以用来判断上一个执行命令是否执行成功:0 success、非0 failure
$ ls ./
test.sh
$ echo $?
0
$ ls noexists
ls: cannot access noexists: No such file or directory
$ echo $?
2
2、$$
:当前 Shell 进程的 PID
$ echo $$
2790
# 输出环境变量进行复验
$ echo ${BASHPID}
2790
3、$_
:上一个命令的最后一个参数
$ echo A B C
A B C
$ echo $_
C
4、$!
:最近一个后台异步执行的命令的进程 PID
# '&' 符号可以使命令后台异步执行
$ echo A B C &
[1] 9554
A B C
$ echo $!
9554
[1]+ Done echo A B C
5、$0
:输出当前 Shell 的名称
命令行环境中输出
$ echo $0
-bash
Shell Script 文件中输出
Script 文件内容:
#!/bin/bash
echo $0
执行:
$ ./test.sh
./test.sh
6、$-
:当前 Shell 的启动参数
$ echo $-
himBH
himBH 每个字母都是一个 Shell 启动参数,具体含义可以搜索查询一下。
7、$@、$#、$N(number)
$#
表示脚本的参数数量$@
表示脚本的参数值$N
表示第N个 Shell 执行参数(第0个默认为 Shell script 文件名称)
#!/bin/bash
echo $@
echo $#
echo $0
echo $1
echo $2
echo $3
$ ./test.sh p1 p2
全部参数: p1 p2
参数数量: 2
第0个参数: ./test.sh
第1个参数: p1
第2个参数: p2
第3个参数:
变量的默认值
${varName:-defVal}
:变量存在且不为空,返回变量值。否则返回默认值(用于保证返回值不为空)。${varName:=defVal}
:变量存在且不为空,返回变量值。否则设置默认值(用于设置默认值)。${varName:+defVal}
:变量存在且不为空,返回默认值。否则返回空(用于校验变量是否存在)。${varName:?message}
:变量存在且不为空,返回变量值。否则中断 Script 执行,并输出 message。如果不指定 message,默认输出:varName: parameter null or not set
(用于参数断言)
上面四种取默认值的语法,在 Script 执行中时,若获取的是 Script 启动参数,可以用1 - 9
来代替。比如:
${1:?message}
:Script 第一个参数不存在即中止执行,并提示指定的错误信息。${2:-defVal}
:Script 第二个参数不存在则返回默认值。- …
declare 命令
用于将普通变量改为特殊变量,为变量增加一些特性。比如声明变量为只读的。
语法:
declare OPTION VARIABLE=value
主要参数(OPTION)如下:
1、-a:声明数组变量。
2、-f:输出所有函数(含实现)。
3、-F:输出所有函数名。
4、-i:声明整数变量。参数声明整数变量以后,可以直接进行数学运算。
$ declare -i tempVar
$ var1=1
$ var2=2
$ tempVar=var1+var2
$ echo ${tempVar}
3
我们仅需要将接收结果的变量设置为整数变量,不需要对所有参与运算的变量设置为整数变量
如果不设定为整数变量,仅会进行值拼接,而不是进行运算。
5、-l:声明变量值为小写字母。
$ declare -l var1=LOWER
$ echo ${var1}
lower
$ var1=LOWER
$ echo ${var1}
lower
6、-u:声明变量为大写字母。
$ declare -u var1=upper
$ echo ${var1}
UPPER
$ var1=upper
$ echo ${var1}
UPPER
7、-r:声明只读变量。
$ declare -r var1
$ var1=upper
-bash: var1: readonly variable
$ unset var1
-bash: unset: var1: cannot unset: readonly variable
8、-x:导出变量为环境变量。
$ declare -x var1
# 等同于
$ export var1
9、-p:查看变量信息。
$ declare -rux var1=upper
$ declare -p var1
declare -rxu var1="UPPER"
若 declare 命令如果用在函数中,声明的变量只在函数内部有效,等同于 local 命令。
若不带任何参数时,declare 命令输出当前环境的所有变量,包括函数在内,等同于不带有任何参数的 set 命令。
readonly 命令
等同于declare -r
,用来声明只读变量,不能改变变量值,也不能 unset 变量。
三个参数:
- -f:定义只读函数。
- -a:定义只读数组变量。
- -p:显示系统中全部只读变量列表。
$ readonly var1
# 实际上就是对变量添加了 -r 属性
$ declare -p var1
declare -r var1="1"