Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本

一、输入输出重定向

输入重定向是指把文件导入到命令中,而输出重定向则是指把原本要输出到屏幕的数据信息写入到指定文件中。在日常的学习和工作中,相较于输入重定向,我们使用输出重定向的频率更高,所以又将输出重定向分为了标准输出重定向错误输出重定向两种不同的技术,以及清空写入追加写入两种模式。

Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本_第1张图片

[root@localhost ~]# ifconfig > text.txt
[root@localhost ~]# cat text.txt
eno16777728: flags=4163  mtu 1500
        inet 192.168.21.112  netmask 255.255.255.0  broadcast 192.168.21.255
        inet6 fe80::20c:29ff:feff:1e9d  prefixlen 64  scopeid 0x20
        ether 00:0c:29:ff:1e:9d  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 384  bytes 25361 (24.7 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10
        loop  txqueuelen 0  (Local Loopback)
        RX packets 245  bytes 24198 (23.6 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 245  bytes 24198 (23.6 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@localhost ~]# 

Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本_第2张图片

如果报错的话,就要用报错信息输出:

Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本_第3张图片

那么我们怎么知道一个信息是正确还是错误那?可以进行如下操作:

[root@localhost ~]# ls ana.cfg &>test(该语句表示无论是正确还是错误的,都将她写入到文件中)
[root@localhost ~]# wc -l anaconda-ks.cfg (输出该文件的所有行)
47 anaconda-ks.cfg
[root@localhost ~]# wc -l < anaconda-ks.cfg 
47
[root@localhost ~]# 

二、管道命令符(shift+enter上面的键)

其执行格式为“命令A | 命令B”。管道命令符的作用也可以用一句话来概括“把前一个命令原本要输出到屏幕的标准正常数据当作是后一个命令的标准输入”。

比如,在发送电子邮件时,默认采用交互式的方式来进行,我们完全可以利用一条结合了管道符的命令语句,把编辑好的内容与标题一起“打包”,最终用这一条命令实现邮件的发送。

[root@localhost ~]# echo "hahahaha" | mail -s "hohohoho" root
[root@localhost ~]# mail
Heirloom Mail version 12.5 7/5/10.  Type ? for help.
"/var/mail/root": 1 message 1 new
>N  1 root                  Tue Mar 27 09:09  18/611   "hohohoho"
& ^CInterrupt
& 
Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本_第4张图片
三、 命令行的通配符
作为Linux运维人员,我们有时候也会遇到明明一个文件的名称就在嘴边但就是想不起来的情况。如果就记得一个文件的开头几个字母,想遍历查找出所有以这个关键词开头的文件,该怎么操作呢?又比如,假设想要批量查看所有硬盘文件的相关权限属性,一种方式是这样的:
[root@localhost ~]# ls -l /dev/sda
brw-rw----. 1 root disk 8, 0 Mar 27  2018 /dev/sda
[root@localhost ~]# ls -l /dev/sda1
brw-rw----. 1 root disk 8, 1 Mar 27  2018 /dev/sda1
[root@localhost ~]# ls -l /dev/sda2
brw-rw----. 1 root disk 8, 2 Mar 27  2018 /dev/sda2
[root@localhost ~]# ls -l /dev/sda3
ls: cannot access /dev/sda3: No such file or directory
[root@localhost ~]# 

幸亏我的硬盘文件和分区只有3个,要是有几百个,估计需要花费一天的时间来忙这个事情了。由此可见,这种方式的效率确实很低。这些硬盘设备文件都是以sda开头并且存放到了/dev目录中,这样一来,即使我们不知道硬盘的分区编号和具体分区的个数,也可以使用通配符来搞定。星号(*)代表匹配零个或多个字符,问号(?)代表匹配单个字符(字母或者是数字),中括号内加上数字[0-9]代表匹配0~9之间的单个数字的字符,而中括号内加上字母[abc]则是代表匹配a、b、c三个字符中的任意一个字符。

下面我们就来匹配所有在/dev目录中且以sda开头的文件:

Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本_第5张图片

由上图可以看出来?不能匹配空值;

[root@localhost ~]# ls -l /dev/sda[1,3,5,7,9]
brw-rw----. 1 root disk 8, 1 Mar 27  2018 /dev/sda1
[root@localhost ~]# ls -l /dev/sda{1,3,5,7,9}
ls: cannot access /dev/sda3: No such file or directory
ls: cannot access /dev/sda5: No such file or directory
ls: cannot access /dev/sda7: No such file or directory
ls: cannot access /dev/sda9: No such file or directory
brw-rw----. 1 root disk 8, 1 Mar 27  2018 /dev/sda1

{}会报错但是[]不会报错;

使用[0-9]来匹配0~9之间的单个数字:

[root@localhost ~]# ls -l /dev/sda[0-9]
brw-rw----. 1 root disk 8, 1 Mar 27  2018 /dev/sda1
brw-rw----. 1 root disk 8, 2 Mar 27  2018 /dev/sda2
四、 常用的转义字符

为了能够更好地理解用户的表达,Shell解释器还提供了特别丰富的转义字符来处理输入的特殊数据。

4个最常用的转义字符如下所示。

反斜杠(\):使反斜杠后面的一个变量变为单纯的字符串。

单引号(''):转义其中所有的变量为单纯的字符串。

双引号(""):保留其中的变量属性,不进行转义处理。

反引号(``):把其中的命令执行后返回结果。

echo就是用来提取出来$后面变量的值:
[root@localhost ~]# PRICE=5
[root@localhost ~]# echo $PRICE
5

Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本_第6张图片

然后使用echo命令来查看本机的Linux版本和内核信息:


下面的图可以看出,两个颜色的不同,说明命令已经经过别名文件处理了:

Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本_第7张图片

取消别名处理:

Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本_第8张图片

Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本_第9张图片

五、重要的环境变量

在Linux系统中,变量名称一般都是大写的,这是一种约定俗成的规范。我们可以直接通过变量名称来提取到对应的变量值。Linux系统中的环境变量是用来定义系统运行环境的一些参数,比如每个用户不同的家目录、邮件存放位置等。

[root@localhost ~]# echo $PATH(变量必须大写)
/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin
[root@localhost ~]# echo $path

[root@localhost ~]# 

简单来说,命令在Linux中的执行分为4个步骤。

第1步:判断用户是否以绝对路径或相对路径的方式输入命令(如/bin/ls),如果是的话则直接执行。

第2步Linux系统检查用户输入的命令是否为“别名命令”,即用一个自定义的命令名称来替换原本的命令名称。可以用alias命令来创建一个属于自己的命令别名,格式为“alias 别名=命令”。若要取消一个命令别名,则是用unalias命令,格式为“unalias 别名”。我们之前在使用rm命令删除文件时,Linux系统都会要求我们再确认是否执行删除操作,其实这就是Linux系统为了防止用户误删除文件而特意设置的rm别名命令,接下来我们把它取消掉:

[root@linuxprobe ~]# ls
anaconda-ks.cfg Documents initial-setup-ks.cfg Pictures Templates
Desktop Downloads Music Public Videos
[root@linuxprobe ~]# rm anaconda-ks.cfg 
rm: remove regular file ‘anaconda-ks.cfg’? y
[root@linuxprobe~]# alias rm
alias rm='rm -i'
[root@linuxprobe ~]# unalias rm
[root@linuxprobe ~]# rm initial-setup-ks.cfg 
[root@linuxprobe ~]#

第3步Bash解释器判断用户输入的是内部命令还是外部命令。内部命令是解释器内部的指令,会被直接执行;而用户在绝大部分时间输入的是外部命令,这些命令交由步骤4继续处理。可以使用“type命令名称”来判断用户输入的命令是内部命令还是外部命令。

第4步系统在多个路径中查找用户输入的命令文件,而定义这些路径的变量叫作PATH,可以简单地把它理解成是“解释器的小助手”,作用是告诉Bash解释器待执行的命令可能存放的位置,然后Bash解释器就会乖乖地在这些位置中逐个查找。PATH是由多个路径值组成的变量,每个路径值之间用冒号间隔,对这些路径的增加和删除操作将影响到Bash解释器对Linux命令的查找。

Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本_第10张图片

例如,我们使用下述命令来查看HOME变量在不同用户身份下都有哪些值(su是用于切换用户身份的命令):

[root@linuxprobe ~]# echo $HOME
/root
[root@linuxprobe ~]# su - linuxprobe
Last login: Fri Feb 27 19:49:57 CST 2017 on pts/0
[linuxprobe@linuxprobe ~]$ echo $HOME
/home/linuxprobe

Linux系统中最重要的10个环境变量

变量名称 作用
HOME 用户的主目录(即家目录)
SHELL 用户在使用的Shell解释器名称
HISTSIZE 输出的历史命令记录条数
HISTFILESIZE 保存的历史命令记录条数
MAIL 邮件保存路径
LANG 系统语言、语系名称
RANDOM 生成一个随机数字
PS1 Bash解释器的提示符
PATH 定义解释器搜索用户执行命令的路径
EDITOR 用户默认的文本编辑器

本章节的复习作业

1.把ls命令的正常输出信息追加写入到error.txt文件中的命令是什么?

答:ls >> error.txt

2.请简单概述管道符的作用。

答:把左面(前面)命令的输出值作为右面(后面)命令的输入值以便进一步处理信息。

3.Bash解释器的通配符中,星号(*)代表几个字符?

答:零个或多个。

4.PATH变量的作用是什么?

答:设定解释器搜索所执行的命令的路径。

5.使用什么命令可以把名为LINUX的一般变量转换成全局变量?

答:export LINUX。

六、Vim文本编辑器

(1)Vim文本编辑器:

“在Linux系统中一切都是文件,而配置一个服务就是在修改其配置文件的参数”。而且在日常工作中大家也肯定免不了要编写文档,这些工作都是通过文本编辑器来完成的。

要想高效率地操作文本,就必须先搞清这三种模式的操作区别以及模式之间的切换方法(见下图)。

命令模式:控制光标移动,可对文本进行复制、粘贴、删除和查找等工作。

输入模式:正常的文本录入。

末行模式:保存或退出文档,以及设置编辑环境。

Vim中常用的命令

命令 作用
dd 删除(剪切)光标所在整行
5dd 删除(剪切)从光标处开始的5行
yy 复制光标所在整行
5yy 复制从光标处开始的5行
n 显示搜索命令定位到的下一个字符串
N 显示搜索命令定位到的上一个字符串
u 撤销上一步的操作
p 将之前删除(dd)或复制(yy)过的数据粘贴到光标后面

下面做一个简单的文本演示:

编写脚本文档的第1步就是给文档取个名字,这里将其命名为practice.txt。如果存在该文档,则是打开它。如果不存在,则是创建一个临时的输入文件,可以分别使用a、i、o三个键从命令模式切换到输入模式。其中,a键与i键分别是在光标后面一位和光标当前位置切换到输入模式,而o键则是在光标的下面再创建一个空行,此时可敲击a键进入到编辑器的输入模式,进入输入模式后,可以随意输入文本内容,Vim编辑器不会把您输入的文本内容当作命令而执行

Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本_第11张图片

在编写完之后,想要保存并退出,必须先敲击键盘Esc键从输入模式返回命令模式,当在末行模式中输入:wq!命令时,就意味着强制保存并退出文档。然后便可以用cat命令查看保存后的文档内容了:


继续编辑这个文档。因为要在原有文本内容的下面追加内容,所以在命令模式中敲击o键进入输入模式更会高效,之后查看文本的内容,果然发现追加输入的内容并没有被保存下来:

[root@localhost ~]# cat practice.txt

you can write in it
this sentence don't need to save
[root@localhost ~]# 

(2)配置主机名称:

为了便于在局域网中查找某台特定的主机,或者对主机进行区分,除了要有IP地址外,还要为主机配置一个主机名,主机之间可以通过这个类似于域名的名称来相互访问。在Linux系统中,主机名大多保存在/etc/hostname文件中,接下来将/etc/hostname文件的内容修改为“linuxprobe.com”:

[root@localhost ~]# vim /etc/hostname
[root@localhost ~]# hostname
linuxprobe.com
(3) 配置网卡信息:

在RHEL 5、RHEL 6中,网卡配置文件的前缀为eth,第1块网卡为eth0,第2块网卡为eth1;以此类推。而在RHEL 7中,网卡配置文件的前缀则以ifcfg开始,加上网卡名称共同组成了网卡配置文件的名字,例如ifcfg-eno16777736

进入到网卡配置文件所在的目录,然后编辑网卡配置文件,在其中填入下面的信息:

[root@linuxprobe ~]# cd /etc/sysconfig/network-scripts/
[root@linuxprobe network-scripts]# vim ifcfg-eno16777736
TYPE=Ethernet
BOOTPROTO=none
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no
NAME=eno16777736
UUID=13756690-ac77-b776-4fc1-f5535cee6f16
ONBOOT=yes
IPADDR0=192.168.21.112
HWADDR=00:0c:29:cd:17:3d
PREFIX0=24
IPV6_PEERDNS=yes
IPV6_PEERROUTES=yes

Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本_第12张图片

nmcli con可以查看网卡的UUID信息,另一种查看UUID的方法移步:点击打开链接

Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本_第13张图片

执行重启网卡设备的命令(在正常情况下不会有提示信息),然后通过ping命令测试网络能否联通。由于在Linux系统中ping命令不会自动终止,因此需要手动按下Ctrl-c键来强行结束进程。

[root@linuxprobe network-scripts]# systemctl restart network
[root@linuxprobe network-scripts]# ping 192.168.21.112
PING 192.168.10.10 (192.168.10.10) 56(84) bytes of data.
64 bytes from 192.168.10.10: icmp_seq=1 ttl=64 time=0.081 ms
64 bytes from 192.168.10.10: icmp_seq=2 ttl=64 time=0.083 ms
64 bytes from 192.168.10.10: icmp_seq=3 ttl=64 time=0.059 ms
64 bytes from 192.168.10.10: icmp_seq=4 ttl=64 time=0.097 ms
^C
--- 192.168.10.10 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 2999ms
rtt min/avg/max/mdev = 0.059/0.080/0.097/0.013 ms

(3)编写Shell脚本

例如,如果想查看当前所在工作路径并列出当前目录下所有的文件及属性信息,实现这个功能的脚本应该类似于下面这样:

[root@linuxprobe ~]# vim example.sh
#!/bin/bash 
#For Example BY linuxprobe.com 
pwd 
ls -al

Shell脚本文件的名称可以任意,但为了避免被误以为是普通文件,建议将.sh后缀加上,以表示是一个脚本文件。

第一行的脚本声明(#!)用来告诉系统使用哪种Shell解释器来执行该脚本;

第二行的注释信息(#)是对脚本功能和某些命令的介绍信息,使得自己或他人在日后看到这个脚本内容时,可以快速知道该脚本的作用或一些警告信息;

第三、四行的可执行语句也就是我们平时执行的Linux命令了。

那我们来执行一下看看结果:

[root@linuxprobe ~]# bash example.sh
/root/Desktop
total 8
drwxr-xr-x. 2 root root 23 Jul 23 17:31 .
dr-xr-x---. 14 root root 4096 Jul 23 17:31 ..
-rwxr--r--. 1 root root 55 Jul 23 17:31 example.sh

除了上面用bash解释器命令直接运行Shell脚本文件外,第二种运行脚本程序的方法是通过输入完整路径的方式来执行。

[root@linuxprobe ~]# ./example.sh
bash: ./Example.sh: Permission denied
[root@linuxprobe ~]# chmod u+x example.sh
[root@linuxprobe ~]# ./example.sh
/root/Desktop
total 8
drwxr-xr-x. 2 root root 23 Jul 23 17:31 .
dr-xr-x---. 14 root root 4096 Jul 23 17:31 ..
-rwxr--r--. 1 root root 55 Jul 23 17:31 example.sh

(4)接收用户参数

  Linux系统中的Shell脚本语言早就考虑到了这些,已经内设了用于接收参数的变量,变量之间可以使用空格间隔。例如$0对应的是当前Shell脚本程序的名称$#对应的是总共有几个参数$*对应的是所有位置的参数值,$?对应的是显示上一次命令的执行返回值,而$1、$2、$3……则分别对应着第N个位置的参数值,如图

Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本_第14张图片

[root@linuxprobe ~]# vim example.sh
#!/bin/bash
echo "当前脚本名称为$0"
echo "总共有$#个参数,分别是$*。"
echo "第1个参数为$1,第5个为$5。"
[root@linuxprobe ~]# sh example.sh one two three four five six
当前脚本名称为example.sh
总共有6个参数,分别是one two three four five six。
第1个参数为one,第5个为five。

Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本_第15张图片

(5)判断用户的参数

文件测试所用的参数

操作符 作用
-d 测试文件是否为目录类型
-e 测试文件是否存在
-f 判断是否为一般文件
-r 测试当前用户是否有权限读取
-w 测试当前用户是否有权限写入
-x 测试当前用户是否有权限执行


从上图可知进入到haha的文件是不成功的,因为在系统中不存在这个文件,那么我们如何在进入文件之前进行判断这个目录是否存在呢?现在举一个栗子:

下面使用文件测试语句来判断/etc/fstab是否为一个目录类型的文件,然后通过Shell解释器的内设$?变量显示上一条命令执行后的返回值。如果返回值为0,则目录存在;如果返回值为非零的值,则意味着目录不存在:

[root@linuxprobe ~]# [ -d /etc/fstab ]
[root@linuxprobe ~]# echo $?
1

再使用文件测试语句来判断/etc/fstab是否为一般文件,如果返回值为0,则代表文件存在,且为一般文件

[root@linuxprobe ~]# [ -f /etc/fstab ]
[root@linuxprobe ~]# echo $?
0

Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本_第16张图片

例如在Shell终端中逻辑“与”的运算符号是&&,它表示当前面的命令执行成功后才会执行它后面的命令,因此可以用来判断/dev/cdrom文件是否存在,若存在则输出Exist字样。

[root@linuxprobe ~]# [ -e /dev/cdrom ] && echo "Exist"
Exist

除了逻辑“与”外,还有逻辑“或”,它在Linux系统中的运算符号为||,表示当前面的命令执行失败后才会执行它后面的命令,因此可以用来结合系统环境变量USER来判断当前登录的用户是否为非管理员身份:

[root@linuxprobe ~]# echo $USER
root
[root@linuxprobe ~]# [ $USER = root ] || echo "user"
[root@linuxprobe ~]# su - linuxprobe 
[linuxprobe@linuxprobe ~]$ [ $USER = root ] || echo "user"
user

第三种逻辑语句是“非”,在Linux系统中的运算符号是一个叹号(!),它表示把条件测试中的判断结果取相反值。也就是说,如果原本测试的结果是正确的,则将其变成错误的;原本测试错误的结果则将其变成正确的。

我们现在切换回到root管理员身份,再判断当前用户是否为一个非管理员的用户。由于判断结果因为两次否定而变成正确,因此会正常地输出预设信息:

[linuxprobe@linuxprobe ~]$ exit
logout
[root@linuxprobe root]# [ $USER != root ] || echo "administrator"
administrator

整数比较运算符仅是对数字的操作,不能将数字与字符串、文件等内容一起操作,而且不能想当然地使用日常生活中的等号、大于号、小于号等来判断。

 可用的整数比较运算符

操作符 作用
-eq 是否等于
-ne 是否不等于
-gt 是否大于
-lt 是否小于
-le 是否等于或小于
-ge 是否大于或等于

我们先测试一下10是否大于10以及10是否等于10(通过输出的返回值内容来判断):

[root@linuxprobe ~]# [ 10 -gt 10 ]
[root@linuxprobe ~]# echo $?
1
[root@linuxprobe ~]# [ 10 -eq 10 ]
[root@linuxprobe ~]# echo $?
0

Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本_第17张图片

下来先使用free -m命令查看内存使用量情况(单位为MB),然后通过grep Mem:命令过滤出剩余内存量的行,再用awk '{print $4}'命令只保留第四列,最后用FreeMem=`语句`的方式把语句内执行的结果赋值给变量。

这个演示确实有些难度,但看懂后会觉得很有意思,没准在运维工作中也会用得上。

[root@linuxprobe ~]# free -m
            total     used     free     shared     buffers     cached
Mem:        1826      1244     582      9          1           413
-/+ buffers/cache:    830 996
Swap:       2047      0        2047
[root@linuxprobe ~]# free -m | grep Mem:
Mem:        1826      1244     582      9 
[root@linuxprobe ~]# free -m | grep Mem: | awk '{print $4}'
582
[root@linuxprobe ~]# FreeMem=`free -m | grep Mem: | awk '{print $4}'`
[root@linuxprobe ~]# echo $FreeMem 
582

上面用于获取内存可用量的命令以及步骤可能有些“超纲”了,如果不能理解领会也不用担心,接下来才是重点。我们使用整数运算符来判断内存可用量的值是否小于1024,若小于则会提示“Insufficient Memory”(内存不足)的字样:

[root@linuxprobe ~]# [ $FreeMem -lt 1024 ] && echo "Insufficient Memory"
Insufficient Memory

(6)流程控制语句

          if条件测试语句

if条件语句的单分支结构由if、then、fi关键词组成,而且只在条件成立后才执行预设的命令,相当于口语的“如果……那么……”。

Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本_第18张图片

图 单分支的if语句

下面使用单分支的if条件语句来判断/media/cdrom文件是否存在,若存在就结束条件判断和整个Shell脚本,反之则去创建这个目录:

[root@linuxprobe ~]# vim mkcdrom.sh
#!/bin/bash
DIR="/media/cdrom"
if [ ! -e $DIR ]
then
mkdir -p $DIR
fi

Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本_第19张图片

因此这里继续用“bash 脚本名称”的方式来执行脚本。在正常情况下,顺利执行完脚本文件后没有任何输出信息,但是可以使用ls命令验证/media/cdrom目录是否已经成功创建:

[root@linuxprobe ~]# bash mkcdrom.sh
[root@linuxprobe ~]# ls -d /media/cdrom
/media/cdrom

if条件语句的双分支结构由if、then、else、fi关键词组成,它进行一次条件匹配判断,如果与条件匹配,则去执行相应的预设命令;反之则去执行不匹配时的预设命令,相当于口语的“如果……那么……或者……那么……”。

第4章 Vim编辑器与Shell命令脚本。第4章 Vim编辑器与Shell命令脚本。

图双分支的if条件语句

因此为了避免用户等待时间过长,需要通过-c参数来规定尝试的次数,并使用-i参数定义每个数据包的发送间隔,以及使用-W参数定义等待超时时间:

[root@linuxprobe ~]# vim chkhost.sh
#!/bin/bash
ping -c 3 -i 0.2 -W 3 $1 &> /dev/null(黑洞文件用来存储输出到屏幕上的文本信息)
if [ $? -eq 0 ]
then
echo "Host $1 is On-line."
else
echo "Host $1 is Off-line."
fi
因此可以使用整数比较运算符来判断$?变量是否为0,从而获知那条语句的最终判断情况。这里的服务器IP地址为192.168.21.112,我们来验证一下脚本的效果:
[root@localhost ~]# bash chkhost.sh 192.168.10.10
Host 192.168.10.10 is Off-line.
[root@localhost ~]# bash chkhost.sh 192.168.21.112
Host 192.168.21.112 is On-line.
[root@localhost ~]# 
当主机在线的时候,再去传文件,如果主机不在线的话,就不能传送文件;

if条件语句的多分支结构由if、then、else、elif、fi关键词组成,它进行多次条件匹配判断,这多次判断中的任何一项在匹配成功后都会执行相应的预设命令,相当于口语的“如果……那么……如果……那么……”:

Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本_第20张图片

图 多分支的if条件语句

下面使用多分支的if条件语句来判断用户输入的分数在哪个成绩区间内,然后输出如Excellent、Pass、Fail等提示信息。在Linux系统中,read是用来读取用户输入信息的命令,能够把接收到的用户输入信息赋值给后面的指定变量,-p参数用于向用户显示一定的提示信息。在下面的脚本示例中,只有当用户输入的分数大于等于85分且小于等于100分,才输出Excellent字样;若分数不满足该条件(即匹配不成功),则继续判断分数是否大于等于70分且小于等于84分,如果是,则输出Pass字样;若两次都落空(即两次的匹配操作都失败了),则输出Fail字样:

[root@linuxprobe ~]# vim chkscore.sh
#!/bin/bash
read -p "Enter your score(0-100):" GRADE
if [ $GRADE -ge 85 ] && [ $GRADE -le 100 ] ; then
echo "$GRADE is Excellent"
elif [ $GRADE -ge 70 ] && [ $GRADE -le 84 ] ; then
echo "$GRADE is Pass"
else
echo "$GRADE is Fail" 
fi
[root@linuxprobe ~]# bash chkscore.sh
Enter your score(0-100):88
88 is Excellent
[root@linuxprobe ~]# bash chkscore.sh 
Enter your score(0-100):80
80 is Pass
 for条件循环语句

for循环语句允许脚本一次性读取多个信息,然后逐一对信息进行操作处理,当要处理的数据有范围时,使用for循环语句再适合不过了。

第4章 Vim编辑器与Shell命令脚本。第4章 Vim编辑器与Shell命令脚本。

图 for循环语句的语法格式

下面使用for循环语句从列表文件中读取多个用户名,然后为其逐一创建用户账户并设置密码。首先创建用户名称的列表文件users.txt,每个用户名称单独一行。我们可以自行决定具体的用户名称和个数:

[root@linuxprobe ~]# vim users.txt
andy
barry
carl
duke
eric
george
执行批量创建用户的Shell脚本Example.sh,在输入为账户设定的密码后将由脚本自动检查并创建这些账户接下来编写Shell脚本Example.sh。
/dev/null是一个被称作Linux黑洞的文件,把输出信息重定向到这个文件等同于删除数据(类似于没有回收功能的垃圾箱),可以让用户的屏幕窗口保持简洁。

[root@linuxprobe ~]# vim Example.sh
#!/bin/bash
read -p "Enter The Users Password : " PASSWD
for UNAME in `cat users.txt`
do
id $UNAME &> /dev/null
if [ $? -eq 0 ]
then
echo "Already exists"
else
useradd $UNAME &> /dev/null
echo "$PASSWD" | passwd --stdin $UNAME &> /dev/null
if [ $? -eq 0 ]
then
echo "$UNAME , Create success"
else
echo "$UNAME , Create failure"
fi
fi
done

在Linux系统中,/etc/passwd是用来保存用户账户信息的文件。如果想确认这个脚本是否成功创建了用户账户,可以打开这个文件,看其中是否有这些新创建的用户信息。

Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本_第21张图片
[root@localhost ~]# tail -6 /etc/passwd
andy:x:1001:1001::/home/andy:/bin/bash
barry:x:1002:1002::/home/barry:/bin/bash
carl:x:1003:1003::/home/carl:/bin/bash
duke:x:1004:1004::/home/duke:/bin/bash
eric:x:1005:1005::/home/eric:/bin/bash
george:x:1006:1006::/home/george:/bin/bash

增加用户:useradd xxx
删除用户:userdel xxx
切换root:su root 回车输入密码
普通用户使用root命令:su - root 回车输入密码
su root 和su - root的底层环境是不一样的

如尝试让脚本从文本中自动读取主机列表,然后自动逐个测试这些主机是否在线。

首先创建一个主机列表文件ipadds.txt:

[root@linuxprobe ~]# vim ipadds.txt
192.168.10.10
192.168.10.11
192.168.21.112

然后前面的双分支if条件语句与for循环语句相结合,让脚本从主机列表文件ipadds.txt中自动读取IP地址(用来表示主机)并将其赋值给HLIST变量,从而通过判断ping命令执行后的返回值来逐个测试主机是否在线。脚本中出现的$(命令)是一种完全类似于第3章的转义字符中反引号`命令`的Shell操作符,效果同样是执行括号或双引号括起来的字符串中的命令。

[root@linuxprobe ~]# vim CheckHosts.sh
#!/bin/bash
HLIST=$(cat ~/ipadds.txt)
for IP in $HLIST
do
ping -c 3 -i 0.2 -W 3 $IP &> /dev/null
if [ $? -eq 0 ] ; then
echo "Host $IP is On-line."
else
echo "Host $IP is Off-line."
fi
done
[root@linuxprobe ~]# ./CheckHosts.sh
Host 192.168.10.10 is On-line.
Host 192.168.10.11 is Off-line.
Host 192.168.21.112 is Off-line.

while条件循环语句

while循环语句通过判断条件测试的真假来决定是否继续执行命令,若条件为真就继续执行,为假就结束循环。

第4章 Vim编辑器与Shell命令脚本。第4章 Vim编辑器与Shell命令脚本。

图 while循环语句的语法格式

接下来结合使用多分支的if条件测试语句与while条件循环语句,编写一个用来猜测数值大小的脚本Guess.sh。该脚本使用$RANDOM变量来调取出一个随机的数值(范围为0~32767),将这个随机数对1000进行取余操作,并使用expr命令取得其结果,再用这个数值与用户通过read命令输入的数值进行比较判断。这个判断语句分为三种情况,分别是判断用户输入的数值是等于、大于还是小于使用expr命令取得的数值我们当前要关注的是while条件循环语句中的条件测试始终为true,因此判断语句会无限执行下去,直到用户输入的数值等于expr命令取得的数值后,这两者相等之后才运行exit 0命令,终止脚本的执行。

[root@linuxprobe ~]# vim Guess.sh
#!/bin/bash
PRICE=$(expr $RANDOM % 1000)
TIMES=0
echo "商品实际价格为0-999之间,猜猜看是多少?"
while true
do
read -p "请输入您猜测的价格数目:" INT
let TIMES++
if [ $INT -eq $PRICE ] ; then
echo "恭喜您答对了,实际价格是 $PRICE"
echo "您总共猜测了 $TIMES 次"
exit 0
elif [ $INT -gt $PRICE ] ; then
echo "太高了!"
else
echo "太低了!"
fi
done

在这个Guess.sh脚本中,我们添加了一些交互式的信息,从而使得用户与系统的互动性得以增强。而且每当循环到let TIMES++命令时都会让TIMES变量内的数值加1,用来统计循环总计执行了多少次。这可以让用户得知总共猜测了多少次之后,才猜对价格。

[root@localhost ~]# bash Guess.sh
商品实际价格为0-999之间,猜猜看是多少?
请输入您猜测的价格数目:500
太高了!
请输入您猜测的价格数目:400
太低了!
请输入您猜测的价格数目:450
太高了!
请输入您猜测的价格数目:410
太高了!
请输入您猜测的价格数目:401
恭喜您答对了,实际价格是 401
您总共猜测了 5 次

case条件测试语句

case条件测试语句和switch语句的功能非常相似!case语句是在多个范围内匹配数据,若匹配成功则执行相关命令并结束整个条件测试;而如果数据不在所列出的范围内,则会去执行星号(*)中所定义的默认命令。

Guess.sh脚本中有一个致命的弱点—只能接受数字!您可以尝试输入一个字母,会发现脚本立即就崩溃了。原因是字母无法与数字进行大小比较,例如,“a是否大于等于3”这样的命题是完全错误的。我们必须有一定的措施来判断用户的输入内容,当用户输入的内容不是数字时,脚本能予以提示,从而免于崩溃。

接下来我们编写脚本Checkkeys.sh,提示用户输入一个字符并将其赋值给变量KEY,然后根据变量KEY的值向用户显示其值是字母、数字还是其他字符。

[root@linuxprobe ~]# vim Checkkeys.sh
#!/bin/bash
read -p "请输入一个字符,并按Enter键确认:" KEY
case "$KEY" in
[a-z]|[A-Z])
echo "您输入的是 字母。"
;;
[0-9])
echo "您输入的是 数字。"
;;
*)
echo "您输入的是 空格、功能键或其他控制字符。"
esac
[root@linuxprobe ~]# bash Checkkeys.sh
请输入一个字符,并按Enter键确认:6
您输入的是 数字。
[root@linuxprobe ~]# bash Checkkeys.sh
请输入一个字符,并按Enter键确认:p
您输入的是 字母。
[root@linuxprobe ~]# bash Checkkeys.sh
请输入一个字符,并按Enter键确认:^[[15~
您输入的是 空格、功能键或其他控制字符。

Linux学习笔记RHEL 7(三)--管道符、重定向、环境变量、Vim编辑器与Shell命令脚本_第22张图片

计划任务服务程序

接下来需要了解如何设置服务器的计划任务服务,把周期性、规律性的工作交给系统自动完成。

计划任务分为一次性计划任务与长期性计划任务,大家可以按照如下方式理解。

一次性计划任务:今晚11点30分开启网站服务。

长期性计划任务:每周一的凌晨3点25分把/home/wwwroot目录打包备份为backup.tar.gz。

我们可以用at命令实现这种功能,只需要写成“at 时间”的形式就可以。如果想要查看已设置好但还未执行的一次性计划任务,可以使用“at -l”命令;要想将其删除,可以用“atrm 任务序号”。在使用at命令来设置一次性计划任务时,默认采用的是交互式方法。例如,使用下述命令将系统设置为在今晚23:30分自动重启网站服务。

[root@linuxprobe ~]# at 23:30
at > systemctl restart httpd
at > 此处请同时按下Ctrl+d来结束编写计划任务
job 3 at Mon Apr 27 23:30:00 2015
[root@linuxprobe ~]# at -l
3 Mon Apr 27 23:30:00 2016 a root

可以把前面学习的管道符(任意门)放到两条命令之间,让at命令接收前面echo命令的输出信息,以达到通过非交互式的方式创建计划一次性任务的目的。

[root@linuxprobe ~]# echo "systemctl restart httpd" | at 23:30
job 4 at Mon Apr 27 23:30:00 2015
[root@linuxprobe ~]# at -l
3 Mon Apr 27 23:30:00 2016 a root
4 Mon Apr 27 23:30:00 2016 a root

如果我们不小心设置了两个一次性计划任务,可以使用下面的命令轻松删除其中一个:

[root@linuxprobe ~]# atrm 3
[root@linuxprobe ~]# at -l
4 Mon Apr 27 23:30:00 2016 a root

假设在每周一、三、五的凌晨3点25分,都需要使用tar命令把某个网站的数据目录进行打包处理,使其作为一个备份文件。我们可以使用crontab -e命令来创建计划任务。为自己创建计划任务无需使用-u参数,具体的实现效果的参数如crontab -l命令结果所示:

[root@linuxprobe ~]# crontab -e
no crontab for root - using an empty one
crontab: installing new crontab
[root@linuxprobe ~]# crontab -l
25 3 * * 1,3,5 /usr/bin/tar -czvf backup.tar.gz /home/wwwroot

需要说明的是,除了用逗号(,)来分别表示多个时间段,例如“8,9,12”表示8月、9月和12月。还可以用减号(-)来表示一段连续的时间周期(例如字段“日”的取值为“12-15”,则表示每月的12~15日)。以及用除号(/)表示执行任务的间隔时间(例如“*/2”表示每隔2分钟执行一次任务)。

我们再添加一条计划任务,它的功能是每周一至周五的凌晨1点钟自动清空/tmp目录内的所有文件。尤其需要注意的是,在crond服务的计划任务参数中,所有命令一定要用绝对路径的方式来写,如果不知道绝对路径,请用whereis命令进行查询,rm命令路径为下面输出信息中加粗部分。

(分,时,日,月,星期,命令:命令只能用绝对命令)

[root@linuxprobe ~]# whereis rm
rm: /usr/bin/rm /usr/share/man/man1/rm.1.gz /usr/share/man/man1p/rm.1p.gz
[root@linuxprobe ~]# crontab -e
crontab: installing new crontab
[root@linuxprobe ~]# crontab -l
25 3 * * 1,3,5 /usr/bin/tar -czvf backup.tar.gz /home/wwwroot
0 1 * * 1-5 /usr/bin/rm -rf /tmp/*

本章节的复习作业

1.Vim编辑器的三种模式分别是什么?

答:命令模式、末行模式与输入模式(也叫编辑模式或插入模式)。

2.怎么从输入模式切换到末行模式?

答:需要先敲击Esc键退回到命令模式,然后敲击冒号(:)键后进入末行模式。

3.一个完整的Shell脚本应该哪些内容?

答:应该包括脚本声明、注释信息和可执行语句(即命令)。

4.分别解释Shell脚本中$0与$3变量的作用。

答:在Shell脚本中,$0代表脚本文件的名称,$3则代表该脚本在执行时接收的第三个参数。

5.if条件测试语句有几种结构,最灵活且最复杂的是哪种结构?

答:if条件测试语句包括单分支、双分支与多分支等三种结构,其中多分支结构是最灵活且最复杂的结构,其结构形式为if…then…elif…then…else…fi。

6.for条件循环语句的循环结构是什么样子的?

答:for条件循环语句的结构为“for 变量名 in 取值列表 do 命令序列 done”

7.若在while条件循环语句中使用true作为循环条件,那么会发生什么事情?

答:因条件测试值永久为true,因此脚本中循环部分会无限地重复执行下去,直到碰到exit命令才会结束。

8.如果需要依据用户的输入参数执行不同的操作,最方便的条件测试语句是什么?

答:case条件语句。

9.Linux系统的长期计划任务所使用的服务是什么,其参数格式是什么?

答:长期计划任务需要使用crond服务程序,参数格式是“分、时、日、月、星期 命令”。


你可能感兴趣的:(燎沉香·Rhel,7)