鸟哥的Linux私房菜
  • 目录及概述
  • 第零章 计算机概论
    • 0.1 电脑-辅助人脑的好工具
    • 0.2 个人电脑架构与相关设备元件
    • 0.3 数据表示方式
    • 0.4 软件程序运行
    • 0.5 重点回顾
    • 0.6 本章习题
    • 0.7 参考资料与延伸阅读
  • 第一章 Linux是什么与如何学习
    • 1.1 Linux是什么
    • 1.2 Torvalds的Linux发展
    • 1.3 Linux当前应用的角色
    • 1.4 Linux该如何学习
    • 1.5 重点回顾
    • 1.6 本章习题
    • 1.7 参考资料与延伸阅读
  • 第二章 主机规划与磁盘分区
    • 2.1 Linux与硬件的搭配
    • 2.2 磁盘分区
    • 2.3 安装Linux前的规划
    • 2.4 重点回顾
    • 2.5 本章习题
    • 2.6 参考资料与延伸阅读
  • 第三章 安装CentOS7.X
    • 3.1 本练习机的规划-尤其是分区参数
    • 3.2 开始安装CentOS_7
    • 3.3 多重开机安装流程与管理
    • 3.4 重点回顾
    • 3.5 本章习题
    • 3.6 参考资料与延伸阅读
  • 第四章 首次登陆与线上求助
    • 4.1 首次登录系统
    • 4.2 文字模式下指令的下达
    • 4.3 Linux系统的线上求助man_page与info_page
    • 4.4 超简单文字编辑器-nano
    • 4.5 正确的关机方法
    • 4.6 重点回顾
    • 4.7 本章习题
    • 4.8 参考资料与延伸阅读
  • 第五章 Linux的文件权限与目录配置
    • 5.1 使用者与群组
    • 5.2 Linux文件权限概念
    • 5.3 Linux目录配置
    • 5.4 重点回顾
    • 5.5 本章习题
    • 5.6 参考资料与延伸阅读
  • 第六章 Linux文件与目录管理
    • 6.1 目录与路径
    • 6.2 文件与目录管理
    • 6.3 文件内容查阅
    • 6.4 文件与目录的默认权限与隐藏权限
    • 6.5 指令与文件的搜寻
    • 6.6 极重要的复习-权限与指令间的关系
    • 6.7 重点回顾
    • 6.8 本章习题
    • 6.9 参考资料与延伸阅读
  • 第七章 Linux磁盘与文件系统管理
    • 7.1 认识Linux文件系统
    • 7.2 文件系统的简单操作
    • 7.3 磁盘的分区-格式化-检验与挂载
    • 7.4 设置开机挂载
    • 7.5 内存交换空间swap创建
    • 7.6 文件系统的特殊观察与操作
    • 7.7 重点回顾
    • 7.8 本章习题
    • 7.9 参考资料与延伸阅读
  • 第八章 文件的压缩与打包
    • 8.1 压缩文件的用途与技术
    • 8.2 Linux系统常见的压缩指令
    • 8.3 打包指令-tar
    • 8.4 XFS文件系统的备份与还原
    • 8.5 光盘写入工具
    • 8.6 其他常见的压缩与备份工具
    • 8.7 重点回顾
    • 8.8 本章习题
    • 8.9 参考资料与延伸阅读
  • 第九章 vim程序编辑器
    • 9.1 vi与vim
    • 9.2 vi的使用
    • 9.3 vim的额外功能
    • 9.4 其他vim使用注意事项
    • 9.5 重点回顾
    • 9.6 本章习题
    • 9.7 参考资料与延伸阅读
  • 第十章 认识与学习BASH
    • 10.1 认识bash这个shell
    • 10.2 shell的变量功能
    • 10.3 命令别名与历史命令
    • 10.4 BashShell的操作环境
    • 10.5 数据流重导向
    • 10.6 管线命令-pipe
    • 10.7 重点回顾
    • 10.8 本章习题
    • 10.9 参考资料与延伸阅读
  • 第十一章 正则表达式与文件格式化处理
    • 11.1 开始之前-什么是正则表达式
    • 11.2 基础正则表达式
    • 11.3 延伸正则表达式
    • 11.4 文件的格式化与相关处理
    • 11.5 重点回顾
    • 11.6 本章习题
    • 11.7 参考资料与延伸阅读
  • 第十二章 学习shell scripts
    • 12.1 什么是ShellScripts
    • 12.2 简单的ShellScript练习
    • 12.3 善用判断式
    • 12.4 条件判断式
    • 12.5 循环loop
    • 12.6 ShellScript的追踪与debug
    • 12.7 重点回顾
    • 12.8 本章习题
  • 第十三章 Linux帐号管理与ACL权限控制
    • 13.1 Linux的账户与群组
    • 13.2 账号管理
    • 13.3 主机的细部权限规划-ACL的使用
    • 13.4 使用者身份切换
    • 13.5 使用者的特殊shell和PAM模块
    • 13.6 Linux主机上的使用者讯息传递
    • 13.7 CentOS7环境下大量创建账号的方法
    • 13.8 重点回顾
    • 13.9 本章习题
    • 13.10 参考资料与延伸阅读
  • 第十四章 磁盘配额(Quota)与进阶文件系统管理
    • 14.1 磁盘配额Quota的应用与实作
    • 14.2 软件磁盘阵列SoftwareRAID
    • 14.3 逻辑卷轴管理员LogicalVolumeManager
    • 14.4 重点回顾
    • 14.5 本章习题
    • 14.6 参考资料与延伸阅读
  • 第十五章 例行性工作调度(crontab)
    • 15.1 什么是例行性工作调度
    • 15.2 仅执行一次的工作调度
    • 15.3 循环执行的例行性工作调度
    • 15.4 可唤醒停机期间的工作任务
    • 15.5 重点回顾
    • 15.6 本章习题
  • 第十六章 程序管理与SELinux初探
    • 16.1 什么是程序process
    • 16.2 工作管理JobControl
    • 16.3 程序管理
    • 16.4 特殊文件与程序
    • 16.5 SELinux初探
    • 16.6 重点回顾
    • 16.7 本章习题
    • 16.8 参考资料与延伸阅读
  • 第十七章 认识系统服务(daemon)
    • 17.1 什么是daemon与服务service
    • 17.2 通过systemctl管理服务
    • 17.3 systemctl针对service类型的配置文件
    • 17.4 systemctl针对timer的配置文件
    • 17.5 CentOS7.x默认启动的服务简易说明
    • 17.6 重点回顾
    • 17.7 本章习题
    • 17.8 参考资料与延伸阅读
  • 第十八章 认识与分析登录文件
    • 18.1 什么是登录文件
    • 18.2 rsyslog.service-记录登录文件的服务
    • 18.3 登录文件的轮替logrotate
    • 18.4 systemd-journald.service简介
    • 18.5 分析登录文件
    • 18.6 重点回顾
    • 18.7 本章习题
    • 18.8 参考资料与延伸阅读
  • 第十九章 开机流程、模块管理与Loader
    • 19.1 Linux的开机流程分析
    • 19.2 核心与核心模块
    • 19.3 BootLoader-Grub2
    • 19.4 开机过程的问题解决
    • 19.5 重点回顾
    • 19.6 本章习题
    • 19.7 参考资料与延伸阅读
  • 第二十章 网络设置与备份策略
    • 20.1 系统基本设置
    • 20.2 服务器硬件数据的收集
    • 20.3 备份要点
    • 20.4 备份的种类和频率与工具的选择
    • 20.5 鸟哥的备份策略
    • 20.6 灾难复原的考虑
    • 20.7 重点回顾
    • 20.8 本章习题
    • 20.9 参考资料与延伸阅读
  • 第二十一章 软件安装:源代码与Tarball
    • 21.1 开源代码的软件安装与升级简介
    • 21.2 使用传统程序语言进行编译的简单范例
    • 21.3 用make进行宏编译
    • 21.4 Tarball的管理与建议
    • 21.5 函数库管理
    • 21.6 检验软件正确性
    • 21.7 重点回顾
    • 21.8 本章习题
    • 21.9 参考资料与延伸阅读
  • 第二十二章 软件安装:RPM,SRPM与YUM功能
    • 22.1 软件管理员简介
    • 22.2 RPM软件管理程序-rpm
    • 22.3 YUM线上升级机制
    • 22.4 SRPM的使用-rpmbuild
    • 22.5 重点回顾
    • 22.6 本章习题
    • 22.7 参考资料与延伸阅读
  • 第二十三章 X Window设置介绍
    • 23.1 什么是XWindowSystem
    • 23.2 XServer配置文件解析与设置
    • 23.3 显卡驱动程序安装范例
    • 23.4 重点回顾
    • 23.5 本章习题
    • 23.6 参考资料与延伸阅读
  • 第二十四章 核心编译
    • 24.1 编译前的任务-认识核心与取得核心源代码
    • 24.2 核心编译的前处理与核心功能选择
    • 24.3 核心的编译与安装
    • 24.4 额外-单一核心模块编译
    • 24.5 以最新核心版本编译CentOS7.x的核心
    • 24.6 重点回顾
    • 24.7 本章习题
    • 24.8 参考资料与延伸阅读
