zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Shell 变量的作用域详解

变量shell 详解 作用域
2023-09-14 09:01:47 时间

局部变量


定义本地变量(变量写在脚本里面,只有在脚本执行的时候,变量才会生效)

本地变量在用户当前的 Shell 生存期的脚本中使用。例如,本地变量 ZHANG取值为 ett098,这个值只在用户当前 Shell 生存期中有意义。如果在 Shell 中启动另一个进程或退出,本地变量 ZHANG 值将无效。
什么意思呢?举个例子来看
[root@localhost ~]# ZHANG=shell
[root@localhost ~]# vi shell.sh
echo $ZHANG
[root@localhost ~]# sh shell.sh
#<==输出为空
普通字符串变量定义
变量名=value
变量名='value'
变量名="value"
shell 中变量名的要求:一般是字母,数字,下划线组成。字母开头。例: zhang,zhang123,zhang_training。
例 1:下面的例子会输出什么结果。
a=192.168.1.2
b='192.168.1.2'
c="192.168.1.2"
echo "a=$a"
echo "b=$b"
echo "c=${c}

  

提示:
1)$c 和${c}在这里等同
答案:
a=192.168.1.2
b=192.168.1.2
c=192.168.1.2
例 2:想一想 a,b,c 各是什么结果?
a=192.168.1.2-$a
b='192.168.1.2-$a'
c="192.168.1.2-$a"
echo "a=$a"
echo "b=$b"
echo "c=${c}"
答案:
a=192.168.1.2-
b=192.168.1.2-$a
c=192.168.1.2-192.168.1.2-
提示:
第一种定义 a 变量的方式是直接定义变量内容,内容一般为简单连续的数字、 字符串、路径名等。
第二种定义 b 变量的方式是通过单引号定义变量。这个方式的特点是:输出变量时引号里是什么就输出什么,即使内容中有变量也会把变量名原样输出。此法比较适合于定义显示纯字符串。
第三种定义 c 变量方式是通过双引号定义变量。这个方式的特点是:输出变量时引号里的变量会经过解析后输出该变量的内容,而不是把引号中变量名原样输出,适合于字符串中附带有变量的内容的定义。
习惯:数字不加引号,其他默认加双引号

 

 

Shell 变量的作用域(Scope),就是 Shell 变量的有效范围(可以使用的范围)。

在不同的作用域中,同名的变量不会相互干涉,就好像 A 班有个叫小明的同学,B 班也有个叫小明的同学,虽然他们都叫小明(对应于变量名),但是由于所在的班级(对应于作用域)不同,所以不会造成混乱。但是如果同一个班级中有两个叫小明的同学,就必须用类似于“大小明”、“小小明”这样的命名来区分他们。

Shell 变量的作用域可以分为三种:
  • 有的变量只能在函数内部使用,这叫做局部变量(local variable);
  • 有的变量可以在当前 Shell 进程中使用,这叫做全局变量(global variable);
  • 而有的变量还可以在子进程中使用,这叫做环境变量(environment variable)。

Shell 局部变量


Shell 也支持自定义函数,但是 Shell 函数和 C++、Java、C# 等其他编程语言函数的一个不同点就是:在 Shell 函数中定义的变量默认也是全局变量,它和在函数外部定义变量拥有一样的效果。请看下面的代码:

[root@www ~]# vim local.sh 
#!/bin/bash
#定义函数
function func(){
a=99
}
#调用函数
func

echo $a

[root@www ~]# chmod o+x local.sh 
[root@www ~]# ./local.sh 
99

a 是在函数内部定义的,但是在函数外部也可以得到它的值,证明它的作用域是全局的,而不是仅限于函数内部。

要想变量的作用域仅限于函数内部,可以在定义时加上local命令,此时该变量就成了局部变量。请看下面的代码

[root@www ~]# vim local.sh 
#!/bin/bash
function func(){
local a=99
}
func

echo $a

[root@www ~]# chmod o+x local.sh 
[root@www ~]# ./local.sh 

[root@www ~]# 

输出结果为空,表明变量 a 在函数外部无效,是一个局部变量。

Shell 变量的这个特性和 JavaScript 中的变量是类似的。在 JavaScript 函数内部定义的变量,默认也是全局变量,只有加上var关键字,它才会变成局部变量。

