细读shell-2

 9 . var=valueexport 前后差在哪? 

来了解一下 bash 变量(variable)

所谓的变量,就是利用一个特定的"名称"(name)来存取一段可以变化的""(value)

*设定(set)* 

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

代码:

   ls -la /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",然后,第二行再将值扩充为 "A:B:C:E"  

上面的扩充范例,我们使用区隔符号( : )来达到扩充目的, 

要是没有区隔符号的话,如下是有问题的: 

A=BCD 

A=$AE 

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

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

A=BCD 

A=${A}E 

上例中,我们使用 {} 将变量名称的范围给明确定义出来, 

如此一来,我们就可以将 A 的变量值从 BCD 给扩充为 BCDE

 

10.* 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 的参数。 

 *取消变量

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

  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

应该不难发现为何同样的 var=${str=expr} null unset 之下的不同吧? 

11. exec source 差在哪?

cd /etc/aa/bb/cc可以执行 

但是把这条命令写入shell shell 不执行! 

这是什幺原因呀! 

首先, 我们所执行的任何程序,都是由父行程(parent process)所产生出来的一个子行程(child process)  子行程在结束后,将返回到父行程去。此一现像在 Linux 系统中被称为 fork

* 所谓环境变量其实就是那些会传给子行程的变量。 

* 环境变量只能从父行程到子行程单向继承。换句话说:在子行程中的环境如何变更,均不会影响父行程的环境。 

接下来,再让我们了解一下命令脚本(shell script)的概念。 

所谓的 shell script  讲起来很简单,就是将你平时在 shell prompt  后所输入的多行 command line 依序写入一个文件去而已。 

其中再加上一些条件判断、互动界面、参数运用、函数调用、等等技巧,得以让 script 更加"聪明"的执行.

再结合以上两个概念(process + script),那应该就不难理解如下这句话的意思了: 

* 正常来说,当我们执行一个 shell script 时,其实是先产生一个 sub-shell 的子行

sub-shell 再去产生命令行的子行程。

引用

cd /etc/aa/bb/cc可以执行 

但是把这条命令写入shell shell 不执行!

这是什幺原因呀!

引用:

因为,一般我们跑的 shell script 是用 subshell 去执行的。 

process 的观念来看,是 parent process 产生一个 child process 去执行, 

child 结束后,会返回 parent ,但 parent 的环境是不会因 child 的改变而改变的。

所谓的环境元数很多,凡举 effective id, variable, workding dir 等等... 

其中的 workding dir ($PWD) 正是楼主的疑问所在: 

当用 subshell 来跑 script 的话,sub shell $PWD 会因为 cd 而变更, 

但当返回 primary shell 时,$PWD 是不会变更的。 

 

那好,接下来,再让我们了解一下 source 命令好了。 

* 所谓 source 就是让 script 在当前 shell 内执行、而不是产生一个 sub-shell 来执

由于所有执行结果均于当前 shell 内完成,若 script 的环境有所改变,当然也会改变

境了�u 

因此,只要我们要将原本单独输入的 script 命令行变成 source 命令的参数,就可轻

前例提到的问题了。 

比方说,原本我们是如此执行 script 的:  

./my.script

现在改成这样即可:

source ./my.script 

或: 

. ./my.script

---- exec 又与 source/fork 有何不同呢?

要了解 exec 或许较为复杂,尤其扯上 File Descriptor 的话...

* exec 也是让 script 在同一个行程上执行,但是原有行程则被结束了。 

也就是简而言之:原有行程会否终止,就是 exec source/fork 的最大差异了。

下面让我们写两个简单的 script ,分别命令为 1.sh 2.sh  

1.sh   

代码:

 

#!/bin/bash 

A=B 

echo "PID for 1.sh before exec/source/fork:$$" 

export A 

echo "1.sh: \$A is $A" 

case $1 in 

        exec) 

                echo "using exec..." 

                exec ./2.sh ;; 

        source) 

                echo "using source..." 

                . ./2.sh ;; 

        *) 

                echo "using fork by default..." 

                ./2.sh ;; 

esac

echo "PID for 1.sh after exec/source/fork:$$" 

echo "1.sh: \$A is $A" 

2.sh

#!/bin/bash 

echo "PID for 2.sh: $$" 

echo "2.sh get \$A=$A from 1.sh" 

A=C 

export A 

echo "2.sh: \$A is $A" 

然后,分别跑如下参数来观察结果:  

代码:

$ ./1.sh fork 

$ ./1.sh source 

$ ./1.sh exec 

 

12.( ) { } 差在哪?

许多时候,我们在 shell 操作上,需要在一定条件下一次执行多个命令, 也就是说,要么不执行,要么就全执行,而不是每次依序的判断是否要执行下一个命令。

们就可引入"命令群组"(command group)的概念:将多个命令集中处理。

虽然两者都可将多个命令作群组化处理,但若从技术细节上,却是很不一样的:

( ) command group 置于 sub-shell 去执行,也称 nested sub-shell

{ } 则是在同一个 shell 内完成,也称为 non-named command group

要是在 command group 中扯上变量及其它环境的修改,我们可以根据不同的需求来使用 ( ) { }  

13. function

所谓的 function ,就是用一个名字去命名一个 command group ,然后再调用这个名字去

执行 command group  

non-named command group 来推断,大概你也可以猜到我要说的是 { } 了吧?

bash 中,function 的定义方式有两种: 

方法一

function function_name { 

    command1 

    command2 

    command3 

    .... 

}

方式二:

fuction_name () { 

    command1 

    command2 

    command3 

    .... 

}

若碰到所定意的名称与现有的命令或别名(Alias)冲突的话,方式二

或许会失败。 

function 在某一程度来说,也可称为"函式",但请不要与传统编程所使用的函式(library)搞混了,毕竟两者差异很大。 

惟一相同的是,我们都可以随时用"已定义的名称"来调用它们... 

若我们在 shell 操作中,需要不断的重复质行某些命令,我们首先想到的,或许是将命令写成命令稿(shell script) 

不过,我们也可以写成 function ,然后在 command line 中打上 function_name 就可当一舨的 script 来使用了。 

只是若你在 shell 中定义的 function ,除了可用 unset function_name 取消外,一旦退出shell function 也跟着取消。 

然而,在 script 中使用 function 却有许多好处,除了可以提高整体 script 的执行效能外(因为已被加载) 

简单而言,若你会将多个命令写成 script 以供调用的话,那,你可以将 function 看成script 中的 script ... ^_^ 

 

 

你可能感兴趣的:(shell,bash)