var=value?export前后差在哪?-- Shell十三问<第五问>

这次让我们暂时丢开 command line ,先来了解一下 bash 变量(variable)吧.所谓的 变量,就是就是利用一个特定的"名称"(name)来存取一段可以变化的"值"(value)。

在 bash 中,你可以用 "=" 来设定或重新定义变量的内容:

name=value

在设定变量的时侯,得遵守如下 规则:

  • 等号左右两边不能使用区隔符号(IFS),也应避免使用 shell 的保留字符(meta charactor)。
  • 变量名称不能使用 $ 符号。
  • 变量名称的第一个字母不能是数字(number)。
  • 变量名称长度不可超过 256 个字母。
  • 变量名称及变量值之大小写是有区别的(case sensitive)。

如下是一些变量设定时常见的错误:

A= B :不能有 IFS
1A=B :不能以数字开头
$A=B :名称不能有 $
a=B :这跟 a=b 是不同的

如下则是可以接受的设定:

A=" B" :IFS 被关闭了 (请参考前面的 quoting 章节)
A1=B :并非以数字开头
A=$B :$ 可用在变量值内
This_Is_A_Long_Name=b :可用 _ 连接较长的名称或值,且大小写有别。

变量替换(substitution)

Shell 之所以强大,其中的一个因素是它可以在命令行中对变量作替换(substitution)处理。在命令行中使用者可以使用 $ 符号加上变量名称(除了在用 = 号定义变量名称之外),将变量值给替换出来,然后再重新组建命令行。

比方:

$ A=ls
$ B=la
$ C=/tmp
$ $A -$B $C
(注意:以上命令行的第一个 $ 是 shell prompt ,并不在命令行之内。)

必需强调的是,我们所提的变量替换,只发生在 command line 上面。(是的,让我们再回到 command line 吧!)仔细分析最后那行 command line ,不难发现在被执行之前(在输入CR 字符之前),$ 符号会对每一个变量作替换处理(将变量值替换出来再重组命令行),最后会得出如下命令行:

ls -la /tmp

还记得第二章我请大家"务必理解"的那两句吗?若你忘了,那我这里再重贴一遍:

注意:

若从技术细节来看,shell 会依据 IFS(Internal Field Seperator) 将 command line 所输入的文字给拆解为"字段"(word)。然后再针对特殊字符(meta)先作处理,最后再重组整行command line 。

这里的 $ 就是 command line 中最经典的 meta 之一了,就是作变量替换的!

在日常的 shell 操作中,我们常会使用 echo 命令来查看特定变量的值,例如:

$ echo $A -$B $C

我们已学过, echo 命令只单纯将其 argument 送至"标准输出"(STDOUT,通常是我们的荧幕)。 所以上面的命令会在荧幕上得到如下结果:

ls -la /tmp

这是由于 echo 命令在执行时,会先将 \(A(ls)、\)B(la)、跟 $C(/tmp) 给替换出来的结果。

利用 shell 对变量的替换处理能力,我们在设定变量时就更为灵活了:

A=B
B=$A

这样,B 的变量值就可继承 A 变量"当时"的变量值了。不过,不要以"数学逻辑"来套用变量的设定,比方说:

A=B
B=C

这样并不会让 A 的变量值变成 C 。再如:

A=B
B=$A
A=C

同样也不会让 B 的值换成 C 。

上面是单纯定义了两个不同名称的变量:A 与 B ,它们的值分别是 B 与 C 。

