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. BGV再掀DeFi投资热潮,NGK全球启动大会圆满落幕

    此次全球启动大会的主题为"BGV再掀DeFi投资热潮,后市发展如何". 首先发言的是NGK灵石团队首席技术官STEPHEN先生,他先是对出席此次大会的嘉宾.到场的媒体记者以及NGK ...

  2. uni-app小白入门自学笔记(一)

    码文不易啊,转载请带上本文链接呀,感谢感谢 https://www.cnblogs.com/echoyya/p/14427845.html 目录 码文不易啊,转载请带上本文链接呀,感谢感谢 https ...

  3. Redis 日志篇:系统高可用的杀手锏

    特立独行是对的,融入圈子也是对的,重点是要想清楚自己向往怎样的生活,为此愿意付出怎样的代价. 我们通常将 Redis 作为缓存使用,提高读取响应性能,一旦 Redis 宕机,内存中的数据全部丢失,假如 ...

  4. Echart饼图旋转

    1 <!DOCTYPE html> 2 <html lang="en"> 3 4 <head> 5 <meta charset=" ...

  5. idea中Maven-build lifecycle中下面标签详解

    原文链接:https://blog.csdn.net/mr_orange_klj/article/details/82153945 Maven是基于一个build lifecycle的中心概念,意味着 ...

  6. 微信小程序:单选框radio和复选框CheckBox

    单选框radio: 可以通过color属性来修改颜色. 复选框checkbox:

  7. Go的map

    目录 map 一.map的创建 1.map的类型 2.定义并初始化 二.给map添加元素 三.获取map的元素 四.删除map的元素 五.获取map的长度 六.map的类型 七.map的相等性 八.循 ...

  8. golang知识总结

    目录 1.slice扩容规则 2.内存寻址.内存对齐,go结构体内存对齐策略 3.go语言map类型分析 3.1 hash冲突 3.2 hash表扩容 3.3 go语言中的map结构是hash表. 3 ...

  9. LeetCode-二叉树的镜像

    二叉树的镜像 二叉树的镜像 给定一个二叉树,输出二叉树的镜像. 只需要使用一个简单的递归,分别对左右子树反转后再对当前结点进行反转. #include<iostream> #include ...

  10. C#扩展方法的一分钟小例子

    扩展方法是静态方法,是类的一部分,但没有在类的源代码中,就像一个补丁 首先创建一个静态类,然后创建一个静态方法,重点是静态方法的参数 public static class xExtension { ...