Powered by GitBook
On this page
  • 12.4.1 利用 if .... then
  • 12.4.2 利用 case ..... esac 判断
  • 12.4.3 利用 function 功能
  1. 第十二章 学习shell scripts

12.4 条件判断式

只要讲到“程序”的话,那么条件判断式,亦即是“ if then ”这种判别式肯定一定要学习的! 因为很多时候,我们都必须要依据某些数据来判断程序该如何进行。举例来说,我们在上头的 [ans_yn.sh] 讨论输入回应的范例中不是有练习当使用者输入 Y/N 时,必须要执行不同的讯息输出吗?简单的方式可以利用 && 与 || ,但如果我还想要执行一堆指令呢?那真的得要 if then 来帮忙啰~下面我们就来聊一聊!

12.4.1 利用 if .... then

这个 if .... then 是最常见的条件判断式了~简单的说,就是当符合某个条件判断的时候, 就予以进行某项工作就是了。这个 if ... then 的判断还有多层次的情况!我们分别介绍如下:

  • 单层、简单条件判断式

如果你只有一个判断式要进行,那么我们可以简单的这样看:

if [ 条件判断式 ]; then
    当条件判断式成立时,可以进行的指令工作内容;
fi   <==将 if 反过来写,就成为 fi 啦!结束 if 之意!

