前言

与 Java、Go 等编程语言类似,Bash 中也可以自定义变量,那么如何自定义变量呢?除了自定义变量之外,Bash中还有没有其他类型的变量供我们使用呢?一起来学习吧!

变量分类

Bash 中,变量主要分为以下四种类型:

  • 自定义变量:类似Java、Go语言中的自定义变量,灵活性最高;
  • 环境变量:主要保存和系统环境相关的变量,系统已经定义好了很多环境变量,同时允许用户新增自定义环境变量,灵活性较高;
  • 位置参数变量:这种变量主要是用来向脚本中传递参数或者数据用的,参数名不能自定义,变量的作用也是固定的,只能更改值;
  • 预定义变量:Bash中已经定义好的变量,变量名不能自定义,变量作用也是固定的。

由于内容较多,我们分为两篇文章来学习,本篇文章主要学习前两种变量:自定义变量和环境变量。

自定义变量

Java 中,你可以使用类似 int i = 1 的形式定义一个变量,同样在 Bash 中,你也可以定义变量,需要遵循以下几点规则:

  • 变量名可以由字母、数字、下划线组成,但是不能以数字开头
  • 变量与值之间通过 = 连接,中间不能有空格
  • 变量的值如果有空格,需要用单引号或者双引号包括
  • 在变量的值中,可以使用 “\” 符号进行转义
  • 如果需要追加变量的值,需要使用双引号包含"$变量名" 或者 ${变量名包含}
  • Bash定义的变量默认都是字符串类型的,如果要进行数值运算,必须指定变量类型为数值类型

用户变量也可以称为本地变量,因为自定义变量只在当前 Shell 中生效,其他 Shell 不能使用。

示例

 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
33
34
35
36
37
38
39
# 变量与值通过 '=' 连接
[root@VM-0-5-centos ~]# name=lifelmy

# 变量与值中间不能有空格
[root@VM-0-5-centos ~]# name = lifelmy
-bash: name: 未找到命令

# 变量名不能使用数字开头,
[root@VM-0-5-centos ~]# 2name=lifelmy     
-bash: 2name=lifelmy: 未找到命令

# 值如果有空格,需要使用单引号或者双引号包括
[root@VM-0-5-centos ~]# title=this is a title
-bash: is: 未找到命令
[root@VM-0-5-centos ~]# title="this is a title"
[root@VM-0-5-centos ~]# echo $title
this is a title

# 变量的值可以使用转义符进行转义
[root@VM-0-5-centos ~]# name=\lifelmy
[root@VM-0-5-centos ~]# echo $name
lifelmy
[root@VM-0-5-centos ~]# name=\\lifelmy
[root@VM-0-5-centos ~]# echo $name
\lifelmy

# 变量值的追加
[root@VM-0-5-centos ~]# name=lifelmy
[root@VM-0-5-centos ~]# name1=${name}123
[root@VM-0-5-centos ~]# name2="$name"456
[root@VM-0-5-centos ~]# echo $name1
lifelmy123
[root@VM-0-5-centos ~]# echo $name2
lifelmy456

# 命令的结果赋值给变量
[root@VM-0-5-centos ~]# now=$(date)
[root@VM-0-5-centos ~]# echo $now
2021年 11月 14日 星期日 13:48:43 CST

变量调用

使用 $变量名 可以调用变量

1
2
3
[root@VM-0-5-centos ~]# name=lifelmy
[root@VM-0-5-centos ~]# echo $name
lifelmy

变量查看

使用 set 命令可以查看所有变量(包括环境变量)

1
2
3
4
5
[root@VM-0-5-centos ~]# set | grep name
_name=lifelmy
name=lifelmy
name1=lifelmy123
name2=lifelmy456

变量删除

使用 unset 可以删除变量

1
2
3
4
5
6
7
[root@VM-0-5-centos ~]# name=lifelmy
[root@VM-0-5-centos ~]# echo $name
lifelmy
[root@VM-0-5-centos ~]# unset name
[root@VM-0-5-centos ~]# echo $name


数值运算

自定义变量的类型默认是字符串类型,无法进行数值计算:

1
2
3
4
5
[root@VM-0-5-centos ~]# aa=1
[root@VM-0-5-centos ~]# bb=2
[root@VM-0-5-centos ~]# cc=$aa+$bb
[root@VM-0-5-centos ~]# echo $cc
1+2

那么如何进行数值运算呢?在介绍之前,我们先来学习一个命令,该命令用于查看、设置变量的类型:

1
2
3
4
5
6
7
8
declare [+/-] [选项] 变量名

