Shell 变量

1. Shell 变量概述

变量是任何程序或脚本的重要组成部分,变量为程序或脚本访问内存中的可修改的一块数据提供了简单的方式。

Linux Shell 中的变量可以被指定为任意的数据类型,比如文本字符串或者数值。你也可以通过修改 Shell 中的变量来改变 Shell 的样式。

接下来就让我们来了解和学习一下 Shell 中的变量吧。

1.1 Shell 变量是什么

Shell 变量是什么呢,顾名思义,就是可以变化的量,如果有编程基础的读者知道任何程序中都有变量,从我们小时候做数学题,定义的 x 变量,最终给定值,用值替换 x 变量,这就是我们最初接触的变量,它和我们在 Shell 中接触的变量本质上是一致的。

变量在 Shell 中是存储计算器内存的单元,其中存放的值可以改变,当我们在编写 Shell 脚本中,如果一些经常用的字符串或数字我们就可以定义一个变量,将具体的值赋给这个变量,然后在具体的函数或脚本中引用这个变量,就相当于拿到了这个值。

1.2 为什么要用变量

我们大概了解了 Shell 变量是什么,那么为什么在 Shell 脚本中需要变量呢?

例如在一个 Shell 脚本中,我们需要指定一个目录,脚本内多处调用了这个目录名称,现在我们有个需求目录名称改变了,我们该怎么办呢,在脚本中每个使用该目录的地方都需要改成新的目录名称,有没有简单的方法只需要改一处地方呢,这时候就用到了我们的变量,在脚本的全局定义一个变量例如 BASE_DIR,然后将目录赋值给这个变量,在具体引用的时候利用这个目录的变量名称,下次需要我们改,只用改最前面这个变量即可。

因此我们使用变量就是为了方便,同时也使得我们的脚本更具灵活性扩展性与后期的可维护性。

2. Shell 变量基本操作

我们知道了 Shell 中变量是什么以及其在 Shell 编写中的重要性,那么让我们先来学习 Shell 变量的基本操作。

2.1 变量的定义

在使用 Shell 变量前,需要先定义变量,定义变量的方式有三种:

2.1.1 直接赋值

顾名思义就是直接将一个值赋值给一个变量名称,这种需要注意值中不能包含空白字符

例如:正确的直接赋值变量:DIR=/tmp,其中 DIR 为变量名,/tmp 为值

错误的直接赋值:STRING=hello Shell,其中 STRINNG 为变量名,hello Shell 为值,其中值包含了空白字符,这种赋值变量就是错误的。

2.1.2 单引号赋值

如果值中为普通字符,那么单引号赋值与双引号赋值没有区别,其可以包含空白字符,但是如果其中包含了变量的使用,那么单引号赋值方式为单引号里面的内容是什么就输出什么,此种赋值方式适用于不希望解析变量的场景,仅显示纯字符串。

例如:

[root@master ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@master ~]# DIR='$PATH'
[root@master ~]# echo $DIR
$PATH
[root@master ~]# DATE='$(date)'
[root@master ~]# echo $DATE
$(date)

PATH 为 linux 系统内置的一个环境变量,DIR 为我们定义的变量,值为单引号引起来的 $PATH,查看其内容也为 $PATH,同理对于执行 date 命令。

2.1.3 双引号赋值

与单引号赋值一样,其也可以包含空白字符,但是与单引号赋值不同的是,双引号赋值可以解析引号内的变量或执行命令,即不是将双引号中的变量名和命令原样输出,而是解析其中变量的内容,然后进行输出。

例如:

[root@master ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@master ~]# DIR="$PATH"
[root@master ~]# echo $DIR
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@master ~]# DATE="$(date)" 
[root@master ~]# echo $DATE    
Sun Mar 8 22:13:57 CST 2020

可以通过与单引号示例对照查看,双引号赋值解析了 PATH 变量的值然后输出,同理对于执行命令 date

2.2 变量的使用

我们知道了变量的定义方式,那么该如何使用变量呢?使用变量非常简单,只需要在定义的变量名前面添加 $ 即可使用定义的变量

例如:

[root@master ~]# DIR=/tmp
[root@master ~]# echo $DIR
/tmp

在使用变量中有一种特殊情况,需要我们手动指定变量的边界是什么,此时就需要使用到 {}

[root@master ~]# STRING="hello Shell"
[root@master ~]# echo "test $STRINGtest"
test 
[root@master ~]# echo "test ${STRING}test"
test hello Shelltest

