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

Shell 变量的作用域可以分为三种:

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

Shell 局部变量

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

  1. #!/bin/bash
  2. #定义函数
  3. function func(){
  4. a=99
  5. }
  6. #调用函数
  7. func
  8. #输出函数内部的变量
  9. echo $a

输出结果:
99

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

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

  1. #!/bin/bash
  2. #定义函数
  3. function func(){
  4. local a=99
  5. }
  6. #调用函数
  7. func
  8. #输出函数内部的变量
  9. echo $a

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

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

本节只是演示了函数的定义和调用,并没有对语法细节作过多说明,后续我们将在《Shell函数》一节中进行深入讲解。

Shell 全局变量

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

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

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


图1:打开两个 Shell 窗口

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

需要强调的是,全局变量的作用范围是当前的 Shell 进程,而不是当前的 Shell 脚本文件,它们是不同的概念。打开一个 Shell 窗口就创建了一个 Shell 进程,打开多个 Shell 窗口就创建了多个 Shell 进程,每个 Shell 进程都是独立的,拥有不同的进程 ID。在一个 Shell 进程中可以使用 source 命令执行多个 Shell 脚本文件,此时全局变量在这些脚本文件中都有效。

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

  1. #!/bin/bash
  2. echo $a
  3. b=200

b.sh 的代码如下:

  1. #!/bin/bash
  2. echo $b

打开一个 Shell 窗口,输入以下命令:

[c.biancheng.net]$ a=99
[c.biancheng.net]$ . ./a.sh
99
[c.biancheng.net]$ . ./b.sh
200

这三条命令都是在一个进程中执行的,从输出结果可以发现,在 Shell 窗口中以命令行的形式定义的变量 a,在 a.sh 中有效;在 a.sh 中定义的变量 b,在 b.sh 中也有效,变量 b 的作用范围已经超越了 a.sh。

注意,必须在当前进程中运行 Shell 脚本,不能在新进程中运行 Shell 脚本,不了解的读者请转到《执行Shell脚本》。

Shell 环境变量

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

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

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

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


图2:进入 Shell 子进程

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

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

[c.biancheng.net]$ a=22       #定义一个全局变量
[c.biancheng.net]$ echo $a #在当前Shell中输出a,成功
22
[c.biancheng.net]$ bash #进入Shell子进程
[c.biancheng.net]$ echo $a #在子进程中输出a,失败 [c.biancheng.net]$ exit #退出Shell子进程,返回上一级Shell
exit
[c.biancheng.net]$ export a #将a导出为环境变量
[c.biancheng.net]$ bash #重新进入Shell子进程
[c.biancheng.net]$ echo $a #在子进程中再次输出a,成功
22
[c.biancheng.net]$ exit #退出Shell子进程
exit
[c.biancheng.net]$ exit #退出父进程,结束整个Shell会话

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

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

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

第一个窗口中的环境变量 a 在第二个窗口中就无效。

环境变量也是临时的

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

