1、变量的定义与使用
在Shell脚本中,我们可以自由定义变量来存储数据。变量定义的语法如下:
var_name="value"
例如,定义一个变量greeting
并给它赋值为Hello
:
greeting="Hello"
变量的使用只需要在变量名前添加$
符号来引用这个变量即可:
echo $greeting # Hello
echo ${greeting} # Hello
变量名外面的花括号{}
是可选的,{}
可以帮助解释器识别变量的边界,例如下面这种情况:
echo "${greeting}Shell" # HelloShell
如果不给变量greeting
添加{}
,写成echo "$greetingShell"
,解释器就会把greetingShell
当成一个变量,最终导致结果为空
2、变量的命名规范
和其它语言类似,Shell变量的命名须遵循以下规则:
- 只包含字母、数字和下划线:注意字母大小写敏感,不能以数字开头
- 避免使用Shell关键字:例如
if
、then
、else
、fi
等 - 使用大写字母表示常量:习惯上,常量的命名通常使用大写字母
- 避免使用特殊符号:例如
$
等,Shell特殊符号会与Shell语法产生冲突 - 避免使用空格:变量名和等号两侧不能包含空格,空格通常用于分隔命令和参数
3、Shell变量的类型
Shell支持不同类型的变量,其中一些主要的类型包括:
1)字符串变量
在Shell中,变量通常被视为字符串。可以使用单引号'
或双引号"
来定义字符串,例如:
greeting='Hello'
greeting="Hello"
2)整数变量
在一些Shell中,可以使用declare
或typeset
命令来声明整数变量,例如:
declare -i my_integer=10
这样的声明告诉Shell将my_integer
视为整数,如果将非整数值赋值给它,Shell会尝试将其转换为整数
3)数组变量
Shell允许在一个变量中存储多个值。Shell只支持一维数组,初始化时不需要定义数组大小,例如:
my_array=(A B "C" D)
与其它语言类似,数组元素的下标从0开始,读取数组元素值的一般格式为:
${array_name[index]}
例如,读取第三个元素:
echo "${my_array[2]}" # C
4、Shell变量的分类
Shell中的变量主要分为系统变量、用户(自定义)变量、位置变量和特殊变量
4.1、系统变量(全局变量)
系统变量(环境变量)是指在Linux系统中提前创建的一类变量,主要用于设置用户的工作环境,包括用户宿主目录、命令查找路径等。环境变量的值由Linux系统自动维护,当用户注销时这些值将丢失
环境变量适用于所有用户进程,一般在/etc/profile
文件中定义。一般地,所有环境变量都大写,且必须用export
命令导出
1)设置环境变量
编辑配置文件/etc/profile
,将Shell变量输出为环境变量(全局变量):
# 方式1
var_name=value; export var_name
# 方式2
var_name=value
export var_name
# 方式3(推荐)
export var_name=value
2)刷新环境变量
刷新配置文件/etc/profile
,让修改后的配置信息立即生效:
source /etc/profile
3)查询环境变量
查询环境变量的值,检查环境变量是否生效:
echo $var_name
环境变量其它相关命令如下:
# 查看系统所有环境变量
env
# 清除系统环境变量
unset var_name
4.2、用户变量(局部变量)
用户变量(局部变量)在用户当前Shell生命周期的脚本中使用,其他Shell启动的程序不能访问,不同用户可以定义各自的用户变量
用户变量的定义和使用见本文第1节。声明静态变量(只读变量)的语法如下:
# 设置变量为只读
var_name=value
readonly var_name
用户变量其它相关命令如下:
# 显示所有用户变量
set
# 清除用户变量(静态变量不能unset,只读变量不可更改)
unset var_name
4.3、位置变量
位置变量也称位置参数,使用$0
、$1
、$2
、…
、$9
表示,位置变量属于只读变量,主要用于按照从左到右的顺序依次向Shell脚本传递参数,命令或脚本本身的名称使用$0
表示,参数的个数可以任意多,$1~$9
表示第1个到第9个参数,10个及以上的参数需要使用{}
才能被访问到,例如第10个参数表示为${10}
以下是一个示例:
1) 编写脚本并保存:
vim add.sh
#!/bin/bash
echo "执行的文件名:$0";
echo "执行的脚本名: `basename $0`";
SUM=`expr $1 + $2`
echo "$1 + $2 = $SUM"
2) 添加执行权限:
chmod +x add.sh
3) 执行脚本:
./add.sh 2 3
脚本输出的结果如下:
执行的文件名:./add.sh
执行的脚本名: add.sh
2 + 3 = 5
4.4、特殊变量
特殊变量(预定义变量)是由Bash程序预先定义好的一类变量,特殊变量属于只读变量,反映Shell脚本运行过程中的控制信息,预定义变量使用$
符号和另一个符号组合表示
$#
:传递给脚本命令行中位置参数的数量$*
:列出命令行中的位置参数列表,所有参数看作一个整体字符串$@
:列出命令行中的位置参数列表,每个参数看作独立的字符串$?
:返回最后一行命令的执行退出状态,0表示执行成功,非0值表示执行出现异常$0
:返回当前执行的脚本或程序的路径名称$$
:返回当前Shell进程的进程号(PID)$!
:返回后台运行的最后一个进程号(PID)$-
:返回当前Shell的选项标志(不常用)$IFS
:返回Shell脚本中的字段分隔符(默认为空格+制表符+换行符),可重新指定
$*
与$@
的区别如下:
- 相同点:都是引用的所有位置参数
- 不同点:只在双引号中,假设脚本传递了三个参数(1 2 3),
*
等价于"1 2 3"
(1个字符串),@
等价于"1"
"2"
"3"
(3个字符串)
#!/bin/bash
echo "-- \$* 演示 ---"
for i in "$*"; do
echo $i
done
echo "-- \$@ 演示 ---"
for i in "$@"; do
echo $i
done
执行脚本,输出结果如下:
-- $* 演示 ---
1 2 3
-- $@ 演示 ---
1
2
3
5、变量中的特殊符号
5.1、字符串变量
5.1.1、字符串中的特殊符号
Shell字符串可以使用单引号,也可以使用双引号,也可以不使用引号
greeting=Hello
greeting='Hello'
greeting="Hello"
1)单引号
仅作为字符串使用。包含的变量将被当做字符串解释
echo '$greeting world' # $greeting world
echo 'Today is `date`' # Today is `date`
单引号字符串中不能出现单独一个的单引号,不能出现转义字符
2)双引号
双引号中可以引用变量,包含的变量将被解释
echo "$greeting world" # hello world
echo "Today is `date`" # Today is 2024年 08月 18日 星期日 18:30:05 CST
3)反引号
反引号和$()
作用相同,主要用于执行命令并将其输出插入到命令行中
echo "`ls -l`" # 输出执行命令ls -l的结果
# 使用转义字符echo输出需要添加-e转义
echo -e "Today is \"`date`\"" # Today is "2024年 08月 18日 星期日 18:30:05 CST"
echo "`hostname`" # 获取当前主机名
echo "`basename $0`" # 获取执行的脚本名
5.1.2、字符串变量的操作
字符串变量的基本操作如下:
1)字符串拼接
greeting="Hello"
name="Tom"
# 直接拼接
echo $greeting$name # HelloTom
# 使用双引号拼接
greeting_1="$greeting, $name !"
echo $greeting_1 # Hello, Tom !
# 使用单引号拼接
greeting_2=''$greeting', '$name' !'
echo $greeting_2 # Hello, Tom !
2)获取字符串长度
# 语法:${#string}
echo ${#greeting} # 5
# 当变量为字符串时,${#string}等价于${#string[0]}
echo ${#greeting[0]} # 5
3)提取子字符串
字符串第一个字符的索引值为0
# ${str:sta:len}:从字符串第2个字符开始截取3个字符
echo ${greeting:1:3} # ell
# ${str:sta}:从字符串第2个字符开始截取(后面全部)
echo ${greeting:1} # ello
4)字符串替换
# ${str/sub/new}:用new替换str中的第一个sub
echo ${greeting/l/t} # Hetlo
# ${str//sub/new}:用new替换str中的全部sub
echo ${greeting//l/t} # Hetto
5)查找子字符串的位置
# 查找字符e的位置(从1开始)
echo `expr index "$greeting" e` # 2
# 查找子字符串lo的位置(从1开始)
echo `expr index "$greeting" lo` # 4
5.1.3、正则表达式
正则表达式是用来匹配文本的强大工具。以下是一些基本的正则表达式符号:
.
:匹配任意一个字符*
:匹配前面的字符0次或多次+
:匹配前面的字符1次或多次?
:匹配前面的字符0次或1次^
:匹配行首$
:匹配行尾[abc]
:匹配a、b、c
中的任一个字符[0-9]
:匹配任意一个数字字符\
:转义特殊字符,使其被当作普通字符匹配
例如,正则表达式a.*b
可以匹配以a
开头,以b
结束的任意字符串
更多关于正则表达式的介绍和使用详见文章:传送门
5.2、数组变量
5.2.1、数组中的特殊符号
数组中的特殊符号基本使用如下:
# 1)打印数组
# 方式1
echo ${my_array[@]} # A B C D
# 方式2
echo ${my_array[*]} # A B C D
# 2)获取数组元素的数量(获取数组长度/大小)
# 方式1
echo ${#my_array[@]} # 4
# 方式2
echo ${#my_array[*]} # 4
# 3)访问数组元素(下标从0开始)
echo ${my_array[2]} # B
# 4)取得数组中某个元素的长度
echo ${#my_array[2]} # 1
5.2.2、数组变量的操作
数组变量的常见操作包括但不限于:
1)数组遍历
# 方式1
for i in ${my_array[@]}; do
echo $i
done
# 方式2
for ((i = 0; i < ${#my_array[@]}; i++)); do
echo ${my_array[i]}
done
# 方式3
i=0
while [ $i -lt ${#my_array[@]} ]; do
echo ${my_array[$i]}"
done
2)数组截取(切片)
语法格式:${arr[@]:sta:len}
(sta
为闭区间,不改变原数组)
# 从起始索引1开始,截取2个元素
echo ${my_array[@]:1:2} # B C
# 截取起始位置及后面的全部元素:${arr[@]:sta}
echo ${my_array[@]:2} # C D
# 截取前n(2)个元素:${arr[@]:0:n}
echo ${my_array[@]:0:2} # A B
# 截取后n(2)个元素:${arr[@]:$((${#arr[@]}-n)):n}
echo ${my_array[@]:$((${#my_array[@]}-2)):2} # C D
3)数组删除
# 删除数组元素(删除第二个元素)
unset my_array[1]
echo ${my_array[@]} # A C D
# 删除数组
unset my_array
echo ${my_array[@]} #
4)数组替换
语法格式:${arr[@]/old/new}
(不改变原数组)
# 将D替换为E
echo ${my_array[@]/D/E} # A B C E
# 改变原数组(重新赋值):arr[index]=value
my_array[1]=E
echo ${my_array[@]} # E B C D
5)数组扩容
向数组追加元素的常见方法如下:
# 方式1:使用索引:将数组长度作为新元素索引进行追加
arr[${#arr[@]}]=value
# 方式2:使用+=()操作符:必须使用(),多个元素使用空格隔开,在数组末尾追加元素或数组
arr+=(value1 value2 ...)
# 方式3:使用数组展开:使用数组展开追加元素,多个元素使用空格隔开
# 使用注意:必须使用双引号和@,否则包含空格的元素将被拆分为多个元素
arr=("${arr[@]}" value1 value2 ...)