Linux Shell编程学习笔记(一)变量和文件描述符

时间:2023-02-12 21:59:53

看了<Unix的设计思想>,情不自禁希望重新学习我曾经忽视的linux的shell脚本编程。我并不是随波逐流,也不是那种顽固的技术党,只是很纯粹的想了解这么技术,如果你有什么好的想法,也可以跟我交流,下面是我的微博地址:

http://weibo.com/1752090185/profile?rightmod=1&wvr=5&mod=personinfo

如果你有跟我一样的学习热情,不妨跟我一块学习下去。

先说变量的定义:

变量的定义非常的简单,就是变量名=变量值,中间不加空格。如果你加上空格,那么shell会认为这个是一个命令而不是一个语句。而你在引用变量的 时候可以通过$变量名的方式来引用。其实你也可以通过${变量名}和$((变量名))的方式来取出它的值。$符号后面将被默认解析成变量,这里我做了一个 假设,比如你有一个变量为a,另外一个变量为ab,那么它取的是a的值还是ab的值呢?

如果出现的是下列这种语句呢?

$abc

对,应该是没有这个变量的,它会去查找abc这个变量,而这个变量我们压根就没有定义过.那么如果我们一定要取得a这个变量呢?相信你跟我一样已经有了答案,那就是写成${a}bc的形式。

接下来我们要说到一个很特别的变量叫做环境变量,环境变量我们可以理解为系统预设的宏,或者常量。当然这么说可能不合理,因为环境变量是可以设置的。子进程可以定义自己的环境变量,父进程会将环境变量的参数传递给子进程。

环境变量的设置要通过export命令,通过不带参数的export命令可以打印出所有的环境变量.也许有人会问,我为什么要用export命令 呢?我用=一样可以定义变量。实际上是这样的,我们自己定义的变量只在进程内有效,是不会传递给子进程的。那怎么测试这样东西呢?我们知道在shell执 行的大部分命令都是开启一个进程,发生了进程的上下文切换,我们可以在shell命令行里面再启动一个shell作为上一个shell的子进程。

在启动shell之前我们先定义两个变量

param1="david_chen"

export param2="chen_david"

接下来我们执行一下shell,我这里使用的是bash,我就执行一下bash

好了,接下来我们打印一下这两个参数吧,我们发现,param1没有东西,而param2是正常的。

在shell里面,每一变量的类型我们都可以理解为是一个字符串数组,也就是说在shell里面是没有类型的,也就是所谓的弱类型语言。

我们知道我们学习语言的本质目的是为了运算,我们先说一下数学运算.数学运算里面有三个常用的命令:

expr,let 和 bc.

expr,let用于整数运算,而bc用于浮点数运算。

之前我们提到过{}(())这两个算子,是用来引用变量的,这里我们引入了另外一个算子[],这些算子里面的变量都是可以不加$的,而能用于算数运 算的是(())和[] 也就是说这两个基本上是等价的。而在使用这些操作符号的时候一定要注意变量和符号之间要有空格。浮点数我们就不深究了,有需要我会再回头来看下资料的。

 

接下来我们接着我们的第二个主题,文件描述符。

其实文件描述符就是一个句柄,或者说是一个字长的整数。其中的三个整数是被系统占用的也就是0,1,2。它们分别对应着标准输入,标准输出和错误。重定向的目的就是将这些流的方向重新定向到你希望的地方。你可能将它持久化,可能是管道,也可能是网络。

不论我们是标准输出,还是错误输出,我们都会在屏幕上看到信息,因此我们可以看出,实际上标准输出和错误输出实际上对应的是同一个定向终端文件。也就是屏幕。我们可以通过数字来制定文件描述符的定向流。

比如说 2>syserr.log 那么也就是将2描述符也就是错误输出定向到syserr.log这个文件流中,而1>sysout.log也是一样的道理。如果你采用这种方法那么你的标准定向就会发生变化。

命令格式为:

cmd 2>syserr.log 1>sysout.log

如果你认为2中的数据没有你需要的,你可以定向到一个很特殊的文件 /dev/null中,所有被定向到这个文件中的流都会被丢弃。

接下来我将介绍一个非常重要的数据结构,数组。称它为数组可能并不恰当,可以称它为映射表,和其他的脚本语言非常类似,这里我们所说的数组一样具有映射功能。

数组的定义可以通过 array=(1 2 3 4)这种方式来定义,一般的脚本都会支持空格和,两种分割符号,但是在shell里面只支持空格。这里我们还是可以引用C语言中的指针思想,其实在 shell脚本中的数组就是一个地址指针。我们知道,在数组中,数组的指针对应着索引0的位置,我尝试了一下打印$array:

发现真的如我所愿打印出1 那么对于指针运算是否支持呢?在前面我们知道对于算数运算的算子是[].我尝试了一下指针的运算$[ array + 1].如我们所料的一样它打印出索引为1的指针指向数值。我们可以类比的推断出来,其实shell中的数组也是一种指针,通过对指针的运算,实际上得到的 还是个指针。与我们的传统变量不同,传统变量得到的是一个具体的值。那就更加奇怪了不是吗?

我们在定义一个变量a=1的时候,通过运算$[a+1]得到的是2,如果a是一个数组,我们得到的是一个指针指向值。这两种截然不同的操作只说明一 点。shell是有类型的,我们姑且可以认为有两种,一种是普通类型,一种是指针类型。不过貌似所有的语言大都是这种模式,只不过对于弱语言来说是后期绑 定的类型。

好的我们上面讲了我们在取得数组值的时候可以采用$[a+n]的方法来获得,还有一种是通过${a[n]}的形式来获得索引为n的值.如果我们要遍历整个数组呢?

${array[*]}

这种写法可以让我们遍历整个数组。对于数组长度的运算采用$[#array[*]],就跟我们的字符串数组一样。你定义了一个字符串数组a="123"

${#a}就是得到这个字符串数组的长度。说完索引数组,我们接着说关联数组

 关联数组之前是需要调用declare -A testarray来申明关联数组类型的,我也不知道为何,我希望有了解的读者能给我个答案。可以加我的微博@ -非子墨- 来跟我私信。

然后配置你的关联属性.对于关联数组中的key,你不得不给出[]下标,不然可能会出现"为关联数组赋值时必须使用下标"这种错误。所以关联数组的格式为:

([key1]=value1 [key2]=value2)

如果打印keys的话只需要在数组前面加上!符号~估计在数组中除了value就是key吧~~