至于条件判断式的判断方法,与前一小节的介绍相同啊!较特别的是,如果我有多个条件要判别时, 除了 [ans_yn.sh] 那个案例所写的,也就是“将多个条件写入一个中括号内的情况”之外, 我还可以有多个中括号来隔开喔!而括号与括号之间,则以 && 或 || 来隔开,他们的意义是:

  • && 代表 AND ;

  • || 代表 or ;

所以,在使用中括号的判断式中, && 及 || 就与指令下达的状态不同了。举例来说, ans_yn.sh 里面的判断式可以这样修改:

[ "${yn}" == "Y" -o "${yn}" == "y" ] 上式可替换为 [ "${yn}" == "Y" ] || [ "${yn}" == "y" ]

之所以这样改,很多人是习惯问题!很多人则是喜欢一个中括号仅有一个判别式的原因。好了, 现在我们来将 ans_yn.sh 这个脚本修改成为 if ... then 的样式来看看:

[dmtsai@study bin]$ cp ans_yn.sh ans_yn-2.sh  <==用复制来修改的比较快!
[dmtsai@study bin]$ vim ans_yn-2.sh
#!/bin/bash
# Program:
#       This program shows the user's choice
# History:
# 2015/07/16    VBird   First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

read -p "Please input (Y/N): " yn

