定义变量的规则

在定义变量时,有一些规则需要遵守:

  • 变量名称可以由字母、数字和下划线组成,但不能以数字开头;

  • 在 Bash 中,变量的默认类型都是字符串,如果要进行数值运算,则必须指定变量类型为数值型;

  • 变量用等号连接值,等号左右两侧不能有空格。例如:

    1
    2
    [root@localhost ~]# name = cangls
    -bash: name: command not found
  • 变量的值如果有空格,需要使用单引号或者双引号括起来。 如:test="hello world"。其中双引号括起来的内容$\和反引号都拥有特殊含义,而但因好括起来的内容都是普通字符。

  • 在变量的值中,可以使用\转义符。

  • 如果需要增加变量的值,那么可以进行变量值的叠加。不过变量需要双引号包括 "$变量名" 或者使用 "${变量名}" 来包含变量名。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #叠加变量 test,变量值变成了123456
    [root@localhost ~ ] # test=123
    [root@localhost ~ ] # test="$test"456
    [root@localhost ~ ] # echo $test
    123456
    #再叠加变量 test,变量值变成了123456789
    [root@localhost ~ ] # test=${test}789
    [root@localhost ~ ] # echo $test
    123456789
  • 如果是把命令的结果作为变量值赋予变量,则需要使用反引号或者$( )包含命令。例如:

    1
    2
    3
    [root@localhost ~ ] # test=$(date)
    [root@localhost ~ ] # echo $test
    2022年 10月 28日 星期五 16:29:16 CST

变量的分类

在Shell中变量的分类:

类型 描述
用户自定义变量 这种变量是最常见的变量,由用户自由定义变量名和变量值。(只在当前Shell生效,子Shell不生效)
环境变量 这种变量中主要保存的是和系统操作环境相关的数据,比如当前登录用户,用户的家目录,命令的提示符等。环境变量的变量名可以自由定义,但是一般对系统起作用的环境变量的变量名是系统预先设定好的。
预定义变量 在Bash中已经定义好的变量,变量名不能自定义,变量作用也是固定的
位置参数变量 主要用来向脚本传递参数或数据的,变量名不能自定义,作用是固定的
1
2
3
4
5
说明:环境变量分为两种,一种是允许用户自定义的,还有一种是系统定义好的。

用户定义的这种环境变量,对整个操作系统的环境来说作用不是很大,唯一的作用就是,在父子Shell中都生效。

而系统定义的环境变量,会对我们的操作环境产生一定影响。对系统定义的环境变量,我们不能修改变量的名称和作用,只能修改变量的属性值。系统定义的环境变量主要就是在几个重要的环境变量配置文件中的变量,如/etc/profile文件中的变量。

用户自定义变量

这种变量是最常见的变量,由用户自由定义变量名和变量值。

可以在Shell 脚本里面定义,也可以在终端里面直接定义

1
2
3
4
[root@localhost ~ ] # test=123
[root@localhost ~ ] # test="$test"456
[root@localhost ~ ] # echo $test
123456

环境变量

在Shell中,环境变量分为两种。一种时用户自定义的环境变量,另一种是系统自带的环境变量。

查看环境变量时使用 env 命令去查询。

用户自定义的环境变量
使用 export 关键字声明的变量就是环境变量。

1
[root@localhost ~]# export AGE="18"

也可以先定义一个自定义变量,然后把自定义变量声明为环境变量:

1
2
[root@localhost ~]# AGE=18
[root@localhost ~]# export AGE

系统自带的环境变量

系统自带的环境变量名称和作用都不能改变,这里只介绍一个 PATH 环境变量。

PATH环境变量的作用是系统查找命令的路径。

在执行脚本时可以通过绝对路径去执行,也可以通过相对路径执行。这两种都可以理解,因为都能精准的通过路径找到该脚本,或者当前目录就有该脚本。

有一些系统命令,比如 lsls命令在系统的 /bin 目录下。但我们直接执行 ls 命令,并没有使用绝对路径,系统就能执行 ls 命令,这是为什么呢?

根本原因就是在这个 PATH 环境变量中。