通过示例我们能够看出,不使用 {} 引起来变量,如果变量名后面继续有字符,Shell 无法判断我们的变量边界在哪里,我们推荐在使用变量的时候都带上 {} 减少出错的概率,并且方便我们人为识别变量的边界。

2.3 变量的更新

我们已经定义了变量,如果对变量进行更新修改呢,更新修改变量重新赋值即可

例如:

[root@master ~]# DIR=/tmp
[root@master ~]# echo $DIR
/tmp
[root@master ~]# DIR=/root
[root@master ~]# echo $DIR
/root

另外在变量中存在只读变量,顾名思义其为定义好后,只能只读,不能对其进行修改,使用 readonly 命令可以将变量定义为只读变量

[root@master ~]# readonly STRING="Shell"
[root@master ~]# echo ${STRING}
Shell
[root@master ~]# STRING="test"
-bash: STRING: readonly variable
[root@master ~]# echo ${STRING}
Shell

可以看到我们使用 readonly 定义变量后,对变量进行修改时候报错,只读变量无法进行修改。

2.4 变量的删除

变量的删除使用 unset,删除后就消除了定义的变量,变量被删除后不能再次使用;unset 命令不能删除只读变量。

例如

[root@master ~]# DIR=/tmp 
[root@master ~]# echo "dir is ${DIR}"            
dir is /tmp
[root@master ~]# unset DIR
[root@master ~]# echo "dir is ${DIR}" 
dir is

3. Shell 变量规范

与其他语言一样,Shell 中变量也需要遵循一定的规范,

3.1 命名规范

  • 变量命名只能使用英文字母,数字和下划线,首字母不能以数字开头
    • 例如
      • 正常变量:BASE_DIR,File_Name,AEG3,_DIR 等都是正常的变量
      • 无效变量:1file,#dir
  • 变量名中间不能使用空格或标点符号
    • 例如:DIR FILEDIR?FILE 就为无效变量
  • 变量命名不能使用 base 里面的关键字(help 来查看关键字)
  • Shell 变量大小写敏感,也就是定义为大写的变量名,引用的时候必须完全一致。
    • 例如:DIRNAMEdirname 就是两个不同的变量

3.2 赋值规范

  • 变量与值直接使用 “=” 连接,等号两边不能存在空白字符;

    • 例如: dirname = "/tmp" 赋值方法就是错误的。
  • 如果值中有空白字符,使用单引号或双引号引起来,双引号对于其中引用的值将会根据其内容转化,单引号内的特殊字符则一律当字符串进行处理;

    • 例如:DIRNAME="$PATH" 的值为系统 PATH 实际的可执行文件路径,DIRNAME='$PATH' 的值为 $PATH 的字符串。
  • 可用 \ 来转译变量,让其变为一般字符;

    • 例如:DIRNAME=\$PATH 的值为 $PATH 的字符串。
  • 如果变量的值为指令,可用使用反撇号,或 $() 来引用;

    • 例如:下面两个是一样的
  • DATE=`date`
    DATE=$(date)
    
  • 在脚本中定义普通字符串变量时,应尽量把变量的内容用双引号括起来;

    • 例如:DIRNAME="/tmp"
  • 单纯数字的变量内容可以不加引号;

    • 例如:NUM=10

4. 变量的分类

Shell 的变量有三种分类,每种都有其不同的作用范围

4.1 局部变量

局部变量,顾名思义其只在 Shell 脚本中定义的变量,或在 Shell 脚本函数中定义的变量,只能在 Shell 脚本中使用,或只能在 Shell 脚本函数中使用

例如:

[root@master Shell]# cat local_var.sh 
#!/bin/bash

DIR=/tmp
echo "dir is ${DIR}"
[root@master Shell]# bash local_var.sh 
dir is /tmp
[root@master Shell]# echo "dir is ${DIR}"
dir is 

我们可以看出在脚本内部定义的变量 DIR 只在 local_var 脚本内可以使用,在全局下没有此变量,后期我们学到函数再来说明函数中的局部变量。

4.2 环境变量

环境变量为当前 Shell 中定义的临时变量,在当前 Shell 定义的变量可以传递给子 Shell,没有父子关系的 Shell 不能使用此变量,当然可以利用 export 将当前 Shell 的变量传递给其他终端的 Shell 中。

当前 Shell 为父 Shell,在其上运行 bash 就可以进入到子 Shell 中,让我们来看示例