if [ "${yn}" == "Y" ] &#124;&#124; [ "${yn}" == "y" ]; then
    echo "OK, continue"
    exit 0
fi
if [ "${yn}" == "N" ] &#124;&#124; [ "${yn}" == "n" ]; then
    echo "Oh, interrupt!"
    exit 0
fi
echo "I don't know what your choice is" && exit 0

不过,由这个例子看起来,似乎也没有什么了不起吧?原本的 ans_yn.sh 还比较简单呢~ 但是如果以逻辑概念来看,其实上面的范例中,我们使用了两个条件判断呢!明明仅有一个 ${yn} 的变量,为何需要进行两次比对呢? 此时,多重条件判断就能够来测试测试啰!

  • 多重、复杂条件判断式

在同一个数据的判断中,如果该数据需要进行多种不同的判断时,应该怎么作?举例来说,上面的 [ans_yn.sh] 脚本中,我们只要进行一次 ${yn} 的判断就好 (仅进行一次 if ),不想要作多次 if 的判断。 此时你就得要知道下面的语法了:

# 一个条件判断,分成功进行与失败进行 (else)
if [ 条件判断式 ]; then
    当条件判断式成立时,可以进行的指令工作内容;
else
    当条件判断式不成立时,可以进行的指令工作内容;
fi

如果考虑更复杂的情况,则可以使用这个语法:

# 多个条件判断 (if ... elif ... elif ... else) 分多种不同情况执行
if [ 条件判断式一 ]; then
    当条件判断式一成立时,可以进行的指令工作内容;
elif [ 条件判断式二 ]; then
    当条件判断式二成立时,可以进行的指令工作内容;
else
    当条件判断式一与二均不成立时,可以进行的指令工作内容;
fi

你得要注意的是, elif 也是个判断式,因此出现 elif 后面都要接 then 来处理!但是 else 已经是最后的没有成立的结果了, 所以 else 后面并没有 then 喔!好!我们来将 ans_yn-2.sh 改写成这样:

[dmtsai@study bin]$ cp ans_yn-2.sh ans_yn-3.sh
[dmtsai@study bin]$ vim ans_yn-3.sh
#!/bin/bash
# Program:
#       This program shows the user's choice
# History:
# 2015/07/16    VBird   First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

read -p "Please input (Y/N): " yn

if [ "${yn}" == "Y" ] &#124;&#124; [ "${yn}" == "y" ]; then
    echo "OK, continue"
elif [ "${yn}" == "N" ] &#124;&#124; [ "${yn}" == "n" ]; then
    echo "Oh, interrupt!"
else
    echo "I don't know what your choice is"
fi

是否程序变得很简单,而且依序判断,可以避免掉重复判断的状况,这样真的很容易设计程序的啦! ^_^! 好了,让我们再来进行另外一个案例的设计。一般来说,如果你不希望使用者由键盘输入额外的数据时, 可以使用[上一节提到的参数功能 ($1)]!让使用者在下达指令时就将参数带进去! 现在我们想让使用者输入“ hello ”这个关键字时,利用参数的方法可以这样依序设计:

  1. 判断 $1 是否为 hello,如果是的话,就显示 "Hello, how are you ?";

  2. 如果没有加任何参数,就提示使用者必须要使用的参数下达法;

  3. 而如果加入的参数不是 hello ,就提醒使用者仅能使用 hello 为参数。

整个程序的撰写可以是这样的:

[dmtsai@study bin]$ vim hello-2.sh
#!/bin/bash
# Program:
#    Check $1 is equal to "hello"
# History:
# 2015/07/16    VBird    First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

if [ "${1}" == "hello" ]; then
    echo "Hello, how are you ?"
elif [ "${1}" == "" ]; then
    echo "You MUST input parameters, ex> {${0} someword}"
else
    echo "The only parameter is 'hello', ex> {${0} hello}"
fi

然后你可以执行这支程序,分别在 $1 的位置输入 hello, 没有输入与随意输入, 就可以看到不同的输出啰~是否还觉得挺简单的啊! ^_^。事实上, 学到这里,也真的很厉害了~好了,下面我们继续来玩一些比较大一点的计划啰~