PATH环境变量中配置的这些路径,就是系统查找命令的路径。也就是说当我们输入了一个程序名,如果没有写命令的路径,系统就会到PATH环境变量定义的路径中去寻找,是否有可以执行的程序。从左到右一个一个目录查找你所要执行的命令,找到了就直接执行,而如果把所有的目录都搜索完了,也没有找到你所执行的命令,就会报错,提示:

1
bash: tttt:未找到命令

那想要不输入绝对路径就能执行一个程序或者脚本,就只要对PATH环境变量进行叠加就可以实现了

1
2
3
4
5
#在变量PATH的后面,加入/root/sh目录
[root@localhost ~]# PATH="$PATH":/root/sh
#查询PATH的值,变量叠加生效了
[root@localhost ~]# echo $PATH
/usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/root/sh

但需要注意的是,这种方法只是临时生活笑,一旦重启或者注销就会消失。如果想要永久生效,就需要写入环境变量配置文件 /etc/profile 中。比如配置JAVA环境

1
2
3
4
5
6
7
8
## 假设JAVA环境已经解压好了

## 编辑 /etc/profile 文件,在文件末尾添加以下内容
[root@localhost ~]# vim /etc/profile

## 在文件末尾添加以下内容
export JAVA=/usr/lib/java/bin
export PATH=$PATH:$JAVA

想要环境变量生效需要重启或者注销,但也可以让当前Shell生效,使用以下命令:

1
[root@localhost ~]# source /etc/profile

预定义变量

预定义变量就是,事先把变量的名称和作用确定好(都是不可变的),只能修改变量的内容,也就是变量的值。

其实预定义变量和位置参数变量都可以统称为预定义变量。

预定义变量 作用
$? $?存储的值是最后一次执行的命令的返回状态。
$$ 当前进程的进程号(PID)
$! 后台允许的最后一个进程的进程号(PID)
1
在 `$?` 的返回状态中,变量值为0则代表上一个命令正确执行;变量值非0则代表上一个命令执行不正确。

位置参数变量

位置参数变量的作用主要用于脚本的传参。

位置变量参数 作用
$n n为数字,$0代表命令本身,$1-$9代表第一个到第九个参数,十个以上的参数需要用大括号括起来:${10}
$* 这个变量代表命令行中所有的参数,$*把所有的参数看成一个整体
$@ 这个变量也代表命令行中所有的参数,不过 $@ 把每个参数区分对待
$# 这个变量代表命令行中所有参数的个数

$n

n 为数字,$n 代表命令本身

1
[root@localhost ~]# bash add.sh 1 2

在以上示例中,$0 代表命令本身,$1 为1,$2 为2,以此类推。

$#
$# 这个变量代表命令行中所有参数的个数

1
2
3
4
5
6
7
8
9
10
11
12
13
# 定义一个脚本parameter.sh

[root@cjh tmp]# vim parameter.sh

# 脚本内容如下:
#!/bin/bash
# 使用$#代表所有参数的个数
echo "The parameters \$# is:$#"

# 执行该脚本
[root@cjh tmp]# chmod 755 parameter.sh
[root@cjh tmp]# ./parameter.sh 11 22 33 44
The parameters $# is:4

变量的作用域

Shell 脚本中所产生的变量默认情况下只针对当前终端或者当前 Shell 生效的,那如果在当前 Shell 中生成一个新的子 Shell、或者是该 Shell 的父进程,都无法引用当前 Shell 下创建的变量。

1
那如何将当前 Shell 创建的变量能在父 Shell 或者子 Shell 中运行呢?

可以更改执行脚本的方式,也可以将该变量导出

改变执行脚本的方式

Shell 脚本的执行脚本方式有4中,分别是:

  • bash demo.sh
  • ./demo.sh
  • source demo.sh
  • . demo.sh
1
这 4 种方式分别有什么区别呢?

bash demo.sh./demo.sh 这两种方式很类似,这两种方式会产生一个叫做 bash 的子进程,在这个子进程下面再去运行该脚本。唯一的区别呢就是 ./demo.sh 的这种方式会执行 Sha-Bang,而 bash 不用,因为这种方式就显示的指定 bash 去执行该脚本。

而后两种方式 source demo.sh. demo.sh 就和前 2 种产生子进程然后执行脚本的方式不一样,这两种是在本进程下面去执行脚本文件的。