# 在当前Shell中定义变量STR
[root@master Shell]# STR="Shell"						
[root@master Shell]# echo "hello ${STR}"
hello Shell
# 运行命令bash进入子Shell
[root@master Shell]# bash
# 在子Shell中查看没有STR
[root@master Shell]# echo "hello ${STR}"
hello 
# 退出子Shell回到父Shell
[root@master Shell]# exit
exit
# 利用export导入环境变量
[root@master Shell]# export STR="Shell"
# 在此进入子Shell
[root@master Shell]# bash
# 查看子Shell中已经有了变量STR
[root@master Shell]# echo "hello ${STR}"
hello Shell

环境变量为临时的,在我们启动终端的时候,系统会从特定的文件中加载一系列环境变量。

系统中还有一些系统内置的变量,例如 PATHUSER 等,我们可以利用命令 env 或 export 来查看当前 Shell 的环境变量,例如:

[root@master Shell]# env
XDG_SESSION_ID=1276
HOSTNAME=master
SELINUX_ROLE_REQUESTED=
Shell=/bin/bash
TERM=xterm
HISTSIZE=1000
SSH_CLIENT=61.150.11.174 55929 22
SELINUX_USE_CURRENT_RANGE=
SSH_TTY=/dev/pts/0
USER=root
...

# 查看当前Shell
[root@master Shell]# echo $Shell
/bin/bash
# 查看可执行命令的路径
[root@master Shell]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
# 查看当前登录系统用户
[root@master Shell]# echo $USER
root

linux 系统的一些内置参数

$Shell  	默认 Shel
$HOME   	当前用户家目录
$IFS    	内部字段分隔符
$LANG   	默认语
$PATH   	默认可执行程序路径
$PWD    	当前目录
$UID    	当前用户 ID
$USER   	当前用户
$HISTSIZE	历史命令大小,可通过 HISTTIMEFORMAT 变量设置命令执行时间
$RANDOM	    随机生成一个 0 至 32767 的整数
$HOSTNAME	主机名

4.3 全局变量

全局变量为在当前 Shell 进程中运行的脚本都可以使用该变量,在 Shell 中默认定义的变量就是全局变量,例如:

[root@master Shell]# cat global_var1.sh 
#!/bin/bash
echo "${STR1}"				# 查看STR1变量的内容
STR2="sh"							# 定义STR2变量的值为sh
[root@master Shell]# cat global_var2.sh 
#!/bin/bash
echo "${STR2}"				# 查看STR2变量的内容
[root@master Shell]# STR1="Shell"                    
[root@master Shell]# . ./global_var1.sh 
Shell
[root@master Shell]# . ./global_var2.sh 
sh

我们可以看出利用. 来运行 Shell 脚本,是在当前用户登录的 Shell 终端中运行,其变量是在当前 Shell 进程中可以共享的。

5. 实例

我们通过简单编写一个获取当前用户登录信息的脚本,来展示目前登录用户的信息

[root@master Shell_var]# cat login_info.sh  
#!/bin/bash
# Description: login info scripts
# Auth: kaliarch
# Email: kaliarch@163.com
# function: show user info
# Date: 2020-03-08 13:36
# Version: 1.0

echo "当前登录系统用为:  ${USER}"
echo "当前登录系统时间:  $(date +"%Y-%m-%d %H:%M:%S")"
echo "当前登录系统Shell: ${Shell}"
echo "当前用户家目录为:  ${HOME}"

# 运行脚本查看内容
[root@master Shell_var]# bash login_info.sh 
当前登录系统用为:  root
当前登录系统时间:  2020-03-08 12:16:04
当前登录系统Shell: /bin/bash
当前用户家目录为:  /root

6. 注意事项

  • 在引用变量是我们遵守规范,带上 {},可以避免不必要的麻烦和坑;
  • 命名变量见名知意,形成有自己一套的命名规范;
  • 学习 Shell 需要多动手实践,将变量的三种类型通过实践真正理解,后期在编写 Shell 的时候可以灵活运用。

7. 小结

变量在我们 Shell 编程中的有这独特的地位,其将我们的一些数据通过一次性的赋值,大大增强了脚本的灵活性与后期可维护性。我们需要通过本章节充分理解 Shell 的基本操作及其种类,在编写 Shell 中灵活运用,遵循定义及使用规范,可以使得我们编写的 Shell 更加友好及健壮。