[dmtsai@study ~]$ netstat -tuln
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN
tcp6       0      0 :::22                   :::*                    LISTEN
tcp6       0      0 ::1:25                  :::*                    LISTEN
udp        0      0 0.0.0.0:123             0.0.0.0:*
udp        0      0 0.0.0.0:5353            0.0.0.0:*
udp        0      0 0.0.0.0:44326           0.0.0.0:*
udp        0      0 127.0.0.1:323           0.0.0.0:*
udp6       0      0 :::123                  :::*
udp6       0      0 ::1:323                 :::*
#封包格式           本地IP:端口             远端IP:端口             是否监听

上面的重点是“Local Address (本地主机的IP与端口对应)”那个字段,他代表的是本机所启动的网络服务! IP的部分说明的是该服务位于那个接口上,若为 127.0.0.1 则是仅针对本机开放,若是 0.0.0.0 或 ::: 则代表对整个 Internet 开放 (更多信息请参考服务器架设篇的介绍)。 每个端口 (port) 都有其特定的网络服务,几个常见的 port 与相关网络服务的关系是:

  • 80: WWW

  • 22: ssh

  • 21: ftp

  • 25: mail

  • 111: RPC(远端程序调用)

  • 631: CUPS(打印服务功能)

假设我的主机有兴趣要侦测的是比较常见的 port 21, 22, 25及 80 时,那我如何通过 netstat 去侦测我的主机是否有打开这四个主要的网络服务端口呢?由于每个服务的关键字都是接在冒号“ : ”后面, 所以可以借由撷取类似“ :80 ”来侦测的!那我就可以简单的这样去写这个程序喔:

[dmtsai@study bin]$ vim netstat.sh
#!/bin/bash
# Program:
#     Using netstat and grep to detect WWW,SSH,FTP and Mail services.
# History:
# 2015/07/16    VBird    First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

# 1\. 先作一些告知的动作而已~
echo "Now, I will detect your Linux server's services!"
echo -e "The www, ftp, ssh, and mail(smtp) will be detect! \n"

# 2\. 开始进行一些测试的工作,并且也输出一些信息啰!
testfile=/dev/shm/netstat_checking.txt
netstat -tuln > ${testfile}          # 先转存数据到内存当中!不用一直执行 netstat
testing=$(grep ":80 " ${testfile})   # 侦测看 port 80 在否?
if [ "${testing}" != "" ]; then
    echo "WWW is running in your system."
fi
testing=$(grep ":22 " ${testfile})   # 侦测看 port 22 在否?
if [ "${testing}" != "" ]; then
    echo "SSH is running in your system."
fi
testing=$(grep ":21 " ${testfile})   # 侦测看 port 21 在否?
if [ "${testing}" != "" ]; then
    echo "FTP is running in your system."
fi
testing=$(grep ":25 " ${testfile})   # 侦测看 port 25 在否?
if [ "${testing}" != "" ]; then
    echo "Mail is running in your system."
fi

实际执行这支程序你就可以看到你的主机有没有启动这些服务啦!是否很有趣呢? 条件判断式还可以搞的更复杂!举例来说,在台湾当兵是国民应尽的义务,不过,在当兵的时候总是很想要退伍的! 那你能不能写个脚本程序来跑,让使用者输入他的退伍日期,让你去帮他计算还有几天才退伍?

由于日期是要用相减的方式来处置,所以我们可以通过使用 date 显示日期与时间,将他转为由 1970-01-01 累积而来的秒数, 通过秒数相减来取得剩余的秒数后,再换算为日数即可。整个脚本的制作流程有点像这样:

  1. 先让使用者输入他们的退伍日期;

  2. 再由现在日期比对退伍日期;

  3. 由两个日期的比较来显示“还需要几天”才能够退伍的字样。