若变量被重复定义的话,则原有旧值将被新值所取代。(这不正是"可变的量"吗?

当我们在设定变量的时侯,请记着这点:

  • 用一个名称储存一个数值

    仅此而已。

此外,我们也可利用命令行的变量替换能力来"扩充"(append)变量值:

A=B:C:D
A=$A:E

这样,第一行我们设定 A 的值为 "B:C:D",然后,第二行再将值扩充为 "B:C:D:E" 。

上面的扩充范例,我们使用区隔符号( : )来达到扩充目的,要是没有区隔符号的话,如下是有问题的:

A=BCD
A=$AE

因为第二次是将 A 的值继承 $AE 的提换结果,而非 $A 再加 E !

要解决此问题,我们可用更严谨的替换处理:

A=BCD
A=${A}E

上例中,我们使用 {} 将变量名称的范围给明确定义出来,如此一来,我们就可以将 A 的变量值从 BCD 给扩充为 BCDE 。

export

严格来说,我们在当前 shell 中所定义的变量,均属于"本地变量"(local variable),只有经过 export 命令的"输出"处理,才能成为环境变量(environment variable):

$ A=B
$ export A

或:

$ export A=B

经过 export 输出处理之后,变量 A 就能成为一个环境变量供其后的命令使用。

在使用 export 的时侯,请别忘记 shell 在命令行对变量的"替换"(substitution)处理,

比方说:

$ A=B
$ B=C
$ export $A

上面的命令并未将 A 输出为环境变量,而是将 B 作输出,这是因为在这个命令行中,$A 会首先被提换出 B 然后再"塞回"作 export 的参数。

要理解这个 export ,事实上需要从 process 的角度来理解才能透彻。我将于下一章为大家说明 process 的观念,敬请留意。

取消变量

要取消一个变量,在 bash 中可使用 unset 命令来处理:

unset A

与 export 一样,unset 命令行也同样会作变量替换(这其实就是 shell 的功能之一),

因此:

$ A=B
$ B=C
$ unset $A

事实上所取消的变量是 B 而不是 A 。

此外,变量一旦经过 unset 取消之后,其结果是将整个变量拿掉,而不仅是取消其变量值。

如下两行其实是很不一样的:

$ A=
$ unset A

第一行只是将变量 A 设定为"空值"(null value),但第二行则让变量 A 不在存在。

虽然用眼睛来看,这两种变量状态在如下命令结果中都是一样的:

$ A=
$ echo $A
$ unset A
$ echo $A

请学员务必能识别 null value 与 unset 的本质区别,这在一些进阶的变量处理上是很严格的。

比方说:

$ str= # 设为 null
$ var=${str=expr} # 定义 var
$ echo $var
$ echo $str
$ unset str # 取消
$ var=${str=expr} # 定义 var
$ echo $var
expr
$ echo $str
expr

聪明的读者(yes, you!),稍加思考的话,应该不难发现为何同样的 var=${str=expr} 在 null 与 unset 之下的不同吧?

嗯... 好吧,我就解释一下 var=\({str=expr} :
首先,var=\)str 这个大家都可理解吧。而接下来的思考方向是,究竟 $str 这个变量是如下哪一种情况呢:

1) unset
2) null
3) not null
4) 假如是 unset ,那么 var=${str=expr} 的结果将是:
var=expr
str=expr
2) 假如是 null ,那 var=${str=expr} 的结果是:
var=
str=
3) 假如是 not null (比方为 xyz ),那 var=${str=expr} 之结果是:
var=xyz
str=xyz
接下来,再来看看 var=${str:=expr} 好了:
1) $str 为 not set :
var=expr
str=expr
2) $str 为 null :
var=expr
str=expr
3) $str 为 not null (str=xyz):
var=xyz
str=xyz
最后比教一下 ${str=expr} 与 ${str:=expr} :
* 两者在 not set 与 not null 都一至
* 但当 null 值时,前者会将 $var 与 $str 都设为 null ,但后者则设为 expr从这个再延伸出其它模拟,不防请大家"实作"观查一下有何不同?
var=${str-expr} vs var=${str:-expr}
var=${str+expr} vs var=${str:+expr}
var=${str?expr} vs var=${str:?expr}