示例:

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
## 创建一个脚本文件,文件含义就是进入 /etc 目录下,并且打印出当前目录的路径

root@ls529hJ9gm:~# vim demo.sh

#!/bin/bash

cd /etc
pwd
## 文件结束

root@ls529hJ9gm:~#

## 下面分别以以上这 4 种方式去执行该脚本,分别看看他们的输出

## 第一种方式执行,执行后打印出了 /etc 目录当时当前进程的位置没有发生改变
root@ls529hJ9gm:~# bash ./demo.sh
/etc

## 第二种方式执行,发现没有权限,最后赋予权限后和第一种结果一样,打印出目录但是当前进程的位置没有发生改变
root@ls529hJ9gm:~# ./demo.sh
-bash: ./demo.sh: Permission denied
root@ls529hJ9gm:~# chmod u+x demo.sh
root@ls529hJ9gm:~# ./demo.sh
/etc
root@ls529hJ9gm:~#
root@ls529hJ9gm:~#

## 第三种方式,在脚本执行完之后,当前进程的位置由 ~ 目录变成了 /etc 目录
root@ls529hJ9gm:~# source ./demo.sh
/etc
root@ls529hJ9gm:/etc#
root@ls529hJ9gm:/etc# cd ~
root@ls529hJ9gm:~#

## 第四种方式与第三种方式结果一样,位置发生了改变
root@ls529hJ9gm:~# . demo.sh
/etc
root@ls529hJ9gm:/etc#
root@ls529hJ9gm:/etc#

所以要想在子 Shell 去引用父 Shell 的变量,那就要使用 source. 的方式去执行脚本

导出变量

另外一种方式就是将该变量设置为导出

1
2
3
4
export 变量名=值

// 另外一种导出方式
export 变量名

将该变量导出之后,就可以在子 Shell 中使用父 Shell 中导出的变量。

接收键盘输入的数据

位置参数变量是用来向脚本中传递值的,但在执行脚本的时候,脚本无法给出所需参数的提示,这是对用户非常的不友好。

这种情况下可以使用 read 命令去解决,read 命令是用来接受键盘输入的命令,并提示相关输入说明。

命令格式如下:

1
read [选项] [变量名]

选项:

  • -p:“提示信息”:在等待read输入时,输出提示信息。
  • -t:秒数:read命令会一直等待用户输入,使用此选项可以指定等待时间。
  • -n:字符数:read命令只接受指定的字符数,就会执行。
  • -s:隐藏输入的数据,适用于机密信息的输入。

变量名:

  • 变量名可以自定义,如果不指定变量名,会把输入保存入默认变量REPLY。
  • 如果只提供了一个变量名,则整个输入行赋予该变量。
  • 如果提供了一个以上的变量名,则输入行分为若干字,一个接一个地赋予各个变量,而命令行上的最后一个变量取得剩余的所有字。

示例:

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
# 定义一个shell脚本read.sh。
[root@cjh tmp]# vim read.sh

# 脚本内容如下:

#!/bin/bash

# 例1
# 提示“请输入姓名”并等待30秒,把用户的输入保存入变量name中
read -t 30 -p "Please input your name:" name

# 查看变量“$name”中是否保存了你输入的name
echo "Name is $name"

# 例2
# 提示“请输入年龄”并等待30秒,把用户的输入保存入变量age中。
# 年龄是隐私,所以我们用“-s”选项隐藏输入。
read -s -t 30 -p "Please enter your age:" age

# 调整输出格式,换行。
echo -e "\n"
# 查看上边输入的年龄。
echo "Age is $age"

# 例3
# 提示“请选择性别”并等待30秒,把用户的输入保存入变量gender。
# 使用“-n 1”选项,表示只接收一个输入字符就会执行(都不用输入回车,自动就结束输入了。)
# 如下的提示:gender[M/F]
read -n 1 -t 30 -p "Please select your gender[M/F]:" gender

# 调整输出格式,换行。
echo -e "\n"
# 查看上边输入的性别。
echo "Sex is $gender"

最后赋予执行权限并运行:

1
2
chmod +x read.sh
./read.sh