似乎挺难的样子?其实也不会啦,利用“ date --date="YYYYMMDD" +%s ”转成秒数后,接下来的动作就容易的多了!如果你已经写完了程序,对照下面的写法试看看:

[dmtsai@study bin]$ vim cal_retired.sh
#!/bin/bash
# Program:
#    You input your demobilization date, I calculate how many days before you demobilize.
# History:
# 2015/07/16    VBird    First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

# 1\. 告知使用者这支程序的用途,并且告知应该如何输入日期格式?
echo "This program will try to calculate :"
echo "How many days before your demobilization date..."
read -p "Please input your demobilization date (YYYYMMDD ex>20150716): " date2

# 2\. 测试一下,这个输入的内容是否正确?利用正则表达式啰~
date_d=$(echo ${date2} &#124;grep '[0-9]\{8\}')   # 看看是否有八个数字
if [ "${date_d}" == "" ]; then
    echo "You input the wrong date format...."
    exit 1
fi

# 3\. 开始计算日期啰~
declare -i date_dem=$(date --date="${date2}" +%s)      # 退伍日期秒数
declare -i date_now=$(date +%s)                        # 现在日期秒数
declare -i date_total_s=$((${date_dem}-${date_now}))   # 剩余秒数统计
declare -i date_d=$((${date_total_s}/60/60/24))        # 转为日数
if [ "${date_total_s}" -lt "0" ]; then                 # 判断是否已退伍
    echo "You had been demobilization before: " $((-1*${date_d})) " ago"
else
    declare -i date_h=$(($((${date_total_s}-${date_d}*60*60*24))/60/60))
    echo "You will demobilize after ${date_d} days and ${date_h} hours."
fi

瞧一瞧,这支程序可以帮你计算退伍日期呢~如果是已经退伍的朋友, 还可以知道已经退伍多久了~哈哈!很可爱吧~脚本中的 dated 变量宣告那个 /60/60/24 是来自于一天的总秒数 (24小时60分60秒) 。瞧~全部的动作都没有超出我们所学的范围吧~ ^^ 还能够避免使用者输入错误的数字,所以多了一个正则表达式的判断式呢~ 这个例子比较难,有兴趣想要一探究竟的朋友,可以作一下[课后练习题] 关于计算生日的那一题喔!~加油!

12.4.2 利用 case ..... esac 判断

上个小节提到的“ if .... then .... fi ”对于变量的判断是以“比对”的方式来分辨的, 如果符合状态就进行某些行为,并且通过较多层次 (就是 elif ...) 的方式来进行多个变量的程序码撰写,譬如 [hello-2.sh] 那个小程序,就是用这样的方式来撰写的啰。 好,那么万一我有多个既定的变量内容,例如 hello-2.sh 当中,我所需要的变量就是 "hello" 及空字串两个, 那么我只要针对这两个变量来设置状况就好了,对吧?那么可以使用什么方式来设计呢?呵呵~就用 case ... in .... esac 吧~,他的语法如下:

case  $变量名称 in   <==关键字为 case ,还有变量前有钱字号
  "第一个变量内容")   <==每个变量内容建议用双引号括起来,关键字则为小括号 )
    程序段
    ;;            <==每个类别结尾使用两个连续的分号来处理!
  "第二个变量内容")
    程序段
    ;;
  *)                  <==最后一个变量内容都会用 * 来代表所有其他值
    不包含第一个变量内容与第二个变量内容的其他程序执行段
    exit 1
    ;;
esac                  <==最终的 case 结尾!“反过来写”思考一下!

要注意的是,这个语法以 case (实际案例之意) 为开头,结尾自然就是将 case 的英文反过来写!就成为 esac 啰! 不会很难背啦!另外,每一个变量内容的程序段最后都需要两个分号 (;;) 来代表该程序段落的结束,这挺重要的喔! 至于为何需要有 * 这个变量内容在最后呢?这是因为,如果使用者不是输入变量内容一或二时, 我们可以告知使用者相关的信息啊!废话少说,我们拿 hello-2.sh 的案例来修改一下,他应该会变成这样喔:

[dmtsai@study bin]$ vim hello-3.sh
#!/bin/bash
# Program:
#     Show "Hello" from $1.... by using case .... esac
# History:
# 2015/07/16    VBird    First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

case ${1} in
  "hello")
    echo "Hello, how are you ?"
    ;;
  "")
    echo "You MUST input parameters, ex> {${0} someword}"
    ;;
  *)   # 其实就相当于万用字符,0~无穷多个任意字符之意!
    echo "Usage ${0} {hello}"
    ;;
esac

在上面这个 hello-3.sh 的案例当中,如果你输入“ sh hello-3.sh test ”来执行, 那么屏幕上就会出现“Usage hello-3.sh {hello}”的字样,告知执行者仅能够使用 hello 喔~ 这样的方式对于需要某些固定字串来执行的变量内容就显的更加的方便呢! 这种方式你真的要熟悉喔!这是因为早期系统的很多服务的启动 scripts 都是使用这种写法的 (CentOS 6.x 以前)。 虽然 CentOS 7 已经使用 systemd,不过仍有数个服务是放在 /etc/init.d/ 目录下喔!例如有个名为 netconsole 的服务在该目录下, 那么你想要重新启动该服务,是可以这样做的 (请注意,要成功执行,还是得要具有 root 身份才行!一般帐号能执行,但不会成功!):

/etc/init.d/netconsole restart

重点是那个 restart 啦!如果你使用“ less /etc/init.d/netconsole ”去查阅一下,就会看到他使用的是 case 语法, 并且会规定某些既定的变量内容,你可以直接下达 /etc/init.d/netconsole , 该 script 就会告知你有哪些后续接的变量可以使用啰~方便吧! ^_^

一般来说,使用“ case $变量 in ”这个语法中,当中的那个“ $变量 ”大致有两种取得的方式:

  • 直接下达式:例如上面提到的,利用“ script.sh variable ” 的方式来直接给予 $1 这个变量的内容,这也是在 /etc/init.d 目录下大多数程序的设计方式。

  • 互动式:通过 read 这个指令来让使用者输入变量的内容。

这么说或许你的感受性还不高,好,我们直接写个程序来玩玩:让使用者能够输入 one, two, three , 并且将使用者的变量显示到屏幕上,如果不是 one, two, three 时,就告知使用者仅有这三种选择。

[dmtsai@study bin]$ vim show123.sh
#!/bin/bash
# Program:
#    This script only accepts the flowing parameter: one, two or three.
# History:
# 2015/07/17    VBird    First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

echo "This program will print your selection !"
# read -p "Input your choice: " choice   # 暂时取消,可以替换!
# case ${choice} in                      # 暂时取消,可以替换!
case ${1} in                             # 现在使用,可以用上面两行替换!
  "one")
    echo "Your choice is ONE"
    ;;
  "two")
    echo "Your choice is TWO"
    ;;
  "three")
    echo "Your choice is THREE"
    ;;
  *)
    echo "Usage ${0} {one&#124;two&#124;three}"
    ;;
esac

