Shell变量

在Shell中包括有三种变量类型:系统环境变量、自定义变量和特殊符号变量。

系统环境变量

系统环境变量是系统提供的共享变量,是linux系统加载Shell的配置文件中定义的变量共享给所有的Shell程序使用。环境变量按照其作用范围不同大致可以分为系统级环境变量用户级环境变量

  • 系统级环境变量:Shell环境加载全局配置文件中的变量共享给所有用户所有Shell程序使用,全局共享
  • 用户级环境变量:Shell环境加载个人配置文件中的变量共享给当前用户的Shell程序使用,登录用户使用

Shell的配置文件分类:

  • 全局配置文件

    /etc/profile
    /etc/profile.d/*.sh
    /etc/bashrc

  • 个人配置文件

    当前用户/.bash_profile
    当前用户/.bashrc

环境变量加载初始化过程:

  • 登录Shell环境:需要用户名\密码登录的Shell环境,配置在:/etc/profile/当前用户/.bash_profile
  • 非登录Shell环境:不需要用户名,密码进入的Shell环境或执行脚本文件,配置在:/当前用户/.bashrc/etc/bashrc

加载顺序:

查看当前Shell环境:

1
echo $0

输出-bash表示shell登录环境,输出bash表示shell非登录环境。注意:这个 $0 环境变量如果用在子shell中(shell脚本文件)输出Shell脚本本身的文件名。

切换shell环境:

直接切换用户

1
2
3
4
5
#切换到指定用户,加载Shell登录环境变量
su 用户名 --login #或 su 用户名 -l

# 切换到指定用户,加载Shell非登录环境变量
su 用户名

切换Shell环境执行脚本文件

1
2
3
4
5
6
#先加载Shell登录环境流程初始化环境变量, 再执行脚本文件
sh/bash -l/--login 脚本文件

#先执行加载Shell非登录环境流程初始化环境变量, 再执行脚本文件
bash # 加载Shell非登录环境
sh/bash 脚本文件

退出shell当前环境:

  • logout:只能退出Shell登录环境。
  • exit:可以退出Shell登录与非登录环境

查看系统环境变量:

  • env:查看当前Shell系统环境变量。
  • set:查看Shell变量(系统环境变量+自定义变量+函数)。

常用系统环境变量介绍:

变量名称 含义
PATH 与windows环境变量PATH功能一样,设置命令的搜索路径,以冒号为分割
HOME 当前用户主目录:/root
SHELL 当前shell解析器类型:/bin/bash
HISTFILE 显示当前用户执行命令的历史列表文件:/root/.bash_history
PWD 显示当前所在路径:/root
OLDPWD 显示之前的路径
HOSTNAME 显示当前主机名:itheima
HOSTTYPE 显示主机的架构,是i386、i686、还是x86、x64等:x86_64
LANG 设置当前系统语言环境:zh_CN.UTF-8

自定义变量

自定义变量分为三种类型:

  1. 自定义局部变量:就是定义在一个脚本文件中的变量, 只能在这个脚本文件中使用的变量, 就是局部变量
  2. 自定义常量:就是变量设置值以后不可以修改的变量叫常量, 也叫只读变量
  3. 自定义全局变量:就是在当前脚本文件中定义全局变量,这个全局变量可以在当前Shell环境与子Shell环境中都可以使用

变量的定义:

1
var_name=value

变量定义规则:

  1. 变量名称可以有字母,数字和下划线组成, 但是不能以数字开头
  2. 等号两侧不能有空格
  3. 在bash环境中, 变量的默认类型都是字符串类型, 无法直接进行数值运算
  4. 变量的值如果有空格, 必须使用双引号括起来
  5. 不能使用Shell的关键字作为变量名称

变量的取值:

1
2
3
4
5
# 语法1: 直接使用变量名查询
$var_name
# 语法2: 使用花括号
${var_name}
# 区别: 花括号方式适合拼接字符串

注意: 如果echo "My name is ${var2}Style"$var2 不带花括号,系统会认为获取$var2Style 变量数据,这个变量不存在就获取不到数据,所以尽量使用花括号获取变量值。

变量删除:

1
unset var_name

设置自定义常量:

1
readonly var_name

在定义好局部变量之后,执行readonly var_name即可把变量设置为只读,之后不可再对该变量进行赋值操作。

设置自定义全局变量:

1
export var_name1 var_name2

全局变量可以在当前shell环境的子环境中也可以使用,例如,我们创建2个脚本文件:parent.shchild.sh

1
2
3
4
5
6
#!/bin/bash
#parent.sh
var_global=test
export var_global
echo "parent.sh中输出var_global变量:${var_global}"
bash child.sh
1
2
3
#!/bin/bash
#child.sh
echo "child.sh中输出var_global变量:${var_global}"

然而直接在交互式Shell环境中打印${var_global}是得不到结果的,说明全局变量在当前Shell环境与子Shell环境中可用,父Shell环境中不可用。

特殊符号变量

特殊变量 含义
$n 获取输入参数的
$0, 获取当前Shell脚本文件名字
$1~$9, 获取第一个输入参数到第九个输入参数
${10} 获取10和10以上的参数需要使用花括号
$# 获取所有输入参数的个数
$*$@ 获取所有输入参数数据
区别: 如果不使用双引号, 功能一样, 获取所有参数数据为一个字符串,
如果使用了双引号, $@获取的就是参数列表对象, 每个参数都是一个独立字符串。
$? 获取上一个命令的退出状态码, 一般;来说0代表命令成功,非0代表执行失败。
$$ 获取当前shell环境进程的ID号。

$n用于接收脚本文件执行时传入的参数:

1
sh 脚本文件 输入参数1 输入参数2 ...
  • $0 用于获取当前脚本文件名称的
  • $1~$9, 代表获取第一输入参数到第9个输入参数
  • 第10个以上的输入参数获取参数的格式: ${数字},否则无法获取

$#获取所有输入参数的个数(脚本文件本身并不计算再内),也就是从$1开始计数。

$*$@都是获取所有输入参数, 用于以后输出所有参数。

区别:

  1. 不使用双引号括起来, 功能一样
    $*$@获取所有输入参数,格式为: $1 $2 … $n
  2. 使用双引号括起来
    $*“获取的所有参数拼接为一个字符串, 格式为: “$1 $2 … $n”
    $@“获取一组参数列表对象, 格式为: “$1” “$2” … “$n”
    使用循环打印所有输入参数可以看出区别

演示:

直接输出所有输入参数, 与循环方式输出所有输入参数(使用双引号包含 $*$@ ),创建test.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/bin/bash
# 命令1: 打印当前脚本文件名字
echo "当前脚本文件名称:$0"

# 命令2: 打印第1个输入参数
echo "第一个输入参数:$1"

# 命令3: 打印第2个输入参数
echo "第二个输入参数:$2"

# 命令4: 打印第10个输入参数
echo "第十个输入参数不带花括号获取:$10"
echo "第十个输入参数带花括号获取:${10}"

# 命令5 打印所有输入参数的个数
echo "所有输入参数个数:${#}"

# 增加命令: 实现直接输出所有输入后参数
echo '使用$*直接输出:'$*
echo '使用$@直接输出:'$@

# 增加命令: 使用循环打印输出所有输入参数
echo '循环遍历输出$*所有参数'
for item in "$*"
do
echo $item
done
echo '循环遍历输出$@所有参数'
for item in "$@"
do
echo $item
done

执行效果:

$?用于获取上一个Shell命令的退出状态码, 或者是函数的返回值,每个Shell命令的执行都有一个返回值, 这个返回值用于说明命令执行是否成功,一般来说, 返回0代表命令执行成功, 非0代表执行失败。

$$用于获取当前Shell环境的进程ID号:

自定义系统环境变量

当前用户进入Shell环境初始化的时候会加载全局配置文件/etc/profile里面的环境变量,供给所有Shell程序使用,以后只要是所有Shell程序或命令使用的变量就可以定义在这个文件中。

演示:

  1. 添加设置变量VAR1=VAR1并导出成为环境变量, 在系统级全局配置文件/etc/profile末尾添加如下命令:

    1
    2
    3
    # 创建环境变量
    VAR1=VAR1
    export VAR1
  2. 保存/etc/profile退出,重新加载/etc/profile文件数据更新系统环境变量。(注意:如果这一步不执行,无法读取更新的环境变量)

    1
    source /etc/profile
  3. 在交互式Shell中输出环境变量

    1
    echo $VAR1

Shell字符串变量

字符串是 Shell 编程中最常用的数据类型之一,除了数字和字符串,也没有其他类型了,Shell的变量默认就是字符串。

变量的赋值

字符串变量有3种格式表示,我们先设置一个变量var1=abc,然后比较3种格式的效果:

  1. 单引号:任何字符都会原样输出,在其中使用变量是无效的。

    1
    2
    3
    var2='abc${var1}'
    echo $var2
    #输出:abc${var1}
  2. 双引号(推荐):其中包含了变量,那么该变量会被解析得到值,而不是原样输出。如果字符串中还包含了双引号则需要转义。

    1
    2
    3
    4
    5
    6
    7
    var2="abc${var1}"
    echo $var2
    #输出:abcabc

    var3="hello,\"${var1}\""
    echo $var3
    #输出:hello,"abc"
  3. 不用引号:不被引号包围的字符串中出现变量时也会被解析,这一点和双引号" "包围的字符串一样。但字符串中不能出现空格,否则空格后边的字符串会作为其他变量或者命令解析。

    1
    2
    3
    4
    5
    6
    7
    var2=abc${var1}
    echo $var2
    #输出:abcabc

    var2=abc ${var1}
    echo $var2
    #报错:-bash: abc: command not found

字符串拼接:

  1. 无符号拼接:echo ${var1}${var2}
  2. 双引号拼接:echo "${var1}${var2}"
  3. 混合拼接:echo ${var1}"&"${var2}"或者echo ${var1}'&'${var2}

变量的取值

设置变量var=abc

变量取值:

1
echo ${var}  #输出:abc

获取字符串长度:

1
echo ${#var} #输出:3

截取语法:

格式 说明
${变量名:start:length} 从 string 字符串的左边第 start 个字符开始,向右截取 length 个字符。
${变量名:start} 从 string 字符串的左边第 start 个字符开始截取,直到最后。
${变量名:0-start:length} 从 string 字符串的右边第 start 个字符开始,向右截取 length 个字符。
${变量名:0-start} 从 string 字符串的右边第 start 个字符开始截取,直到最后。
${变量名#*chars} 从 string 字符串第一次出现*chars的位置开始,截取*chars右边的所有字符。
${变量名##*chars} 从 string 字符串最后一次出现*chars的位置开始,截取*chars右边的所有字符。
${变量名%chars*} 从 string 字符串最后一次出现chars*的位置开始,截取chars*左边的所有字符。
${变量名%%chars*} 从 string 字符串第一次出现chars*的位置开始,截取chars*左边的所有字符。

示例,截取path变量的值

操作 结果
${path#/*:} /usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
${path##/*:} /usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
${path%:*bin} /usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
${path%%:*bin} /usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin

替换语法:

格式 说明 示例
${变量名/旧字符串/新字符串} 将旧字符串替换成新字符串,仅替换第一个 ${path/sbin/SBIN}将sbin替换成SBIN,仅替换第一个
${变量名//旧字符串/新字符串} 将旧字符串替换成新字符串,替换所有 ${path//sbin/SBIN}将sbin替换成SBIN,所有的都替换

变量的测试及默认值:

变量设定方式 str没有设定 str为空字符串 str已设定非为空字符串
var=${str-expr} var=expr var=$str=空 var=$str
var=${str:-expr} var=expr var=expr var=$str
var=${str+expr} var=空 var=expr var=expr
var=${str:+expr} var=空 var=空 var=expr
var=${str=expr} str=expr
var=expr
str不变
var=$str=空
str不变
var=$str
var=${str:=expr} str=expr
var=expr
str=expr
var=expr
str不变
var=$str
var=${str?expr} expr输出至stderr var=$str=空 var=$str
var=${str:?expr} expr输出至stderr expr输出至stderr var=$str

-=?都是测试str变量是否设定,如果str没有设定则将表达式的值赋值给var,有设定则赋值str的值。区别就在于-只会更新val的值,而=会同时更新str?则只会将表达式的值输出到标准错误,并不会赋值给var
+与上面的规则相反,它表示只有str有设定值时才会将表达式的值赋值给var,如果没有设定值,则var赋值为空。
:的意义在于,如果不加:默认空字符串也表示str有设定值,而加上:则表示如果str设定了值,但值为空字符串,依然当作str没有设定值处理。

Shell索引数组变量

  • 定义:

    1
    2
    3
    4
    arr=(29 100 13 8 91 44)
    arr=(20 56 "abc")
    arr=([0]=1 [2]=100 [4]=aa) #可直接对索引赋值进行初始化,由于上面只赋值了3个元素, 所以数组的长度是3
    arr[6]=100 #Shell数组元素定义后不是固定的, 定义后还可以赋值
  • 获取:

    1
    2
    3
    4
    ${arr[1]}   #通过下标获取
    ${arr[@]} #获取数组所有元素,也可以使用${arr[*]}
    ${#arr[@]} #获取数组的长度或个数,也可以使用${#arr[*]}
    ${#arr[1]} #获取数组索引1元素的字符长度
  • 拼接:

    1
    2
    array_new=(${array1[@]} ${array2[@]} ...)
    array_new=(${array1[*]} ${array2[*]} ...)

    注:拼接后,新数组的索引为重新排列

  • 删除:

    1
    2
    unset array_name[index]  #删除数组指定元素数据
    unset array_name #删除整个数组