选项:
-: 给变量设定属性类型
+: 取消变量的类型属性
-i: 将变量声明为整数型(integer)
-x: 将变量声明为环境变量
-p: 显示指定变量被声明的类型

declare示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
[root@VM-0-5-centos ~]# name=lifelmy

# 默认是字符串类型
[root@VM-0-5-centos ~]# declare -p name
declare -- name="lifelmy"

# 声明为环境变量后,再次查看变成了 -x
[root@VM-0-5-centos ~]# export name
[root@VM-0-5-centos ~]# declare -p name
declare -x name="lifelmy"

# 声明为整数类型
[root@VM-0-5-centos ~]# declare -i age
[root@VM-0-5-centos ~]# age=18
[root@VM-0-5-centos ~]# declare -p age
declare -i age="18"

接下来我们介绍三种数值计算的方法:

  1. 使用 declare 声明
1
2
3
4
5
6
7
8
[root@VM-0-5-centos ~]# a=11
[root@VM-0-5-centos ~]# b=12

# 声明c为整数
[root@VM-0-5-centos ~]# declare -i c
[root@VM-0-5-centos ~]# c=$a+$b
[root@VM-0-5-centos ~]# echo $c
23
  1. 使用 exprlet 运算工具
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
[root@VM-0-5-centos ~]# a=1
[root@VM-0-5-centos ~]# b=2

# 注意:'+'号左右两侧必须有空格
[root@VM-0-5-centos ~]# c=$(expr $a + $b)
[root@VM-0-5-centos ~]# echo $c

# 不需要有空格
[root@VM-0-5-centos ~]# let d=$a*$b
[root@VM-0-5-centos ~]# echo $d
2
  1. 使用运算式

使用 $((运算式))$[运算式]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# $((运算式))  或 $[运算式]

[root@9c12e0d24857 /]# a=2
[root@9c12e0d24857 /]# b=3
[root@9c12e0d24857 /]# c=$(($a+$b))
[root@9c12e0d24857 /]# echo $c
5
[root@9c12e0d24857 /]# d=$[$a+$b]
[root@9c12e0d24857 /]# echo $d
5

在实际使用中,最常用的是第三种方式。

环境变量

前面讨论的自定义变量,只会在 当前Shell 中生效,而环境变量会在 当前Shell 以及这个 Shell 的所有 子Shell 中生效。如果把环境变量写入相应的配置文件,那么这个环境变量就会在所有的 Shell 中生效。

什么叫做 子Shell 呢? 我们在 Shell 编程初体验 查看支持的Shell 章节讨论过,可以通过执行命令切换到其他 Shell 中,那么当前 Shell 就是 父Shell,切换后的 Shell 就是 子Shell

设置环境变量

1
export 变量名=变量值

如果变量已经存在,可直接使用 export 声明

1
export 变量名

查询环境变量

1
env

删除环境变量

1
unset 变量名

使用环境变量

和自定义变量类似,使用 $变量名 取值

1
echo $变量名

示例一

创建并通过进程树查看 子Shell

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 1. 在当前Bash中执行该命令,能够找到如下输出的一条链路
[root@VM-0-5-centos ~]# pstree
systemd──sshd──sshd───bash───pstree

# 2. 然后在当前Bash中开启一个新的Bash,即一个子Shell
#    再查看进程树相应链路,发现多了个bash
[root@VM-0-5-centos ~]# bash
[root@VM-0-5-centos ~]# pstree
systemd─sshd─┬─sshd───bash───bash───pstree


# 3. 退出子Shell,查看进程树,发现子Shell没有了
[root@VM-0-5-centos ~]# exit
exit
[root@VM-0-5-centos ~]# pstree
systemd──sshd──sshd───bash───pstree

示例二

设置环境变量

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 1. 设置了一个自定义变量,并通过两种方式设置了两个环境变量age 和 gender
[root@VM-0-5-centos ~]# name=lifelmy
[root@VM-0-5-centos ~]# age=18
[root@VM-0-5-centos ~]# export gender=male
[root@VM-0-5-centos ~]# export age

# 2. 通过 set 命令查看所有变量,都可以查到
[root@VM-0-5-centos ~]# set | grep name=
name=lifelmy
[root@VM-0-5-centos ~]# set | grep age=
age=18
[root@VM-0-5-centos ~]# set | grep gender=
gender=male

# 3. 进入一个 子Shell
[root@VM-0-5-centos ~]# bash

# 4. 发现只有两个环境变量,可以在子Shell中可以查询到
[root@VM-0-5-centos ~]# set | grep name=
[root@VM-0-5-centos ~]# set | grep age=
age=18
[root@VM-0-5-centos ~]# set | grep gender=
gender=male