Shell脚本 全局变量、局部变量的更多相关文章

  1. shell脚本中局部变量local

    shell脚本中局部变量 在shell中定义函数可以使代码模块化,便于复用代码.不过脚本本身的变量和函数的变量的作用域问题可能令你费解,在这里梳理一下这个问题. (1)Shell脚本中定义的变量是gl ...

  2. shell脚本一

    在一些复杂的Linux维护工作中,大量重复的输入和交互操作不但费时费力,容易出错.这时候就需要用到脚本. 编写脚本的好处:  批量的处理,自动化的完成维护,减轻管理员的负担. linux的shell脚 ...

  3. Linux&shell之高级Shell脚本编程-创建函数

    写在前面:案例.常用.归类.解释说明.(By Jim) 使用函数 #!/bin/bash # testing the script function myfun { echo "This i ...

  4. Linux Shell脚本编程的注意事项

    Linux下(Shell脚本 http://www.jbxue.com/jb/shell/)编程的一些注意事项,如编程风格.命名风格等. 一.常用技巧 ssh user@server bash < ...

  5. Shell脚本编程总结及速查手册

    Shell是一种编程语言, 它像其它编程语言如: C, Java, Python等一样也有变量/函数/运算符/if语句/循环控制/… 但在开始之前, 我想先理清Shell语言与Shell之间的关系. ...

  6. Shell脚本笔记

      如何查询文件里的某个字符串? grep “字符串” 文件 例:grep "abc" tmp.txt   如何将查询出来的内容赋给变量? str=$(grep "abc ...

  7. 《Linux命令行与shell脚本编程大全》 第十六章 学习笔记

    第十六章:创建函数 基本的脚本函数 创建函数 1.用function关键字,后面跟函数名 function name { commands } 2.函数名后面跟空圆括号,标明正在定义一个函数 name ...

  8. Shell 脚本常用命令

    Shell脚本是Linux开发工作中常用的工具,但是我一直没有找到一个适合自己的简明扼要的HandBook.在工作过程中整理了一下,贴在这里已备查看. 1           Shell中的特殊符号 ...

  9. [转载]关于shell脚本的基本语法

    关于shell脚本的基本语法 整理于:2014-03-31,何俭飞,mymladdr@sina.com 一.执行 1.shell脚本如果要被执行,一般地必须要有执行权限"x"(除了 ...

随机推荐

  1. 使用mybatis时,sql查出来的结果映射不进逆向工程生成的该模型

    为了逆向工程生成驼峰效果,我们会在两个单词中间加_,但是在mybatis中,写sql语句时,要加别名,使别名和你逆向工程生成的该数据模型里面的属性名一致,否则查出来的结果映射不进去.

  2. 使用androidstudio时遇到的一些小错误

    1 路径名字中不能有汉字  报如下错误:Error:(1, 0) Your project path contains non-ASCII characters. This will most lik ...

  3. 利用Python批量重命名一系列文件名杂乱的文件

    假设目录下面有这样一系列命令杂乱的文件: OPENFOAM -TRAINING- PART- #1.pdf OPENFOAM - TRAINING- PART- #2.pdf OPENFOAM- TR ...

  4. webapp兼容问题解决

    1. IOS移动端click事件300ms的延迟响应 移动设备上的web网页是有300ms延迟的,玩玩会造成按钮点击延迟甚至是点击失效.这是由于区分单击事件和双击屏幕缩放的历史原因造成的, 2007年 ...

  5. Vue学习笔记【21】——Vue中的动画(v-for 的列表过渡)

    定义过渡样式:  <style>    .list-enter,    .list-leave-to {      opacity: 0;      transform: translat ...

  6. Java——抽象:abstract

    3.4抽象:abstract 3.4.1什么是抽象类? 随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用.类的设计应该保证父类和子类能够共享特征.有时将一个父类设计得非常抽象 ...

  7. 拆边+BFS队列骚操作——cf1209F

    这个拆边+队列操作实在是太秒了 队列头结点存的是一个存点集的vector,1到这个点集经过的路径权值是一样的,所以向下一层拓展时,先依次走一遍每个点的0边,再走1边...以此类推,能保证最后走出来的路 ...

  8. 构造——cf1213E

    分情况讨论,构造很简单 #include<bits/stdc++.h> using namespace std; #define N 200005 ],t[]; int n,s1,s2,t ...

  9. NodeJS+Express+mySQL服务端开发详解

    随着NodeJS的发展,现在已经被很多人熟知,NodeJS已经成为了前端开发人员必备的技能.本文不会对NodeJS过多介绍 如果你感兴趣可以访问NodeJS 官网, 维基百科 本文是利用NodeJS+ ...

  10. python之-sqlite3

    在这些 URL 中,hostname 表示 MySQL 服务所在的主机,可以是本地主机(localhost),也可以是远程服务器.数据库服务器上可以托管多个数据库,因此 database 表示要使用的 ...