var=value?export前后差在哪?-- Shell十三问<第五问>的更多相关文章

  1. $(( )) 与 $( ) 还有${ } 差在哪?-- Shell十三问<第八问>

    $(( )) 与 \(( ) 还有\){ } 差在哪?-- Shell十三问<第八问> 我们上一章介绍了 ( ) 与 { } 的不同,这次让我们扩展一下,看看更多的变化:$( ) 与 \( ...

  2. exec 跟 source 差在哪?-- Shell十三问<第六问>

    exec 跟 source 差在哪?-- Shell十三问<第六问> 这次先让我们从 CU Shell 版的一个实例贴子来谈起吧: 例中的提问是: cd /etc/aa/bb/cc 可以执 ...

  3. > 与 < 差在哪?-- Shell十三问<第十一问>

    > 与 < 差在哪?-- Shell十三问<第十一问> 谈到 I/O redirection ,不妨先让我们认识一下 File Descriptor (FD) .程序的运算,在 ...

  4. && 与 || 差在哪?-- Shell十三问<第十问>

    && 与 || 差在哪?-- Shell十三问<第十问> 好不容易,进入两位数的章节了... 一路走来,很辛苦吧?也很快乐吧? 在解答本章题目之前,先让我们了解一个概念:r ...

  5. ( ) 与 { } 差在哪?-- Shell十三问<第七问>

    ( ) 与 { } 差在哪?-- Shell十三问<第七问> 先说一下,为何要用 ( ) 或 { } 好了. 许多时候,我们在 shell 操作上,需要在一定条件下一次执行多个命令,也就是 ...

  6. " "( 双引号) 与 ' '( 单引号) 差在哪?-- Shell十三问<第四问>

    " "( 双引号) 与 ' '( 单引号) 差在哪?-- Shell十三问<第四问> 经过前面两章的学习,应该很清楚当你在 shell prompt 后面敲打键盘.直到 ...

  7. 别人 echo 、你也 echo ,是问 echo 知多少?-- Shell十三问<第三问>

    别人 echo .你也 echo ,是问 echo 知多少?-- Shell十三问<第三问> 承接上一章所介绍的 command line ,这里我们用 echo 这个命令加以进一步说明. ...

  8. [^ ] 跟 [! ] 差在哪?-- Shell十三问<第十四问>

    [^ ] 跟 [! ] 差在哪?-- Shell十三问<第十四问> 这道题目说穿了, 就是要探讨 Wildcard(通配符)与 Regular Expression(正则表达式)的差别的. ...

  9. shell十三问

    1) 为何叫做 shell ?在介绍 shell 是甚幺东西之前,不妨让我们重新检视使用者与计算机系统的关系:图(FIXME)我们知道计算机的运作不能离开硬件,但使用者却无法直接对硬件作驱动,硬件的驱 ...

随机推荐

  1. JavaScript 如何使用 setTimeout 实现 setInterval

    JavaScript 如何使用 setTimeout 实现 setInterval website multi content page setIntervalSimulator "use ...

  2. 如何实现一个 markdown 图片粘贴上传的博客后台系统

    如何实现一个 markdown 图片粘贴上传的博客后台系统 js 实现 drag & drop / copy & paste image uploader MongoDB 设计文档对象 ...

  3. Angular Routing

    Angular Routing v9.0.7 https://angular.io/start/start-routing

  4. SVG & Sprite & symbol & use

    SVG & Sprite & symbol & use https://www.zhangxinxu.com/sp/svgo/ https://www.zhangxinxu.c ...

  5. django学习-4.url动态传值

    1.前言 我们在浏览器访问一个网页A是通过一个指定的url地址去访问的.但在浏览器用一个不存在的url地址去执行访问是打不开正确的网页的,只会打开一个浏览器自带的有错误提示的网页. 在django框架 ...

  6. 1063 Set Similarity——PAT甲级

    1063 Set Similarity Given two sets of integers, the similarity of the sets is defined to be Nc/Nt*10 ...

  7. 玩遍博客网站,我整理了 Hugo 及其流行的风格主题

    搭建博客网站是个人进入互联网世界的最常见方式之一.伴随着网站技术的发展,如何搭建博客网站已经变得非常容易了.当然,你可以选择诸如 新浪博客.CSDN.博客园 之类的大型网站,快速创建依赖于大平台的个人 ...

  8. CSS元素层级的概念及性质

    元素的层级的介绍 什么是元素的层级 通过z-index可以改变开启定位元素的层级 父元素的层级再高也不会遮盖住子元素 元素的层级的介绍 什么是元素的层级 当元素开启定位后就会是元素提升一个层级,网页是 ...

  9. 不使用map和set实现LRU——那用List?

    遇到一道面试题,不使用map和set实现LRU,要求get的时间复杂度为O(logn),put的时间复杂度不超过O(n).想到了用ArrayList来实现,保存有序的key.然而牵涉add节点,在保证 ...

  10. 剑指 Offer 56 - I. 数组中数字出现的次数 + 分组异或

    剑指 Offer 56 - I. 数组中数字出现的次数 Offer_56_1 题目描述 解题思路 java代码 /** * 方法一:数位方法 */ class Offer_56_1_2 { publi ...