# 变量
# 赋值
- 在 shell 中可以执行以下格式的命令,给变量赋值:
var=value [var=value]...
- 赋值时,如果变量不存在,则会自动创建它。
- 创建变量时不需要声明其数据类型。
- 赋值符号 = 的前后不能有空格,否则会被视作一条命令。如下:
[root@CentOS ~]# A=1 [root@CentOS ~]# A =1 -bash: A: command not found [root@CentOS ~]# A= 1 -bash: 1: command not found
# 取值
读取变量时,变量名区分大小写。
[root@CentOS ~]# a=1 [root@CentOS ~]# echo $a 1 [root@CentOS ~]# echo $A # 变量名不同,因此读取不到值
用
$var
或${var}
的格式可以读取一个变量的值。- 例:
[root@CentOS ~]# a=1 [root@CentOS ~]# echo $a # 显示变量 a 的值 1 [root@CentOS ~]# echo a # 不加取值符号 $ 则是显示字符串 a a [root@CentOS ~]# $a # 没有 echo 则是将变量 a 的值当作一条命令去执行 -bash: 1: command not found
- 如果目标变量不存在,则读取到的值为空:
[root@CentOS ~]# echo $a # 这里相当于执行 echo [root@CentOS ~]# echo $a+1 # 这里相当于执行 echo +1 +1
- 比起
$var
,更推荐使用${var}
格式,可实现更多功能:[root@CentOS ~]# a=0 [root@CentOS ~]# b=1 [root@CentOS ~]# echo $a$b 12 [root@CentOS ~]# echo ${a}b # 用花括号有利于确定变量名的边界 1b
$var
支持在读取变量时设置默认值:[root@CentOS ~]# echo ${a-0} # 如果变量存在(包括为空),则返回其值,否则返回默认值 1 [root@CentOS ~]# echo ${c-0} 0 [root@CentOS ~]# echo ${c-b} # 默认值应该为一个常量,不会当作变量名进行取值 b [root@CentOS ~]# echo ${a:-Null} # 如果变量存在且不为空,则返回其值,否则返回默认值 Null
[root@CentOS ~]# echo ${a+True} # 如果变量存在,则返回默认值,否则返回空 True [root@CentOS ~]# echo ${a:+True} # 如果变量存在且不为空,则返回默认值,否则返回空 True
[root@CentOS ~]# echo ${c=3} # 如果变量存在,则返回其值。否则返回默认值,并给该变量赋值 3 [root@CentOS ~]# echo $c # 该变量已被创建 3 [root@CentOS ~]# echo ${c:=3} # 如果变量不存在或为空,则返回默认值,并给该变量赋值 3 [root@CentOS ~]# : ${PATH:=/bin} # 在脚本中可用这种方式创建变量并采用默认值
[root@CentOS ~]# a=PWD [root@CentOS ~]# echo ${!a} # ${!var} 语法表示先获取变量 var 的值,再用该值作为变量名,获取另一个变量的值 /root
- Bash v4.0 开始,可通过以下语法,在读取到变量值之后,转换大小写字母:可执行
[root@CentOS ~]# a=hello [root@CentOS ~]# echo ${a^} # 将第一个字母变为大写。后面所有字母不变,可能是大写或小写 Hello [root@CentOS ~]# echo ${a^^} # 将所有字母变为大写 HELLO [root@CentOS ~]# echo ${a,,} # 将所有字母变为小写 hello [root@CentOS ~]# echo ${a~~} # 反转所有字母的大小写 HELLO
bash --version
查看本机的 bash 版本。
- 例:
用
`command`
或$(command)
的格式可以获取一条命令的 stdout 。- 例:
[root@CentOS ~]# a=`echo 1` [root@CentOS ~]# echo $a 1 [root@CentOS ~]# a=`echo1` # 这里获取到的 stdout 为空 -bash: echo1: command not found [root@CentOS ~]# echo $a
- 使用
$(command)
时,( )
有利于确定命令的边界边界,从而支持嵌套使用。如下:[root@CentOS ~]# echo `echo `echo 1`` # 相邻的两个 ` 相互匹配,不支持嵌套 echo 1 [root@CentOS ~]# echo $(echo $(echo 1)) 1
- 例:
用
$((expression))
或$[expression]
的格式可以获取一个表达式的运算结果。- 该表达式是 C 语言风格的,可以使用变量、常量,可以使用算术运算符
+ - * / %
。如下:[root@CentOS ~]# echo $[a+2] 3 [root@CentOS ~]# echo $[ a + 2 ] # 运算符之间可以插入空格 3 [root@CentOS ~]# echo $[(a+2)*3] 9
[root@CentOS ~]# echo $[0%2] # 取模运算 0 [root@CentOS ~]# echo $[1%2] 1
[root@CentOS ~]# echo $[3+4 == 5] # 比较运算 0 [root@CentOS ~]# echo $[3+4 > 5] 1
- 该表达式中不支持使用小数:
[root@CentOS ~]# echo $[1.0] -bash: 1.0: syntax error: invalid arithmetic operator (error token is ".0")
- 如果计算结果为小数,则小数部分会直接舍去,不会四舍五入:
[root@CentOS ~]# echo $[0/3] 0 [root@CentOS ~]# echo $[1/3] 0 [root@CentOS ~]# echo $[3/3] 1
- 还可以使用以下位运算符:
[root@CentOS ~]# echo $[8 & 1] # 按位与 0 [root@CentOS ~]# echo $[8 | 1] # 按位或 9 [root@CentOS ~]# echo $[8 ^ 1] # 按位异或 9 [root@CentOS ~]# echo $[~8] # 按位非 -9 [root@CentOS ~]# echo $[8 << 1] # 左移 16 [root@CentOS ~]# echo $[8 >> 1] # 右移 4
- 该表达式是 C 语言风格的,可以使用变量、常量,可以使用算术运算符
# 变量的作用域
# 全局变量
:global variable ,是直接通过赋值符号 = 创建的变量,只能在当前 shell 中访问。
- 可以在赋值变量的同时执行一条命令,此时该变量的作用域仅限于该命令。
[root@CentOS ~]# A=1 B=1 env | grep -e A= -e B= A=1 B=1 [root@CentOS ~]# echo $A $B # 临时赋值的变量并不会保留
[root@CentOS ~]# export A=1 env | grep A= # export 等命令不支持该语法 [root@CentOS ~]# echo $A
- 可以从文件中导入多个变量,如下:作为全局变量导入:
echo IP=127.0.0.1 > .env echo PORT=80 >> .env
作为环境变量导入:source .env
export `cat .env | sed 's/#.*//g' | xargs`
# 局部变量
:local variable ,是通过 local 命令定义的变量,只能在函数内创建,当函数运行结束时就会被自动删除。
# 环境变量
:environment variable ,是通过 set 、export 等命令定义的变量,可以被当前 shell 及其子 shell 访问。
环境变量的名称通常全部大写。
退出当前 shell 之后,定义的所有局部变量、环境变量都会被删除。因此,
- 在子 shell 中创建的环境变量,不能被父 shell 访问。
- 子 shell 会继承父 shell 的环境变量,但不能影响父 shell 的环境变量。
常用的环境变量:
USER=root LOGNAME=root HOME=/root HOSTNAME=VM_16_6_CentOS SHELL=/bin/bash PWD=/root PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin # 目录之间用冒号 : 分隔 TERM=linux LANG=en_US.utf8 # PROMPT_COMMAND="history -a" # 每次显示终端提示符 $PS1 之前,先执行命令 $PROMPT_COMMAND PS1='[\u@\h \w]\$ ' # 终端提示符的格式。在终端每输入一行命令,就会显示一行新的终端提示符 PS2='>' # 输入多行命令时,终端提示符的格式 PS3='#?' # 在 shell 脚本中使用 select 命令时,每行显示的前缀 PS4='+' # set -x 模式下,每行显示的前缀
- 变量 PS1、PS2 只在 shell 解释器的交互模式中存在。
每次用户新建终端时会重新加载环境变量,可以采用以下方式永久配置环境变量:
echo 'export PATH=$PATH:/root/bin' >> /etc/profile echo 'export PATH=$PATH:/usr/lib/jdk' > /etc/profile.d/java.sh
# 内置变量
:shell 解释器提供了一些内置变量,可以在终端、脚本、函数中调用。
常用的内置变量:
$0 # 当前脚本启动时,使用的文件名。这取决于启动命令,可能是绝对路径或相对路径 $1 # 当前脚本启动时,输入的第 1 个参数。类似的还有 $2、$3、$4 等 $* # 当前脚本启动时,输入的全部参数(作为一个字符串返回) $@ # 当前脚本启动时,输入的全部参数(作为多个字符串返回) ${@:2} # 从第 2 个参数开始的全部参数 $# # 当前脚本启动时,输入参数的个数 $? # 上一条的命令的返回码 $! # 上一个运行的后台进程的 PID $$ # 当前进程的 PID $PPID # 父进程的 PID $FUNCNAME # 当前执行的函数名,以及调用它的每一层父函数名 ${FUNCNAME[0]} # 当前执行的函数名 $RANDOM # 返回一个 0 ~ 32767 范围的整数
例:
[root@CentOS ~]# echo $0 -bash [root@CentOS ~]# echo $1 [root@CentOS ~]# echo $* [root@CentOS ~]# echo $# 0 [root@CentOS ~]# echo $? 0 [root@CentOS ~]# echo $$ 11252
例:获取随机数
[root@CentOS ~]# echo $RANDOM 20383 [root@CentOS ~]# echo $[RANDOM*10/32768] # 返回一个 0~9 范围的随机数 7
# 数据类型
# 整型
- 整型是 int 型常数。而浮点型常数会被视作字符串。
- 例:
[root@CentOS ~]# a=1 [root@CentOS ~]# a=$[a+5] # 对变量进行算术运算 [root@CentOS ~]# a=$[a/2] [root@CentOS ~]# echo $a 3
# 字符串
关于字符串的定界符。
- shell 中定界符分为两种:
- 双引号
- 单引号
- 使用定界符可以明确字符串的界限:
[root@CentOS ~]# echo Hello | wc -l 1 [root@CentOS ~]# echo "Hello |" wc -l Hello | wc -l [root@CentOS ~]# echo "Hello | wc -l" Hello | wc -l
- 打印变量的值时,换行符 \n 会显示成空格,此时可加上字符串定界符:
[root@CentOS ~]# stdout=`echo -e '1\n2'` [root@CentOS ~]# echo $stdout 1 2 [root@CentOS ~]# echo "$stdout" 1 2
- shell 中定界符分为两种:
如果字符串中包含了与定界符相同的字符,会导致字符串被截断。可采用以下几种解决方法:
- 将与定界符相同的字符声明为转义字符:
[root@CentOS ~]# echo \" " [root@CentOS ~]# echo \' ' [root@CentOS ~]# echo "\"A\'B" # 定界符为双引号时,不支持转义 \' "A\'B [root@CentOS ~]# echo '\"' # 定界符为单引号时,不支持转义字符 \"
- 将多个子字符串拼接在一起,依然会被视作一个字符串:
[root@CentOS ~]# echo 'A'\"\''B' # 每个子字符串可以使用不同的定界符 A"'B
- 将与定界符相同的字符声明为转义字符:
定界符为单引号时:
- 字符串中的符号
$
、`
、转义字符,会被视作普通字符:[root@CentOS ~]# echo "$A \$B" # 这里 \$ 被视作一个转义字符 $B [root@CentOS ~]# echo '$A \$B' # 这里 \$ 被视作一个普通字符 $A \$B [root@CentOS ~]# echo '`cat f1`' `cat f1`
- 用
$'string'
的语法可以解析转义字符:[root@CentOS ~]# echo $'\"Hello\'' "Hello' [root@CentOS ~]# echo $'hello\n\x41' hello A [root@CentOS ~]# echo $'\u270B' # 解析 Unicode 字符 ✋
- 字符串中的符号
字符串的处理示例:
[root@CentOS ~]# str=hello [root@CentOS ~]# echo ${#str} # 获取字符串的长度 5 [root@CentOS ~]# echo ${str:0} # 获取从第 0 个字符开始的所有字符 hello [root@CentOS ~]# echo ${str:0:3} # 获取从第 0 个字符开始的 3 个字符 hel [root@CentOS ~]# echo ${str:4:3} o
[root@CentOS ~]# echo ${str/l/_} # (从左开始)寻找第一个匹配 l 的位置,执行一次字符串替换 he_lo [root@CentOS ~]# echo ${str//l/_} # 寻找所有匹配 l 的位置,执行一次字符串替换 he__o [root@CentOS ~]# echo ${str#*l} # 删掉前缀 *l lo [root@CentOS ~]# echo ${str%l*} # 删掉后缀 l* hel
# 数组
- shell 只支持一维数组,但不限制数组的长度。
- 定义数组时要用小括号包住、用空格作为分隔符。
- 用
${数组名[下标]}
的格式可以读取数组的某项元素,用$数组名
的格式读取到的则是数组的第一项元素。 - 例:
[root@CentOS ~]# array=(1 2 3 4) [root@CentOS ~]# echo ${array[3]} 4 [root@CentOS ~]# echo ${array[4]} # 下标无效时,返回值为空 [root@CentOS ~]# echo $array 1
- 数组的处理示例:
echo ${array[@]} # 用 @ 作为下标,可获取数组的全部元素 echo ${#array[@]} # 获取数组的长度 echo ${array[@]:0} # 获取切片(0:] echo ${array[@]:0:5} # 获取切片(0:5] a[${#a[*]}]=5 # 在数组末尾添加元素
# 创建变量
# read
$ read <var>... # 读取键盘的输入,赋值给一个或多个变量
-p "Please input:" # 显示提示
-t 5 # 等待用户的输入最多 5 秒
-n 5 # 用户最多输入 5 个字符,就会被自动结束输入
-a # 将输入保存为数组类型
-e # 允许在输入时按 Tab 自动补全
-r # 不将反斜杠 \ 视作转义字符。默认会转义
-s # 采用密文的形式输入
- 例:
read a read -p "请按下任意按键" -n 1 key
- 当用户按下回车时就会结束输入。
- 输入多个值时要以空格分隔。
- 如果用户输入了太多值,多余的值会一起赋值给最后一个变量。如下:
[root@CentOS ~]# read a b 1 2 3 [root@CentOS ~]# echo $a, $b 1, 2 3
# set
$ set # 显示当前 shell 的所有变量、函数
-a <var> # 将变量声明为环境变量(如果该变量不存在则无效)
-e # exit ,开启 e 模式,如果执行某个命令的返回码非 0 ,则立即退出当前 shell 。通常在 shell 脚本中启用,当命令执行失败时自动终止脚本
+e # 用减号 - 开启一个模式,用加号 + 关闭一个模式
-u # unset ,开启 u 模式,用到了未定义的变量时会报错
-x # execute ,开启 x 模式,执行每条命令之前,打印该命令到 stdout(每行开头会显示加号 + )
-o pipefail # 通过管道符执行多个 shell 命令时,如果所有命令的返回码都为 0 ,最终返回码才为 0 。如果有多个命令的返回码非 0 ,则将最后一个非 0 的返回码作为最终返回码
- 在一行内执行多个 shell 命令时,最后执行的一个命令的返回码,会作为最终的返回码。如下:
[root@CentOS ~]# true | false # 先执行 true 命令,后执行 false 命令 [root@CentOS ~]# echo $? 1
[root@CentOS ~]# true || false # 最后执行的是 true [root@CentOS ~]# echo $? 0
[root@CentOS ~]# x=`false` # 最后执行的是 false [root@CentOS ~]# echo $? 1
[root@CentOS ~]# echo `false` # 最后执行的是 echo [root@CentOS ~]# echo $? 0
- 通过管道符执行多个 shell 命令时,例如:
[root@CentOS ~]# false | true
- 如果处于
set -e
模式,则不会导致 shell 退出。 - 如果处于
set -eo pipefail
模式,则会导致 shell 退出,因此能检查管道符连接的每个命令都执行成功。
- 如果处于
- 通过管道符执行多个 shell 命令时,例如:
# unset
$ unset <var>... # 删除当前 shell 的指定变量
# env
$ env # 显示当前 shell 的所有环境变量
# export
$ export # 显示当前 shell 的所有环境变量及其 declare 语句
<var>... # 将变量声明为环境变量(如果该变量不存在则创建它)
<var>=<value>... # 将变量声明为环境变量并赋值
# declare
$ declare # 显示当前 shell 的所有变量、函数
-i # 显示所有整型变量
-i x # 将变量声明为整型
-i x=0 # 将变量声明为整型并赋值
+i x # 用加号 + 取消声明
- shell 中创建的变量默认为字符串类型,用 declare 命令可以声明变量的数据类型。
- 可用的选项:
- a :数组
- f :函数
- i :整型变量
- r :只读变量
- x :环境变量
- 如果用字符串给整型变量赋值,则整型变量的值会变成 0 。如下:
[root@CentOS ~]# declare -i x [root@CentOS ~]# echo $x [root@CentOS ~]# x=hello [root@CentOS ~]# echo $x 0
- 只读变量一旦创建就不能被修改、不能被删除,只能等当前 shell 退出之后被销毁。
- 不能用 unset 命令删除,不能用 declare +x 取消只读,也不能用 declare、readonly 命令重新赋值,
# readonly
$ readonly # 显示所有只读变量
<var> # 将变量声明为只读变量
<var>=<value> # 将变量声明为只读变量并赋值
# 数学运算
# bc
:用于打开一个简单的计算器终端。
- 例:通过管道符输入要计算的表达式
[root@CentOS ~]# echo "scale=2; 1/3.14" | bc # scale 是小数点后保留的位数 .31
# expr
expr <expression> # 计算一个表达式的值,并输出到 stdout
算术运算的示例:
[root@CentOS ~]# expr 1+2 # 每个运算符、操作数之间,必须用空格分隔 1+2 [root@CentOS ~]# expr 1 + 2 3 [root@CentOS ~]# expr 1 - 2 -1 [root@CentOS ~]# expr \( 1 + 2 \) * 3 # shell 解释器中的特殊字符要进行转义,才能传递给 expr 命令 9 [root@CentOS ~]# expr 1 / 2 0 [root@CentOS ~]# expr 1 % 2 1
比较运算的示例:
[root@CentOS ~]# expr 1 \> 2 # 返回码用 1、0 分别表示 True、False 0 [root@CentOS ~]# expr 1 \< 2 1 [root@CentOS ~]# expr 1 \>= 2 0 [root@CentOS ~]# expr 1 \<= 2 1 [root@CentOS ~]# expr 1 != 2 1 [root@CentOS ~]# expr 1 == 2 0
逻辑运算的示例:
[root@CentOS ~]# expr 2 \& 3 # 与运算:如果两个操作数都存在且不为 0 ,则返回左操作数,否则返回 0 2 [root@CentOS ~]# expr 3 \& '' 0
[root@CentOS ~]# expr 2 \| 3 # 或运算:如果左操作数存在且不为 0 ,则返回它,否则返回右操作数 2 [root@CentOS ~]# expr 0 \| 3 3
支持在表达式中读取变量:
[root@CentOS ~]# expr 1 + $a 2 [root@CentOS ~]# expr 1 + $a # 如果读取一个不存在的变量,则可能导致语法错误 expr: syntax error
# let
let <expression>... # 执行一个或多个算术表达式,可以是赋值语句
- 比 expr 命令更方便:
- 表达式中不需要加空格。
- 表达式中的变量不需要用 $ 取值。
- 支持自加、自减等运算符。
- 例:
[root@CentOS ~]# let a=1+2 [root@CentOS ~]# let a++ [root@CentOS ~]# let a+=10 [root@CentOS ~]# echo $a b=a*2 14 28
- let 命令的返回码通常为 0 。如果最后一个表达式的值为 0 ,则返回码为 1 。
[root@CentOS ~]# let a=0 [root@CentOS ~]# echo $? 1