示例三

查看、使用、删除环境变量

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 使用 env 命令查看环境变量
[root@VM-0-5-centos ~]# env
gender=male
age=18
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
......

# 使用环境变量
[root@VM-0-5-centos ~]# echo $age
18
[root@VM-0-5-centos ~]# echo $gender
male

# 删除环境变量
[root@VM-0-5-centos ~]# unset age

常见系统环境变量-PATH

PATH下面的内容,是系统用于查找命令的路径(使用":“分隔)

1
2
[root@VM-0-5-centos ~]# env | grep PATH
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin

我们可以在PATH后,按照规定的格式,追加自定义的文件夹

1
PATH="$PATH":/root/hello

Shell 编程初体验 编写第一个Shell脚本 章节介绍过,想要执行脚本,必须使用相对路径或者绝对路径。对于 Linux 中的这些系统命令,例如 ls、cd、mkdir,和我们自己写的脚本文件一样,也是可执行程序,为什么输入这些命令时,不需要使用相对或绝对路径呢?那是因为当我们输入了一个命令后,Linux 会在 PATH 下所有文件夹中寻找这个可执行程序,如果找到了就可以执行,如果找不到就会报错 “未找到命令”,这里的未找到,就是在 PATH 对应的路径中未找到。

比如我们写一个脚本,输出 “hello world”,默认只能使用相对或绝对路径执行。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
[root@VM-0-5-centos ~]# vim hello.sh

#!/bin/bash
echo "hello world"

[root@VM-0-5-centos ~]# chmod 755 hello.sh
# 相对路径
[root@VM-0-5-centos ~]# ./hello.sh
hello world
# 绝对路径
[root@VM-0-5-centos ~]# /root/hello.sh
hello world

但是如果我们把该脚本添加到 PATH 下面的一个文件夹中,就可以直接使用了!同时按 “TAB” 键也会进行补全! 可见 “TAB” 键也是在 PATH 下面的路径中查找的!

1
2
3
[root@VM-0-5-centos ~]# cp hello.sh /sbin/
[root@VM-0-5-centos ~]# hello.sh
hello world

但是我们并不推荐使用这种方式,因为 “/sbin” 目录就是用来保存系统命令的,把我们自定义的命令脚本放进去,会将文件搞混。而更优雅的方式,则是将我们脚本所在的文件夹,追加到 PATH 中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 1. 删除上一步复制的hello.sh
[root@VM-0-5-centos ~]# rm /sbin/hello.sh
rm:是否删除普通文件 "/sbin/hello.sh"?y

# 将当前脚本文件所在目录,添加到 PATH 中
[root@VM-0-5-centos ~]# PATH="$PATH":/root
[root@VM-0-5-centos ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root
[root@VM-0-5-centos ~]# hello.sh
hello world

以上的改动只会在当前Shell以及子Shell中生效,因为我们暂未将其写入环境变量配置文件中去。

常见系统环境变量-PS1

定义系统提示符的变量,就是我们输入命令前的提示符,当前我的就是 [root@VM-0-5-centos ~]

PS1 也是属于环境变量的一种,但是使用 env 命令查询不到,使用 set 可以查找到

1
2
[root@VM-0-5-centos ~]# set | grep PS1
PS1='[\u@\h \W]\$ '
代码 说明
\d 显示日期,格式为 “星期 月 日”
\h 显示简写主机名,如默认主机名为"localhost”
\t 显示24小时制事件,格式为"HH:MM:SS"
\T 显示12小时制事件,格式为"HH:MM:SS"
\A 显示24小时制事件,格式为"HH:MM"
\u 显示当前用户名
\w 显示当前所在目录的完整名称
\W 显示当前所在目录的最后一个目录
\# 执行的第几个命令
\$ 提示符,如果是root用户显示"#",普通用户提示符为"$"

我们可以改动下PS1的值来看下效果:

1
2
3
[root@VM-0-5-centos ~]# PS1='[\u@ \t \W]\$ '
[root@ 15:47:59 ~]# ls
hello.sh

这种改动也是只是临时生效,需要永久生效需要写入配置文件。当然这里只是体验下,系统默认的已经够用了,其实也无需更改。

总结

本篇文章介绍了Bash 中的变量分类:自定义变量、环境变量、位置参数变量和预定义变量,并详细介绍了前两种:

  1. 自定义变量的定义、查看、调用以及如何进行数值计算;
  2. 环境变量的设置、查看、使用,并介绍了两种常见的环境变量:PATH 和 PS1。

更多

微信公众号:CodePlayer