此时,你可以使用“ sh show123.sh two ”的方式来下达指令,就可以收到相对应的回应了。 上面使用的是直接下达的方式,而如果使用的是互动式时,那么将上面第 10, 11 行的 "#" 拿掉, 并将 12 行加上注解 (#),就可以让使用者输入参数啰~这样是否很有趣啊?

12.4.3 利用 function 功能

什么是“函数 (function)”功能啊?简单的说,其实, 函数可以在 shell script 当中做出一个类似自订执行指令的东西,最大的功能是, 可以简化我们很多的程序码~举例来说,上面的 show123.sh 当中,每个输入结果 one, two, three 其实输出的内容都一样啊~那么我就可以使用 function 来简化了! function 的语法是这样的:

function fname() {
    程序段
}

那个 fname 就是我们的自订的执行指令名称~而程序段就是我们要他执行的内容了。 要注意的是,因为 shell script 的执行方式是由上而下,由左而右, 因此在 shell script 当中的 function 的设置一定要在程序的最前面, 这样才能够在执行时被找到可用的程序段喔 (这一点与传统程序语言差异相当大!初次接触的朋友要小心!)! 好~我们将 show123.sh 改写一下,自订一个名为 printit 的函数来使用喔:

[dmtsai@study bin]$ vim show123-2.sh
#!/bin/bash
# Program:
#    Use function to repeat information.
# History:
# 2015/07/17    VBird    First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

function printit(){
    echo -n "Your choice is "     # 加上 -n 可以不断行继续在同一行显示
}

echo "This program will print your selection !"
case ${1} in
  "one")
    **printit**; echo ${1} &#124; tr 'a-z' 'A-Z'  # 将参数做大小写转换!
    ;;
  "two")
    **printit**; echo ${1} &#124; tr 'a-z' 'A-Z'
    ;;
  "three")
    **printit**; echo ${1} &#124; tr 'a-z' 'A-Z'
    ;;
  *)
    echo "Usage ${0} {one&#124;two&#124;three}"
    ;;
esac

以上面的例子来说,鸟哥做了一个函数名称为 printit ,所以,当我在后续的程序段里面, 只要执行 printit 的话,就表示我的 shell script 要去执行“ function printit .... ” 里面的那几个程序段落啰!当然啰,上面这个例子举得太简单了,所以你不会觉得 function 有什么好厉害的, 不过,如果某些程序码一再地在 script 当中重复时,这个 function 可就重要的多啰~ 不但可以简化程序码,而且可以做成类似“模块”的玩意儿,真的很棒啦!

Tips 建议读者可以使用类似 vim 的编辑器到 /etc/init.d/ 目录下去查阅一下你所看到的文件, 并且自行追踪一下每个文件的执行情况,相信会更有心得!

另外, function 也是拥有内置变量的~他的内置变量与 shell script 很类似, 函数名称代表示 $0 ,而后续接的变量也是以 $1, $2... 来取代的~ 这里很容易搞错喔~因为“ function fname() { 程序段 } ”内的 $0, $1... 等等与 shell script 的 $0 是不同的。以上面 show123-2.sh 来说,假如我下达:“ sh show123-2.sh one ” 这表示在 shell script 内的 $1 为 "one" 这个字串。但是在 printit() 内的 $1 则与这个 one 无关。 我们将上面的例子再次的改写一下,让你更清楚!

[dmtsai@study bin]$ vim show123-3.sh
#!/bin/bash
# Program:
#    Use function to repeat information.
# History:
# 2015/07/17    VBird    First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

function printit(){
    echo "Your choice is ${1}"   # 这个 $1 必须要参考下面指令的下达
}

echo "This program will print your selection !"
case ${1} in
  "one")
    **printit 1**  # 请注意, printit 指令后面还有接参数!
    ;;
  "two")
    **printit 2**
    ;;
  "three")
    **printit 3**
    ;;
  *)
    echo "Usage ${0} {one&#124;two&#124;three}"
    ;;
esac

在上面的例子当中,如果你输入“ sh show123-3.sh one ”就会出现“ Your choice is 1 ”的字样~ 为什么是 1 呢?因为在程序段落当中,我们是写了“ printit 1 ”那个 1 就会成为 function 当中的 $1 喔~ 这样是否理解呢? function 本身其实比较困难一点,如果你还想要进行其他的撰写的话。 不过,我们仅是想要更加了解 shell script 而已,所以,这里看看即可~了解原理就好啰~ ^_^

Previous12.3 善用判断式Next12.5 循环loop

Last updated 2 years ago

我们在第十章已经学会了 [grep] 这个好用的玩意儿,那么多学一个叫做 netstat 的指令,这个指令可以查询到目前主机有打开的网络服务端口 (service ports), 相关的功能我们会在继续介绍,这里你只要知道,我可以利用“ netstat -tuln ”来取得目前主机有启动的服务, 而且取得的信息有点像这样:

服务器架设篇