Shell 全局变量


所谓全局变量,就是指变量在当前的整个 Shell 进程中都有效。每个 Shell 进程都有自己的作用域,彼此之间互不影响。在 Shell 中定义的变量,默认就是全局变量。

想要实际演示全局变量在不同 Shell 进程中的互不相关性,可在图形界面下同时打开两个 Shell,或使用两个终端远程连接到服务器(SSH)。

首先打开一个 Shell 窗口,定义一个变量 a 并赋值为 99,然后打印,这时在同一个 Shell 窗口中是可正确打印变量 a 的值的。然后再打开一个新的 Shell 窗口,同样打印变量 a 的值,但结果却为空,如图 1 所示。

打开两个Shell窗口

这说明全局变量 a 仅仅在定义它的第一个 Shell 进程中有效,对新的 Shell 进程没有影响。这很好理解,就像小王家和小徐家都有一部电视机(变量名相同),但是同一时刻小王家和小徐家的电视中播放的节目可以是不同的(变量值不同)。

例如,现在有两个 Shell 脚本文件,分别是 a.sh 和 b.sh。a.sh 的代码如下:

#!/bin/bash
echo $a
b=200

b.sh 的代码如下:

#!/bin/bash
echo $b

Shell 环境变量


全局变量只在当前 Shell 进程中有效,对其它 Shell 进程和子进程都无效。如果使用export命令将全局变量导出,那么它就在所有的子进程中也有效了,这称为“环境变量”。

环境变量被创建时所处的 Shell 进程称为父进程,如果在父进程中再创建一个新的进程来执行 Shell 命令,那么这个新的进程被称作 Shell 子进程。当 Shell 子进程产生时,它会继承父进程的环境变量为自己所用,所以说环境变量可从父进程传给子进程。不难理解,环境变量还可以传递给孙进程。

注意,两个没有父子关系的 Shell 进程是不能传递环境变量的,并且环境变量只能向下传递而不能向上传递,即“传子不传父”。

创建 Shell 子进程最简单的方式是运行 bash 命令,如图 2 所示。

进入Shell子进程

通过exit命令可以一层一层地退出 Shell。

下面演示一下环境变量的使用:

[root@localhost ~]$ a=22       #定义一个全局变量
[root@localhost ~]$ echo $a    #在当前Shell中输出a,成功
22
[root@localhost ~]$ bash       #进入Shell子进程
[root@localhost ~]$ echo $a    #在子进程中输出a,失败

[root@localhost ~]$ exit       #退出Shell子进程,返回上一级Shell
exit
[root@localhost ~]$ export a   #将a导出为环境变量
[root@localhost ~]$ bash       #重新进入Shell子进程
[root@localhost ~]$ echo $a    #在子进程中再次输出a,成功
22
[root@localhost ~]$ exit       #退出Shell子进程
exit
[root@localhost ~]$ exit       #退出父进程,结束整个Shell会话

可以发现,默认情况下,a 在 Shell 子进程中是无效的;使用 export 将 a 导出为环境变量后,在子进程中就可以使用了。

export a这种形式是在定义变量 a 以后再将它导出为环境变量,如果想在定义的同时导出为环境变量,可以写作export a=22

一直强调的是环境变量在 Shell 子进程中有效,并没有说它在所有的 Shell 进程中都有效;如果你通过终端创建了一个新的 Shell 窗口,那它就不是当前 Shell 的子进程,环境变量对这个新的 Shell 进程仍然是无效的。请看下图:

环境变量在不同的Shell窗口中无效

环境变量也是临时的


通过 export 导出的环境变量只对当前 Shell 进程以及所有的子进程有效,如果最顶层的父进程被关闭了,那么环境变量也就随之消失了,其它的进程也就无法使用了,所以说环境变量也是临时的。

有读者可能会问,如果我想让一个变量在所有 Shell 进程中都有效,不管它们之间是否存在父子关系,该怎么办呢?

只有将变量写入 Shell 配置文件中才能达到这个目的!Shell 进程每次启动时都会执行配置文件中的代码做一些初始化工作,如果将变量放在配置文件中,那么每次启动进程都会定义这个变量。