![细说Linux系统管理(第2版)](https://wfqqreader-1252317822.image.myqcloud.com/cover/984/35010984/b_35010984.jpg)
2.4 Bash 中的变量和运算符
2.4.1 什么是变量
1.变量的定义
什么是变量呢?从字面上来看就是可以变的量。举一个例子,我们都做过数学的应用题,在应用题中经常定义x 的值是某个数,如果换了一道题,还是定义x 的值,但是x 的值发生了改变,那么这个x 就是变量。变量是存储在计算机内存当中的,其值可以改变。当Shell 脚本需要保存一些信息时,如一个文件名或一个数字,就把它存放在一个变量中。每个变量都有一个名字,所以很容易引用它。变量可以定制用户本身的工作环境。使用变量可以保存有用的信息,使系统获知用户相关设置。变量也可以用于保存暂时信息。
那么,应该如何定义变量呢?其实非常简单,命令如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_79.jpg?sign=1738887156-XhT5FdjyE1NVYjGI7NOwoLhCfdTNdbMy-0-47a959a36d0d2f1b293f63a40ef6450d)
在定义变量时,有一些规则需要遵守。
● 变量名可以由字母、数字和下画线组成,但是不能以数字开头。如果变量名是“2name”,则是错误的。
● 在Bash 中,变量的默认类型是字符串型。如果要进行数值运算,则必须指定变量类型为数值型。比如:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_80.jpg?sign=1738887156-cYWX07qPyxBA45QXbwxaGcxC6GxQzyVl-0-f47d957155d98cb8ff8b79352a8adf3e)
可以看到,变量aa 的值不是“3”,而是“1+2”。因为在Bash 中,变量类型是字符串型,所以系统认为“1+2”只是一个字符串,而不会进行数值运算(在2.4.7节中将介绍数值运算方法)。
● 变量用等号“=”连接值,在“=”左右两侧不能有空格。这是Shell 语言特有的格式要求。在绝大多数的其他语言中,在“=”左右两侧是可以加入空格的。但是,在Shell 中,命令的执行格式是“命令 [选项] [参数]”,如果在“=”左右两侧加入空格,那么Linux系统会误以为这是系统命令,是会报错的。
● 在变量值中如果有空格,则需要使用单引号或双引号包含,如test="hello world!"。用双引号括起来的内容“$”“\”和反引号都拥有特殊含义,而用单引号括起来的内容都是普通字符。
● 在变量值中,可以使用转义符“\”。
● 如果需要增加变量值,则可以进行变量叠加。例如:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_81.jpg?sign=1738887156-Gp4IhTzPxB7KRXPZEP0DAbeHv7LK0jAr-0-bed1261976c1c9492fc9c6739e5d11cd)
变量叠加可以使用两种格式:"$变量名"或${变量名}。
● 如果要把命令的执行结果作为变量值赋予变量,则需要使用反引号或$()包含命令。例如:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_82.jpg?sign=1738887156-RRAtW3dxL9cqyURmry937ucOAey1ZksI-0-7d405a72dc7f4286c14f29b46e9d11a5)
● 环境变量名中的字母建议采用大写形式,以便于和系统命令进行区分。
2.变量的分类
知道了变量的基本概念和定义变量的规则,我们来看看变量的分类。
在其他语言中,一般是按照变量的数据类型来进行分类的,常见的变量类型有数值型、字符串型、日期型、布尔型等。
但是,在Shell 中,为了简化,规定变量的数据类型默认是字符串型,所以就不适合按照变量的数据类型来进行分类了。这就导致Shell 中变量的分类和其他语言中变量的分类不一样,是按照变量的特征与作用来进行分类的,主要分为以下4种。
● 用户自定义变量。这种变量是最常见的变量,由用户自由定义变量名、变量的作用和变量值。绝大多数变量是这种变量。
● 环境变量。环境变量又可以细分为以下两种。
- 一种是用户自定义环境变量。这种环境变量可以由用户自定义变量名、变量的作用和变量值。用户自定义环境变量可以看成全局变量,在所有的子Shell 中生效。为了和系统命令进行区分,变量名中的字母建议采用大写形式。
- 另一种是系统环境变量。这种环境变量的变量名和变量的作用是由系统确定的,用户不能随意修改,只能修改变量的值。系统环境变量是用于定义操作系统基本环境的,是系统的重要参数,比如,当前登录用户、用户的家目录、命令的提示符等,都是和系统操作环境相关的数据。在Windows 系统中,同一台计算机可以有多个用户登录,而且每个用户都可以定义自己的桌面样式和分辨率,这些其实就是Windows系统的操作环境,可以当作Windows 系统的环境变量来理解。
● 位置参数变量。这种变量主要是用来向脚本或函数中传递参数或数据的,变量名是固定的,变量的作用也是固定的,用户只能修改变量的值。其实,位置参数变量可以看成预定义变量的一种(所以在有些书籍中认为Shell 的变量类型只有3种),只是位置参数变量比较多,笔者倾向于将其单独作为一种变量类型。
● 预定义变量。这种变量是在Bash 中已经定义好的变量,变量名不能自定义,变量的作用也是固定的,用户只能修改变量的值(预定义变量的这个特征和位置参数变量的特征是一致的,所以这两种变量都可以看成预定义变量)。
下面分别来学习这几种变量。
2.4.2 用户自定义变量
1.变量的定义
用户自定义变量是最常用的变量类型,也称作本地变量,其特点是变量名、变量的作用和变量值都是由用户自由定义的。那么,该如何定义变量呢?很简单,只需执行“变量名=变量值”命令即可,不过要遵守变量定义规则。例如:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_83.jpg?sign=1738887156-DfXLsJQ9T08mVIcb8ZrWxgG8UKWlWQvS-0-02a3bd03590daa6f0c2a39f743207c22)
变量的定义就这么简单。但是,如果不遵守变量定义规则,就会报错。比如:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_84.jpg?sign=1738887156-oZv4qdDUCub9FM8wHQf3XWqC2xy4P7p1-0-45c371808d20cd51a241fca1cfc8e0b9)
再看看如何进行变量叠加。例如:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_85.jpg?sign=1738887156-HIWWL1GrFKEpIZVEn5VZj1bTzu7cvGQG-0-eb2d6bf1df55195bf745dcf4ec7d7eda)
这里要小心,在进行变量叠加时,变量名需要用双引号或${}包含。
在定义变量时,也可以使用特殊字符,如双引号、单引号、反引号、小括号、大括号等。
2.变量的调用
当需要提取变量中的内容时,需要在变量名之前加入“$”符号。也就是说,当需要调用变量时,需要在变量名之前加入“$”符号。那么,最简单的变量调用就是通过echo 命令输出变量的值。命令如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_86.jpg?sign=1738887156-vETwjxNlHwPiIt4A33Kk9BUOjgTzBcvG-0-4ef2e12af68a41c5b75a1972db7a8229)
变量的调用就这么简单。不过,不仅当通过echo 命令输出变量的值时才需要在变量名之前加入“$”符号,只要需要调用变量,就要在变量名之前加入“$”符号。
3.变量的查看
可以通过echo 命令查看已经设定的变量的值,这种查看是已知变量名查看变量值。但是,如果不知道变量名,那么可以查看系统中已经存在的变量吗?当然可以,只需使用set 命令即可。set 命令可以用来查看系统中的所有变量(只能查看用户自定义变量和环境变量,而不能查看位置参数变量和预定义变量)和设定Shell 的执行环境。命令格式如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_87.jpg?sign=1738887156-27gbQngxirsF8a1qa9eDSYRMlO3xTdjb-0-b5d04e438fa09a8418ff3a506af68828)
举几个例子:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_88.jpg?sign=1738887156-MXLueDWQrGc1lSUtH1TtCBnKVxqKjwnf-0-9e4f00bf54a7f05e74b4ff6d402de643)
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_89.jpg?sign=1738887156-6lCfu7lpRFEU0feCep1824P4KqQGhx0T-0-37999a5bf9760355fbd466d3fbbe6d5a)
set 命令的选项和功能众多,不过更常用的还是使用set 命令查看变量。
4.变量的删除
要想删除用户自定义变量,可以使用unset 命令。命令格式如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_90.jpg?sign=1738887156-FpXn2JoKJfq88DXLECxd0ojvhNoMw8fb-0-c71a3f70cf3f7d3856bd96b9aa169310)
这里只是清空变量,而不是调用变量,所以不需要在变量名之前加入“$”符号。举一个例子:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_91.jpg?sign=1738887156-8LcxjOO9dpk5pdJFCVvhZqcFbXrER9CH-0-ec7cac70dd77c76451b3578cb91dfef4)
在执行这条命令之后,再查看变量,就会发现这个变量已经为空了。
2.4.3 环境变量
1.用户自定义环境变量
用户自定义环境变量和用户自定义变量最主要的区别在于:用户自定义环境变量是全局变量,而用户自定义变量是局部变量。
用户自定义变量只在当前Shell 中生效,而用户自定义环境变量会在当前Shell 和这个Shell的所有子Shell 中生效。如果把环境变量写入相应的配置文件中,那么这个环境变量就会在所有Shell 中生效(这是有区别的,如果不把环境变量写入相应的配置文件中,那么当前Shell 一旦中止,这个环境变量就会消失,而只有写入配置文件才会永久地、在所有Shell 中生效)。
1)用户自定义环境变量的设置
用户自定义环境变量和用户自定义变量的设置方法基本相同,只需通过export 命令将变量声明为环境变量即可。命令如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_92.jpg?sign=1738887156-C8nnmmBwdExY9yHRnLBawErehNSrI1Cd-0-1b523b70760468d0088dcdb72a871a22)
这样,年龄就是环境变量了。当然,也可以先把变量声明为本地变量,再使用export 命令声明为环境变量。命令如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_93.jpg?sign=1738887156-QMUjK6MaMjPM1qetX4TL3CTzN5OTakgn-0-977da34adda2ed6a3ccf63b1e8f465de)
这样,性别也被声明为环境变量了。我们说过,用户自定义变量和用户自定义环境变量的区别在于:用户自定义变量是局部变量,只在当前Shell 中生效;而用户自定义环境变量是全局变量,在当前Shell 和这个Shell 的所有子Shell 中生效。比如:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_94.jpg?sign=1738887156-sFqWJmOgRZPguYUSZ5pcd3d0Vyr3aNuW-0-fe066e0f70eed4314caeffce641f5af9)
然后查看一下这些变量。
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_95.jpg?sign=1738887156-32dXp3xyPf9TYAhk4dD8xzs6tZdEPW23-0-34e6be1dbfbd2f131d6abfbe1023910e)
在当前Shell 中可以看到这3个变量。
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_96.jpg?sign=1738887156-eCaALgjydSY1Xjk2VDycBmML5xWrA7J4-0-877fe9e7e81060082c63ab1aa79d8662)
可以看到,在子Shell 中只能看到用户自定义环境变量“age”和“gender”,这就是用户自定义环境变量和用户自定义变量的区别,也是把用户自定义变量称作本地变量的原因。
2)用户自定义环境变量的查看和删除
先来说说环境变量的查看。既然使用set 命令可以查看所有的变量(包括用户自定义变量、用户自定义环境变量和系统环境变量),当然也可以查看用户自定义环境变量,刚刚的实验就是使用set 命令进行用户自定义环境变量查看的。当然,也可以使用env 命令进行用户自定义环境变量的查看。命令如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_97.jpg?sign=1738887156-AxElgr268u1bu5VXSuJn6jQiJOjc3Qjv-0-530df486ba5f46c90fc87a284a8e3060)
env 命令和set 命令的区别在于:使用set 命令可以查看所有的变量(不能查看位置参数变量和预定义变量),而使用env 命令只能查看环境变量(两种环境变量都可以查看)。可以发现,在系统中默认有很多环境变量,这些环境变量都代表什么含义呢?稍后会有详细介绍。
再来说说环境变量的删除。其实,环境变量的删除方法和用户自定义变量的删除方法是一样的,都使用unset 命令。命令如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_98.jpg?sign=1738887156-EzFrYxGZDDtZlhe14QBzwaCvsP2ZPXz5-0-4fb88732f5f0aa592dcc85239c682f3e)
2.系统环境变量
在Linux 系统中,一般通过系统环境变量配置操作系统的环境,如提示符、查找命令的路径、用户的家目录等。这些系统默认的环境变量的变量名是固定的,变量的作用也是固定的,只能修改变量的值。如果手工修改了某个系统环境变量的变量名,那么这个变量就不会对系统环境起作用,从而变成一个用户自定义环境变量。所以,系统环境变量的变量名或作用是不能修改的。
在系统中默认有很多环境变量,下面来详细了解一下这些环境变量的含义。命令如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_99.jpg?sign=1738887156-o6bSL5KZj9NjoEh2340lz99AmazeJbAh-0-8f09bc0992b76c37e26e29b2485dbd02)
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_100.jpg?sign=1738887156-l1mMLwh7Z5rzA9po3Fs8zUjASEgvsdST-0-6a53685f851e9d7e14dfebc10977c27c)
使用env 命令可以查看到两种环境变量。还有一些变量虽然不是环境变量,但是是和Bash操作接口相关的变量,这些变量也对Bash 操作终端起到了重要的作用。这些变量就只能使用set 命令来查看了,这里只列出重要的内容,如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_101.jpg?sign=1738887156-n4JtSdp3n8QK3cwKo043pMi6uwRWFsoz-0-e935cd339fea4c5ba639beb5620ea839)
使用set 命令是可以查看所有变量的,当然也包括环境变量,所以在这里并没有列出刚刚在env 命令中介绍过的环境变量。其实,我们一般将这些和Bash 操作接口相关的变量也当作环境变量来对待,因为它们确实也是用来定义操作环境的。下面解释一些重要的系统环境变量。
1)PATH 变量:系统查找命令的路径
在2.2节中说过,程序脚本(命令也是二进制程序)要想在Linux 系统中运行,需要使用绝对路径或相对路径指定这个脚本所在的位置。但是,为什么系统命令都没有指定路径,而是直接执行的呢?比如,ls 命令并没有输入“/usr/bin/ls”来执行,而是直接执行的。这就是PATH变量的作用了。
先查看一下PATH 变量的值,命令如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_102.jpg?sign=1738887156-CGpgj1nVPvdjkpYkE39hXDXtYxLV9Ns0-0-f1d10973c20b0e2e2b2bbd8595e4298b)
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_103.jpg?sign=1738887156-aTHSmxNFk2ANyjrCWo0F77eHBf2ZdGSz-0-1ec9b46503bc5d5da9846b5a00291fb6)
PATH 变量的值是用“:”分隔的路径,这些路径就是系统查找命令的路径。也就是说,我们输入了一个程序名,如果没有输入路径,那么系统就会到PATH 变量定义的路径中去查找是否有可以执行的程序,如果找到则执行,否则会报“未找到命令”的错误。
那么,是不是把我们自己写的脚本复制到PATH 变量定义的路径中,也可以不输入路径而直接执行呢?当然是可以的,就拿最开始的hello.sh 脚本来举例吧。
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_104.jpg?sign=1738887156-R7T41IYu4wawrdAJQs4sHtXCegi6ZcKq-0-0fec3922f2ea408b4ecc6b3baf74b7c0)
只要把程序脚本复制到PATH 变量定义的任意路径中,如/usr/bin/目录中,以后这个程序脚本就可以直接执行了,不用再指定绝对路径或相对路径。
如果我们把自己写的所有程序脚本都放在/usr/bin/目录中,那么有时会搞不清系统命令和自己写的程序(其实笔者是很反对改变系统目录的结构的)。是不是可以修改PATH 变量的值,而不把程序脚本复制到/usr/bin/目录中?当然是可以的,通过变量叠加就可以实现。命令如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_105.jpg?sign=1738887156-jyG3djE9ZYMHXHH9rgxXiwj6XhTL1DPr-0-4209e4ad28e0d09200032b50c43f6a98)
当然,这样定义的PATH 变量只能临时生效,一旦重启或注销系统就会消失。如果想要永久生效,则需要将其写入环境变量配置文件中,在2.5节中再详细介绍。
2)PS1变量:命令提示符的定义
PS1是一个很有意思的变量,是用来定义命令提示符的,可以按照我们的需求来定义自己喜欢的命令提示符。PS1变量可以支持以下这些选项。
● \d:显示日期,格式为“星期 月 日”。
● \H:显示完整的主机名。如默认主机名“localhost.localdomain”。
● \h:显示简写的主机名。如默认主机名“localhost”。
● \t:显示24小时制时间,格式为“HH:MM:SS”。
● \T:显示12小时制时间,格式为“HH:MM:SS”。
● \A:显示24小时制时间,格式为“HH:MM”。
● \@:显示12小时制时间,格式为“HH:MM am/pm”。
● \u:显示当前用户名。
● \v:显示Bash 的版本信息。
● \w:显示当前所在目录的完整名称。
● \W:显示当前所在目录的最后一个目录。
● \#:显示执行了多少条命令。
● \$:提示符。如果是root 用户,则会显示提示符为“#”;如果是普通用户,则会显示提示符为“$”。
这些选项该怎么用呢?先看看PS1变量的默认值,命令如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_106.jpg?sign=1738887156-WvWsBE5Y4Z4D8PffdIwUXG5ye0xDlxOU-0-39794c86d2c14217fe97cd25d4f57008)
在PS1变量的值中,如果是可以解释的符号,如“\u”“\h”等,则显示这个符号的作用;如果是不能解释的符号,如“@”或“空格”,则原符号输出。修改一下PS1变量的值,看看会出现什么情况。命令如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_107.jpg?sign=1738887156-srqq1HfwuZFi98LKDs1KQRntETvFg7yF-0-ff337fbe18fa4b38985e0e7589bbf5bb)
这里要小心,PS1变量的值要用单引号包含,否则设置不生效。
再举一个例子:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_108.jpg?sign=1738887156-y9tjGRu4TvNqtKpeEDqQRhrzad7H8KWn-0-918f84cf6f9bfbd06b0e41ed865667dd)
PS1变量可以自由定制,好像看到了一点Linux 可以自由定制和修改的影子,还是很有意思的。不过说实话,已经习惯使用一个命令提示符,如果换一个还是非常别扭的,还是改回默认的命令提示符吧。命令如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_109.jpg?sign=1738887156-lK6kSGmBBLUUz6KWehgHKClFlpEuxOMg-0-e3303b375b5da8a0bd6d3eb6b335c0d1)
注意:这些命令提示符的修改同样是临时生效的,一旦注销或重启系统就会消失。要想永久生效,必须将其写入环境变量配置文件中。
3)LANG 语系变量
LANG 变量定义了Linux 系统的主语系,这个变量的默认值如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_110.jpg?sign=1738887156-uP3H4zPkw95JFnbccdB8YLrefbkYjuXh-0-1fe3fbd8f868998c04a0fd7e5bbfc40c)
这是因为在安装Linux 系统时选择的是中文安装,所以默认的主语系是“zh_CN.UTF-8”。那么,Linux 系统到底支持多少种语系呢?可以使用以下命令进行查看:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_111.jpg?sign=1738887156-SAFB6SQNfeUYpvxreGJ4EdARgFVKsgV7-0-78b4a313a34bb733c0f8ad84450d42d6)
既然Linux 系统支持这么多种语系,那么,当前系统使用的到底是什么语系呢?使用locale命令直接查看,命令如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_112.jpg?sign=1738887156-mKzIOhwGJ3M6NURYwUpyQshuGHXf5h0S-0-e491f6883076e915d2d45cb2c65a1848)
在Linux 系统中,语系主要是通过这些变量来设置的,在这里只需知道LANG 和LC_ALL变量即可,其他变量会依赖这两个变量的值而发生变化。LANG 是定义系统主语系的变量,LC_ALL 是定义整体语系的变量,一般使用LANG 变量来定义系统语系。
我们还要通过/etc/locale.conf文件(在CentOS 6.x中,默认语系配置文件是/etc/sysconfig/i18n)定义系统的默认语系,查看一下这个文件的内容,命令如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_113.jpg?sign=1738887156-dayBGX1dvyHxXE73EIWK45Plc2JbzsVs-0-82ff3bb88aeeb5c30e2f19b359c8851f)
又是当前系统语系,又是默认语系,感觉非常混乱。可以这样理解:默认语系是下次重启之后系统所使用的语系;而当前系统语系是当前系统所使用的语系。如果系统重启,则会从默认语系配置文件/etc/locale.conf 中读出语系,然后赋予变量LANG,让这个语系生效。也就是说,LANG 变量定义的语系只对当前系统生效;要想永久生效,就要修改/etc/locale.conf 文件。
说到这里,需要解释一下Linux 系统中文支持的问题。是不是只要定义语系为中文语系,如zh_CN.UTF-8,就可以正确显示中文了呢?要想正确显示中文,需要满足3个条件:
● 安装了中文编码和中文字体(因为在安装的时候要求大家采用中文安装,所以已经安装了中文编码和中文字体)。
● 将系统语系设置为中文语系。
● 操作终端必须支持中文显示。
我们的操作终端都支持中文显示吗?这要分情况。如果是图形界面,或者使用远程连接工具(如SecureCRT、Xshell 等),那么这些终端都是支持中文编码的,只要语系设置正确,中文显示就没有问题。举一个例子:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_114.jpg?sign=1738887156-lgA93ttvQiFPIRBL5aIYATNmbni9nnm7-0-7d55902aca19698bcad68280d087bc63)
如果是纯字符界面(本地终端tty1~tty6),则是不能显示中文的,因为Linux 系统的纯字符界面是不能显示中文这么复杂的编码的。虽然Linux 系统是采用中文安装的,但纯字符界面的语系却是“en_US. UTF-8”,如图2-2所示。
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_115.jpg?sign=1738887156-5So4j7T3PPgKbr1hxu1MSy4Q36q2rWfn-0-7aafd9a54b39b772496d4c3e77e801d9)
图2-2 纯字符界面的语系
强制更改语系为中文语系,看看会出现什么情况,如图2-3所示。
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_116.jpg?sign=1738887156-k60K7IBFyXAsZevBGHH9HQgfoOcZ5Ukt-0-d4577493a354a1ccc41b99441c479a07)
图2-3 在纯字符界面中设置中文语系
如果非要在纯字符界面中设置中文语系,就会出现乱码。这个问题能够解决吗?可以通过安装第三方zhcon 中文插件来让纯字符界面显示中文。zhcon 的安装并不复杂,查看一下安装说明应该可以轻松地安装。但是请大家注意,这并不是说纯字符界面支持中文显示,而是zhcon插件在起作用。
2.4.4 位置参数变量
在Linux 系统的命令行中,在执行一条命令或脚本时,后面可以跟多个参数,使用位置参数变量来表示这些参数。其中,$0代表命令行本身,$1代表第1个参数,$2代表第2个参数,依次类推。当参数个数超过10个(包括10个)时,就要用大括号把这个数字括起来,例如,${10}代表第10个参数,${14}代表第14个参数。举一个例子:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_117.jpg?sign=1738887156-NNFIMJsANKf6MkVzuzMVFdMmUII2CYXO-0-9387905cc9ae03cdd0b14a6d3c3a3454)
如果执行这样一条命令,则$0的值是ls 命令本身,$1的值是anaconda-ks.cfg 这个文件,$2的值是abc 文件,$3的值是bcd 文件。
在Shell 中可以识别的位置参数变量如表2-9所示。
表2-9 位置参数变量
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_118.jpg?sign=1738887156-rQDBUyPbovkqWxWNpQwcRxk3sPNxbntE-0-32d75d35e76d29bfac3425b387254d48)
位置参数变量主要用于向函数或脚本中传递信息。比如,想要写一个计算器,总要告诉程序应该运算哪个字符吧。如果直接在程序中固定变量的值,那么这个计算器不就只能计算固定的值吗?这样做没有意义。
这时就需要通过位置参数变量向脚本中传递数值,就可以计算不同的数值了。先写一个加法计算器,命令如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_119.jpg?sign=1738887156-vGiBBFJbEl4b3Qo17fBciCyBMjLmhXSX-0-86cb28a253ec0ea22757aad0d61fc16a)
在Shell 中,数值运算必须使用特殊格式,在2.4.7节中再详细介绍,这里就照着例子先执行。执行一下这个脚本:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_120.jpg?sign=1738887156-xEiEd3oq3HJMbA1smU7XhxXgnrpKr9WP-0-1ab8b11d4e91f00dee09aead0271b915)
使用位置参数变量的主要作用是向函数或脚本中传递信息。但是,这种写法只有程序的作者才能知道需要写几个参数,每个参数使用什么数据类型,而普通用户很难正确使用位置参数变量。我们推荐使用read 命令向函数或脚本中传递信息。
还有几个位置参数变量是干什么的呢?再写一个脚本来说明一下,如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_121.jpg?sign=1738887156-EItpHCSEw0BHYIwZkk2CMzHt2w3hzGV1-0-00613585aacb8d8d03e86bdc466c10a2)
执行一下这个脚本:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_122.jpg?sign=1738887156-6jEPEvfV3b77bUw5tecL4arHopbrdTGq-0-fc12c56f37bb815715db291eec14c661)
那么,“$*”和“$@”有区别吗?还是有区别的,$*会把接收到的所有参数当成一个整体,而$@则会区别对待接收到的所有参数。还是举一个例子吧:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_123.jpg?sign=1738887156-h4MBw4y6iYzeDcZeZA95gzASL7cIjLFq-0-3826cbd9e427ec812057bb30b405b0a8)
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_124.jpg?sign=1738887156-BhVsOqA2tSX151GdmalomkuWc0tlciMn-0-8355e343933e912e83bf1f43eae9b676)
在这个脚本中用到了for 循环,关于for 循环,在第3章中还会有详细介绍。执行一下这个脚本:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_125.jpg?sign=1738887156-0Xyztt7BR0y0yFLJft6CTChADalRy7pI-0-760882ddc1580fefc602c718ef7d6d09)
2.4.5 预定义变量
预定义变量是在Shell 一开始时就定义的变量,这一点和默认的环境变量有些类似。不同的是,预定义变量不能被重新定义,用户只能根据Shell 的定义来使用这些变量。其实,严格来说,位置参数变量也是预定义变量的一种,只是位置参数变量的作用比较统一,所以我们把位置参数变量单独划分为一类变量。
那么,预定义变量有哪些呢?通过表2-10来说明一下。
表2-10 预定义变量
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_126.jpg?sign=1738887156-EOoYwjVS8W3MzjjhKF8fRaII6vfkG9dS-0-9a75bf6c07a7377c1aa02f8de535c8d4)
先来看看“$?”这个变量,看起来不好理解,还是举一个例子吧,如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_127.jpg?sign=1738887156-zhT7tyi8lqVpALPI2sorspxNwbHl7Dca-0-7d6b194fbe0ea7e0657449169a92c19b)
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_128.jpg?sign=1738887156-MKQOPstZDPE2BqfnCgWADtxpqg0GbyR1-0-48bbecb7e390f2089229bcb37045d81b)
在这个例子中提到了进程号(PID)的概念,在第6章中会详细介绍。在这里可以理解为,在系统中每个进程都有一个ID,我们把这个ID 称作PID,系统是通过PID 来区分不同的进程的。
接下来说明一下“$$”和“$!”这两个预定义变量。我们写一个脚本,如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_129.jpg?sign=1738887156-kPsXUVqN8EXWXwXyOBQIJbHAapFcN5Ej-0-4d9996a3ad44ee6d82732bf48d45f111)
执行一下这个脚本:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_130.jpg?sign=1738887156-D5HuEeq2tTmQd4c2Im0jWCURlLiD5Ieu-0-a3ec85ba70fc80a55cab2b34fbd9cba7)
这里需要注意的是,不论是variable.sh 脚本,还是find 命令,一旦执行完毕就会停止,所以,使用ps 命令是查看不到这两个进程的PID 的。
在一般情况下,使用“$?”变量来判断上一条命令是否正确执行,后面要讲的test 测试命令也是通过“$?”变量来判断上一条命令是否正确执行的。如果使用“$$”变量来给临时文件命名,则可以保证临时文件名不会重复,这是“$$”变量的一种常见用法。
2.4.6 接收键盘输入
我们刚刚讲过的位置参数变量是可以把用户的输入用参数的方式输入脚本的,不过这种输入方式只有写这个脚本的人才能确定需要输入几个参数,每个参数应该输入什么类型的数据,并不适合普通用户使用。
除位置参数变量外,也可以使用read 命令向脚本中传入数据。read 命令接收标准输入设备(键盘)的输入,或者其他文件描述符的输入。在接收到输入后,read 命令将数据放入一个标准变量中。命令格式如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_131.jpg?sign=1738887156-WK8trOgyqdVOX1CjghbpSEezHmndpHCc-0-1eef0f4a689e9a8b93346df5f0611c96)
还是写一个脚本来解释一下read 命令,如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_132.jpg?sign=1738887156-wAlgpOVWUTv7bh0cqa9R0EIExUm5ycNQ-0-44b26235e27530922582569ac51c8c5e)
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_133.jpg?sign=1738887156-ZEFQglN794kAoXzYIJuQYEZSD7zgukOa-0-1aafa26c3b293aa37fc6f0abbe352f97)
执行一下这个脚本:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_134.jpg?sign=1738887156-CvXozgLL90MEOhT9SmDeC4S8ZkTOo18w-0-c46315855af4c0edc82d159204fe93ad)
read 命令并不难,却是接收键盘输入的重要方法,要熟练使用。
2.4.7 Shell 中的运算符
1.数值运算
Shell 和其他编程语言还是有很多不一样的地方的,其中笔者最不习惯的是:在Shell 中,所有变量的默认类型是字符串型。也就是说,如果不手工指定变量的类型,那么所有的数值都是不能进行运算的。比如:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_135.jpg?sign=1738887156-dxEpUsc7G3CZIC1bXe3sp9Ry2jTdQvqN-0-64fa60b60e44bb21922c9e73f1842313)
如果需要进行数值运算,则可以采用以下3种方法中的任意一种。
1)使用declare 命令声明变量的类型
既然所有变量的默认类型是字符串型,那么,只要把变量声明为整数型,不就可以参与运算了吗?使用declare 命令就可以声明变量的类型。命令格式如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_136.jpg?sign=1738887156-uByTGor2vSPuikY1f2vIDwm8JrIpbtLp-0-6cf978515e16b3694385414dc4274c40)
例子1:数值运算
只要把变量声明为整数型就可以参与运算了吗?试试吧:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_137.jpg?sign=1738887156-GgIpKkOICi6rYgy6M5LwGYyKQ8NkbK01-0-7bc79e777b567777efcf28d1acf4f50e)
这样运算好麻烦!没有办法,Shell 在数值运算方面确实是比较麻烦的,习惯就好了。
例子2:数组变量类型
只有在编写一些较为复杂的程序时才会用到数组,大家不用着急学习数组,当有需要的时候再回来详细学习。那么,数组是什么呢?所谓数组,就是相同数据类型的元素按一定顺序排列的集合。也就是把有限个类型相同的变量用一个名字命名,然后用编号区分它们的变量的集合,我们把这个名字称为数组名,把编号称为下标。组成数组的各个变量被称为数组的分量,又称数组的元素、下标变量。
一看定义就一头雾水,更加不明白数组是什么了。那么,换一种说法,变量和数组都是用来保存数据的,只是变量只能被赋予一个数据值,一旦重复赋值,后一个值就会覆盖前一个值;而数组可以被赋予一组相同类型的数据值。大家可以把变量想象成一间小办公室,在这间办公室里只能容纳一个人办公,办公室名就是变量名;而数组是一间大办公室,可以容纳很多人同时办公,在这间大办公室里办公的每个人是通过不同的座位号来区分的,这个座位号就是数组的下标,而大办公室的名字就是数组名。
还是举一个例子吧:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_138.jpg?sign=1738887156-gi7vdizkOCFnZr7m6ltmUMxymJKc6sKy-0-969af461773dc6fa1eddcf303bf12ddc)
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_139.jpg?sign=1738887156-esgBx4KZ6xW7xtlBLgaMjwyZTwtD1tHd-0-b0a76be53b86bd09220640a3c54680c6)
注意:数组的下标是从0开始的。在调用数组中的元素时,需要使用“${数组[下标]}”的形式来读取。
不过,在刚刚的例子中,并没有把name 变量声明为数组型。其实,只要在定义变量时采用了“变量名[下标]”的形式,这个变量就会被系统认为是数组型,不用强制声明。
例子3:环境变量
其实也可以使用declare 命令把变量声明为环境变量,这个命令的作用和export 命令的作用是一样的。其实,export 命令最终还是调用“declare -x”命令把变量声明为环境变量的。命令如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_140.jpg?sign=1738887156-G9RSbXQxPGUHwSRTmfvKLHUse5ZYlKSQ-0-1070965cc09442193bbc0faecd596867)
例子4:只读属性
一旦给变量设定了只读属性,那么,既不能修改变量的值,也不能删除变量,甚至不能使用“+r”选项取消只读属性。命令如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_141.jpg?sign=1738887156-rKHzgN8spEYTSdrR4WPfygmyke95qhqf-0-fdad4b4bb6f2b6e4477e964931f3b718)
还好这个变量只是在命令行声明的,所以,只要重新登录或重启系统,这个变量就会消失。
例子5:查看变量属性和取消变量属性
变量属性的查看使用“-p”选项,变量属性的取消使用“+”选项。命令如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_142.jpg?sign=1738887156-cbbw8e0kAQDFCXH8aicQOgELkjMFZGQR-0-758e769766c089096e6adf3d4a2f46b1)
2)使用expr 或let 数值运算工具
进行数值运算的第二种方法是使用expr 命令,这个命令就没有declare 命令那么复杂了。命令如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_143.jpg?sign=1738887156-XiIlP4SZ10bZyYfCWBjVst0KMOUK24hn-0-c4467e0b2fd0b4d739d5ec00e6c95a29)
在使用expr 命令进行数值运算时,要注意在“+”左右两侧必须有空格,否则数值运算不执行。
至于let 命令,和expr 命令基本类似,都是Linux 系统中的运算命令。命令如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_144.jpg?sign=1738887156-C8RE3LjRS0jLo5gpuB13GoG0y4ZuVIri-0-d500e976b3d3497794a20b2f3b9b8883)
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_145.jpg?sign=1738887156-0sItbPXojnwfwlo9RVnFsZ9AJc0UlzhU-0-fa3ba4443dc8fcabd6ae721e40b13968)
对于expr 和let 命令,大家可以按照习惯使用,不过let 命令对格式的要求比expr 命令对格式的要求宽松,所以推荐使用let 命令进行数值运算。
3)使用“$((运算式))”或“$[运算式]”方式
其实这是一种方式,“$(())”和“$[]”这两种括号按照个人习惯使用即可。命令如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_146.jpg?sign=1738887156-7xAS4DiYSd9PNgByDPsTBnq4igbrUXAD-0-4925449bf1824b3c6f641bd3033cf54d)
对于这3种数值运算方式,大家可以按照自己的习惯来选择使用。不过,笔者推荐使用“$((运算式))”,这种方式更加简单,也更加常用。
2.Shell 中常用的运算符
通过表2-11来说明一下Shell 中常用的运算符。
表2-11 Shell 中常用的运算符
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_147.jpg?sign=1738887156-TAUfYCkLO0avEPKW6QgMltzleDVYCanu-0-8989de3af226d7efbfb6eb4dac80027d)
运算符优先级表明在每个表达式或子表达式中哪个运算对象首先被运算,数值越大优先级越高,具有较高优先级的运算符先于具有较低优先级的运算符进行数值运算。
还是举几个例子来进行说明。
例子1:加减乘除
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_148.jpg?sign=1738887156-O8fOIuOHzD81BOypXajQBCvaKkAC65eV-0-bdb322cd72c3c2ac361ca12bb1ecf929)
例子2:取模运算
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_149.jpg?sign=1738887156-IJwlSYeFZZSurJ5x1AX6LV310oB12QbK-0-106e8d22d5a9e3fb82f27040d4463f4e)
例子3:逻辑与
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_150.jpg?sign=1738887156-O6gxD8V1Ecf8TkBln4W6HkBXKoQoI4ea-0-6f373b75a74c0ee411964cee0db82c72)
2.4.8 变量测试与内容置换
在脚本中,有时需要判断变量是否存在或是否被赋值。如果变量已经存在并且被赋值,则不改变变量;如果变量不存在或没有被赋值,则赋予其新值。这时就可以使用变量测试与内容置换。在脚本中,可以使用条件判断语句if 来代替这种测试方法,不过,使用Shell 自带的变量测试与内容置换方式更加方便,我们通过表2-12来进行说明。
表2-12 变量测试与内容置换
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_151.jpg?sign=1738887156-YffYuGJbAfRYQx3whSN19JgV7ng5TT3G-0-972e166e6f135b7eb1248d5532368135)
如果在大括号中没有“:”,则变量y 为空值或没有被设置,处理方法是不同的;如果在大括号中有“:”,则变量y 不论是为空值,还是没有被设置,处理方法是一样的。
如果在大括号中是“-”或“+”,则在改变变量x 的值的时候,变量y 的值是不改变的;如果在大括号中是“=”,则在改变变量x 的值的同时,变量y 的值也会改变。
如果在大括号中是“?”,则当变量y 不存在或为空值时,会把“新值”当成报错输出到屏幕上。
举几个例子来说明一下。
例子1:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_152.jpg?sign=1738887156-pYuD4FelfrBXabsqokhJJO6stSCmoBVB-0-9f2df596c577740aa569824cbc392e5b)
和表2-12对比一下,是不是可以看懂了?这是变量y 不存在的情况。如果变量y 为空值呢?
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_153.jpg?sign=1738887156-iFYRsSe7mtTFdVUDrmd3FPfpVoG5MUHR-0-024b264761f1aba3bb12dd3be2c40d72)
如果变量y 有值呢?
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_154.jpg?sign=1738887156-bRIktUdlWKIoLZYGROtpGNf13VsWKaju-0-f4ab76194ec50d03fa927244d09378aa)
例子2:
如果在大括号中是“=”,则又是什么情况呢?先测试一下变量y 没有被设置的情况,命令如下:
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_155.jpg?sign=1738887156-jtX4F4sUD8YF5hmhiUIxqXUquSxnGiFP-0-9e8e08ec16e5f55711bc96d9269c8768)
一旦使用了“=”,就会同时处理变量x 和y,而不像例子1那样只改变变量x 的值。
如果变量y 为空值,则又是什么情况呢?
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_156.jpg?sign=1738887156-lgpdNI2RkkUoAp2KZSD9cFRvQObHShZs-0-67591740ebfb5eea470d28fb6ce9463d)
一旦在大括号中使用“:”,那么变量y 为空值或没有被设置,处理方法是一样的。
如果变量y 已经被赋值,则又是什么情况呢?
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_157.jpg?sign=1738887156-lJVlmzsrLy69QCDv9qIN8dg0ede3ZjD3-0-682d76fd02ae5f0c30a32be5fc9bfff6)
例子3:
再测试一下在大括号中是“?”的情况。
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_158.jpg?sign=1738887156-8O19XvkWafACwfxPusBvdfgmVJ0RsOLZ-0-d38cda80c877cb4b4d4fd93412b5f5a2)
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_159.jpg?sign=1738887156-vjpnfCCLK3LuMVikoNIHRx0WT3Nf58Yi-0-d227a720ab57e4883eb96ade02454294)
如果变量y 已经被赋值呢?
![img](https://epubservercos.yuewen.com/3BAF4C/18685353501446106/epubprivate/OEBPS/Images/txt002_160.jpg?sign=1738887156-pEyrXAg1SFeDhp90CyPCvOl6me8rXcvs-0-bdc90ff96449ce8d700a89c4dc85c0f8)
这些内容实在让人头疼,如果在脚本中用到了,则参考表2-12即可。