【BASH】回顾与知识点梳理(十八)
【BASH】回顾与知识点梳理 十八
- 十八. 条件判断式
- 18.1 利用 test 指令的测试功能
- 文件类型判断
- 文件权限侦测
- 两个文件之间的比较
- 两个整数之间的判定
- 判定字符串的数据
- 多重条件判定
- 18.2 利用判断符号 [ ]
- 18.3 Shell script 的默认参数($0, $1...)
- shift:造成参数变量号码偏移
- 18.4 利用 if .... then
- 单层、简单条件判断式
- 多重、复杂条件判断式
- 18.5 利用 case ..... esac 判断
- 18.6 利用 function 功能
该系列目录 --> 【BASH】回顾与知识点梳理(目录)
十八. 条件判断式
在第五章中,我们提到过 $?
这个变量所代表的意义, 此外,也透过 &&
及 ||
来作为前一个指令执行回传值对于后一个指令是否要进行的依据。第五章的讨论中,如果想要判断一个目录是否存在, 当时我们使用的是 ls 这个指令搭配数据流重导向,最后配合 $?
来决定后续的指令进行与否。 但是否有更简单的方式可以来进行『条件判断』呢?有的~那就是『 test
』这个指令。
18.1 利用 test 指令的测试功能
当我要检测系统上面某些文件或者是相关的属性时,利用 test 这个指令来工作真是好用得不得了,举例来说,我要检查 /dmtsai 是否存在时,使用:
[dmtsai@study ~]$ test -e /dmtsai
执行结果并不会显示任何讯息,但最后我们可以透过 $?
或 &&
及||
来展现整个结果呢! 例如我们在将上面的例子改写成这样:
[dmtsai@study ~]$ test -e /dmtsai && echo "exist" || echo "Not exist"
Not exist <==结果显示不存在啊!
最终的结果可以告知我们是『exist』还是『Not exist』呢!那我知道 -e 是测试一个『东西』在不在,如果还想要测试一下该档名是啥玩意儿时,还有哪些标志可以来判断的呢?呵呵!有底下这些东西喔!
文件类型判断
如 test -e filename 表示存在否
测试的标志 | 代表意义 |
---|---|
-e | 该『档名』是否存在?(常用) |
-f | 该『档名』是否存在且为文件(file)?(常用) |
-d | 该『文件名』是否存在且为目录(directory)?(常用) |
-b | 该『档名』是否存在且为一个 block device 装置? |
-c | 该『档名』是否存在且为一个 character device 装置? |
-S | 该『档名』是否存在且为一个 Socket 文件? |
-p | 该『档名』是否存在且为一个 FIFO (pipe) 文件? |
-L | 该『档名』是否存在且为一个连结档? |
文件权限侦测
如 test -r filename 表示可读否 (但 root 权限常有例外)
测试的标志 | 代表意义 |
---|---|
-r | 侦测该档名是否存在且具有『可读』的权限? |
-w | 侦测该档名是否存在且具有『可写』的权限? |
-x | 侦测该档名是否存在且具有『可执行』的权限? |
-u | 侦测该文件名是否存在且具有『SUID』的属性? |
-g | 侦测该文件名是否存在且具有『SGID』的属性? |
-k | 侦测该文件名是否存在且具有『Sticky bit』的属性? |
-s | 侦测该档名是否存在且为『非空白文件』? |
两个文件之间的比较
如: test file1 -nt file2
测试的标志 | 代表意义 |
---|---|
-nt | (newer than)判断 file1 是否比 file2 新 |
-ot | (older than)判断 file1 是否比 file2 旧 |
-ef | 判断 file1 与 file2 是否为同一文件,可用在判断 hard link 的判定上。 主要意义在判定,两个文件是否均指向同一个 inode 哩! |
两个整数之间的判定
例如 test n1 -eq n2
测试的标志 | 代表意义 |
---|---|
-eq | 两数值相等 (equal) |
-ne | 两数值不等 (not equal) |
-gt | n1 大于 n2 (greater than) |
-lt | n1 小于 n2 (less than) |
-ge | n1 大于等于 n2 (greater than or equal) |
-le | n1 小于等于 n2 (less than or equal) |
判定字符串的数据
测试的标志 | 代表意义 |
---|---|
test -z string | 判定字符串长度是否为 0 ?若 string 为空字符串,则为 true |
test -n string | 判定字符串长度是否非为 0 ?若 string 为空字符串,则为 false。注: -n 亦可省略 |
test str1 == str2 | 判定 str1 是否等于 str2 ,若相等,则回传 true |
test str1 != str2 | 判定 str1 是否不等于 str2 ,若相等,则回传 false |
多重条件判定
例如: test -r filename -a -x filename
测试的标志 | 代表意义 |
---|---|
-a | (and)两状况同时成立!例如 test -r file -a -x file,则 file 同时具有 r 与 x 权限时,才回传 true。 |
-o | (or)两状况任何一个成立!例如 test -r file -o -x file,则 file 具有 r 或 x 权限时,就可回传 true。 |
! | 反相状态,如 test ! -x file ,当 file 不具有 x 时,回传 true |
OK!现在我们就利用 test 来帮我们写几个简单的例子。首先,判断一下,让使用者输入一个档名,我们判断:
- 这个文件是否存在,若不存在则给予一个『Filename does not exist』的讯息,并中断程序;
- 若这个文件存在,则判断他是个文件或目录,结果输出『Filename is regular file』或 『Filename is directory』
- 判断一下,执行者的身份对这个文件或目录所拥有的权限,并输出权限数据!
你可以先自行创作看看,然后再跟底下的结果讨论讨论。注意利用 test 与 && 还有 || 等标志!
[dmtsai@study bin]$ vim file_perm.sh
#!/bin/bash
# Program:
# User input a filename, program will check the flowing:
# 1.) exist? 2.) file/directory? 3.) file permissions
# 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 -e "Please input a filename, I will check the filename's type and permission. \n\n"
read -p "Input a filename : " filename
test -z ${filename} && echo "You MUST input a filename." && exit 0
# 2. 判断文件是否存在?若不存在则显示讯息并结束脚本
test ! -e ${filename} && echo "The filename '${filename}' DO NOT exist" && exit 0
# 3. 开始判断文件类型与属性
test -f ${filename} && filetype="regulare file"
test -d ${filename} && filetype="directory"
test -r ${filename} && perm="readable"
test -w ${filename} && perm="${perm} writable"
test -x ${filename} && perm="${perm} executable"
# 4. 开始输出信息!
echo "The filename: ${filename} is a ${filetype}"
echo "And the permissions for you are : ${perm}"
如果你执行这个脚本后,他会依据你输入的档名来进行检查喔!先看是否存在,再看为文件或目录类型,最后判断权限。 但是你必须要注意的是,由于 root 在很多权限的限制上面都是无效的,所以使用 root 执行这个脚本时, 常常会发现与 ls -l 观察到的结果并不相同!所以,建议使用一般使用者来执行这个脚本试看看。
18.2 利用判断符号 [ ]
除了我们很喜欢使用的 test 之外,其实,我们还可以利用判断符号『 [ ]
』(就是中括号啦) 来进行数据的判断呢! 举例来说,如果我想要知道 ${HOME} 这个变量是否为空的,可以这样做:
[dmtsai@study ~]$ [ -z "${HOME}" ] ; echo $?
使用中括号必须要特别注意,因为中括号用在很多地方,包括通配符与正规表示法等等,所以如果要在 bash 的语法当中使用中括号作为 shell 的判断式时,必须要注意中括号的两端需要有空格符来分隔喔! 假设我空格键使用『□
』符号来表示,那么,在这些地方你都需要有空格键:
[ "$HOME" == "$MAIL" ]
[□"$HOME"□==□"$MAIL"□]↑ ↑ ↑ ↑
你会发现鸟哥在上面的判断式当中使用了两个等号『 == 』。其实在 bash 当中使用一个等号与两个等号的结果是一样的! 不过在一般惯用程序的写法中,
一个等号代表『变量的设定』
,两个等号则是代表『逻辑判断 (是与否之意)』
。 由于我们在中括号内重点在于『判断』而非『设定变量』,因此鸟哥建议您还是使用两个等号较佳!
上面的例子在说明,两个字符串${HOME}
与${MAIL}
是否相同的意思,相当于 test ${HOME} == ${MAIL}
的意思啦! 而如果没有空白分隔,例如[${HOME}==${MAIL}]
时,我们的 bash 就会显示错误讯息了!这可要很注意啊! 所以说,你最好要注意:
- 在中括号 [] 内的每个组件都需要有空格键来分隔;
- 在中括号内的变数,最好都以双引号括号起来;
- 在中括号内的常数,最好都以单或双引号括号起来。
为什么要这么麻烦啊?直接举例来说,假如我设定了 name=“VBird Tsai” ,然后这样判定:
[yurq@node-135 bin]$ name="VBird Tsai"
[yurq@node-135 bin]$ [ ${name} == "VBird" ]
-bash: [: too many arguments
见鬼了!怎么会发生错误啊?bash 还跟我说错误是由于『太多参数 (arguments)』所致! 为什么呢?因为 ${name} 如果没有使用双引号刮起来,那么上面的判定式会变成:
[ VBird Tsai == "VBird" ]
上面肯定不对嘛!因为一个判断式仅能有两个数据的比对,上面 VBird 与 Tsai 还有 “VBird” 就有三个资料! 这不是我们要的!我们要的应该是底下这个样子:
[ "VBird Tsai" == "VBird" ]
这可是差很多的喔!另外,中括号的使用方法与 test 几乎一模一样啊~ 只是中括号比较常用在条件判断式if ..... then ..... fi
的情况中就是了。 好,那我们也使用中括号的判断来做一个小案例好了,案例设定如下:
- 当执行一个程序的时候,这个程序会让用户选择 Y 或 N ,
- 如果用户输入 Y 或 y 时,就显示『 OK, continue 』
- 如果用户输入 n 或 N 时,就显示『 Oh, interrupt !』
- 如果不是 Y/y/N/n 之内的其他字符,就显示『 I don’t know what your choice is 』
[yurq@node-135 bin]$ cat choice.sh
#!/bin/bash
# Program:
# User input a choice[y(Y)/n(N)], program will print the flowing:
# 1.) OK, continue 2.) Oh, interrupt ! 3.) I don't know what your choice is
# History:
# 2023/08/09 yurq First releasePATH=$PATH:~/bin
export PATHecho -e "input a choice [y(Y)/n(N)]\n"
read -p "choice [y(Y)/n(N)]:" choice
[ $choice == 'Y' -o $choice == 'y' ] && echo " OK, continue " &&exit 0|| \
[ $choice == 'N' -o $choice == 'n' ] && echo " Oh, interrupt !" ||\
echo "I don't know what your choice is"
由于输入正确 (Yes) 的方法有大小写之分,不论输入大写 Y 或小写 y 都是可以的,此时判断式内就得要有两个判断才行! 由于是任何一个成立即可 (大写或小写的 y) ,所以这里使用 -o (或) 连结两个判断喔! 很有趣吧!利用这个字符串判别的方法,我们就可以很轻松的将使用者想要进行的工作分门别类呢! 接下来,我们再来谈一些其他有的没有的东西吧!
18.3 Shell script 的默认参数($0, $1…)
我们知道指令可以带有选项与参数,例如 ls -la 可以察看包含隐藏文件的所有属性与权限。那么 shell script 能不能在脚本档名后面带有参数呢?很有趣喔!举例来说,如果你想要重新启动系统的网络,可以这样做:
[dmtsai@study ~]$ file /etc/init.d/network
/etc/init.d/network: Bourne-Again shell script, ASCII text executable
# 使用 file 来查询后,系统告知这个文件是个 bash 的可执行 script 喔!
[dmtsai@study ~]$ /etc/init.d/network restart
restart 是重新启动的意思,上面的指令可以『重新启动 /etc/init.d/network 这支程序』的意思! 唔!那么如果你在 /etc/init.d/network 后面加上 stop 呢?没错!就可以直接关闭该服务了!这么神奇啊?没错啊!如果你要依据程序的执行给予一些变量去进行不同的任务时,本章一开始是使用 read 的功能!但 read 功能的问题是你得要手动由键盘输入一些判断式。如果透过指令后面接参数, 那么一个指令就能够处理完毕而不需要手动再次输入一些变量行为!这样下达指令会比较简单方便啦!
script 是怎么达成这个功能的呢?其实 script 针对参数已经有设定好一些变量名称了!对应如下:
/path/to/scriptname opt1 opt2 opt3 opt4$0 $1 $2 $3 $4
这样够清楚了吧?执行的脚本档名为 $0 这个变量,第一个接的参数就是 $1 啊~ 所以,只要我们在 script 里面善用 $1 的话,就可以很简单的立即下达某些指令功能了!除了这些数字的变量之外,我们还有一些较为特殊的变量可以在 script 内使用来呼叫这些参数喔!
特殊变量 | 意义 |
---|---|
$# | 代表后接的参数『个数』,以上表为例这里显示为『 4 』; |
$@ | 代表『 “$1” “$2” “$3” “$4” 』之意,每个变量是独立的(用双引号括起来); |
$* | 代表『 “$1c$2c$3c$4” 』,其中 c 为分隔字符,默认为空格键, 所以本例中代表『 “$1 $2 $3 $4” 』之意。 |
那个$@
与$*
基本上还是有所不同啦!不过,一般使用情况下可以直接记忆 $@ 即可! 好了,来做个例子吧~假设我要执行一个可以携带参数的 script ,执行该脚本后屏幕会显示如下的数据:
- 程序的文件名为何?
- 共有几个参数?
- 若参数的个数小于 2 则告知使用者参数数量太少
- 全部的参数内容为何?
- 第一个参数为何?
- 第二个参数为何
[dmtsai@study bin]$ vim how_paras.sh
#!/bin/bash
# Program:
# Program shows the script name, parameters...
# History:
# 2015/07/16 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo "The script name is ==> ${0}"
echo "Total parameter number is ==> $#"
[ "$#" -lt 2 ] && echo "The number of parameter is less than 2. Stop here." && exit 0
echo "Your whole parameter is ==> '$@'"
echo "The 1st parameter ==> ${1}"
echo "The 2nd parameter ==> ${2}"
执行结果如下:
[dmtsai@study bin]$ sh how_paras.sh theone haha quot
The script name is ==> how_paras.sh <==檔名
Total parameter number is ==> 3 <==果然有三个参数
Your whole parameter is ==> 'theone haha quot' <==参数的内容全部
The 1st parameter ==> theone <==第一个参数
The 2nd parameter ==> haha <==第二个参数
shift:造成参数变量号码偏移
除此之外,脚本后面所接的变量是否能够进行偏移 (shift) 呢?什么是偏移啊?我们直接以底下的范例来说明好了, 用范例说明比较好解释!我们将 how_paras.sh 的内容稍作变化一下,用来显示每次偏移后参数的变化情况:
[dmtsai@study bin]$ vim shift_paras.sh
#!/bin/bash
# Program:
# Program shows the effect of shift function.
# History:
# 2009/02/17 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo "Total parameter number is ==> $#"
echo "Your whole parameter is ==> '$@'"
shift # 进行第一次『一个变量的 shift 』
echo "Total parameter number is ==> $#"
echo "Your whole parameter is ==> '$@'"
shift 3 # 进行第二次『三个变量的 shift 』
echo "Total parameter number is ==> $#"
echo "Your whole parameter is ==> '$@'"
这玩意的执行成果如下:
[dmtsai@study bin]$ sh shift_paras.sh one two three four five six <==给予六个参数
Total parameter number is ==> 6 <==最原始的参数变量情况
Your whole parameter is ==> 'one two three four five six'
Total parameter number is ==> 5 <==第一次偏移,看底下发现第一个 one 不见了
Your whole parameter is ==> 'two three four five six'
Total parameter number is ==> 2 <==第二次偏移掉三个,two three four 不见了
Your whole parameter is ==> 'five six'
光看结果你就可以知道啦,那个 shift 会移动变量,而且 shift 后面可以接数字,代表拿掉最前面的几个参数的意思。 上面的执行结果中,第一次进行 shift 后他的显示情况是『 one two three four five six』,所以就剩下五个啦!第二次直接拿掉三个,就变成『 two three four five six 』啦! 这样这个案例可以了解了吗?理解了 shift 的功能了吗?
上面这几个例子都很简单吧?几乎都是利用 bash 的相关功能而已~ 不难啦~底下我们就要使用条件判断式来进行一些分别功能的设定了,好好瞧一瞧先~
18.4 利用 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" ] || [ "${yn}" == "y" ]; thenecho "OK, continue"exit 0
fi
if [ "${yn}" == "N" ] || [ "${yn}" == "n" ]; thenecho "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" ] || [ "${yn}" == "y" ]; thenecho "OK, continue"
elif [ "${yn}" == "N" ] || [ "${yn}" == "n" ]; thenecho "Oh, interrupt!"
elseecho "I don't know what your choice is"
fi
是否程序变得很简单,而且依序判断,可以避免掉重复判断的状况,这样真的很容易设计程序的啦!^_^! 好了,让我们再来进行另外一个案例的设计。一般来说,如果你不希望用户由键盘输入额外的数据时, 可以使用上一节提到的参数功能 ($1)!让用户在下达指令时就将参数带进去! 现在我们想让用户输入『 hello 』这个关键词时,利用参数的方法可以这样依序设计:
- 判断 $1 是否为 hello,如果是的话,就显示 “Hello, how are you ?”;
- 如果没有加任何参数,就提示使用者必须要使用的参数下达法;
- 而如果加入的参数不是 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" ]; thenecho "Hello, how are you ?"
elif [ "${1}" == "" ]; thenecho "You MUST input parameters, ex> {${0} someword}"
elseecho "The only parameter is 'hello', ex> {${0} hello}"
fi
然后你可以执行这支程序,分别在 $1 的位置输入 hello, 没有输入与随意输入, 就可以看到不同的输出啰~是否还觉得挺简单的啊! ^_^。事实上, 学到这里,也真的很厉害了~好了,底下我们继续来玩一些比较大一点的计划啰~
我们在前面已经学会了 grep 这个好用的玩意儿,那么多学一个叫做 netstat 的指令,这个指令可以查询到目前主机有开启的网络服务端口口 (service ports), 相关的功能我们会在服务器架设篇继续介绍,这里你只要知道,我可以利用『 netstat -tuln 』来取得目前主机有启动的服务, 而且取得的信息有点像这样:
[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
...
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
netstat -nltu|awk '{print $4}'|grep ':'|sed 's/.*://g'|sort|uniq
实际执行这支程序你就可以看到你的主机有没有启动这些服务啦!是否很有趣呢? 条件判断式还可以搞的更复杂!举例来说,在台湾当兵是国民应尽的义务,不过,在当兵的时候总是很想要退伍的!那你能不能写个脚本程序来跑,让用户输入他的退伍日期,让你去帮他计算还有几天才退伍?
由于日期是要用相减的方式来处置,所以我们可以透过使用 date 显示日期与时间,将他转为由1970-01-01 累积而来的秒数, 透过秒数相减来取得剩余的秒数后,再换算为日数即可。整个脚本的制作流程有点像这样:
- 先让使用者输入他们的退伍日期;
- 再由现在日期比对退伍日期;
- 由两个日期的比较来显示『还需要几天』才能够退伍的字样。
似乎挺难的样子?其实也不会啦,利用『 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} |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"
elsedeclare -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
瞧一瞧,这支程序可以帮你计算退伍日期呢~如果是已经退伍的朋友,还可以知道已经退伍多久了~哈哈!很可爱吧~脚本中的 date_d 变量宣告那个 /60/60/24 是来自于一天的总秒数 (24 小时60 分60 秒) 。瞧~全部的动作都没有超出我们所学的范围吧~ ^_^ 还能够避免用户输入错误的数字,所以多了一个正规表示法的判断式呢~ 这个例子比较难,有兴趣想要一探究竟的朋友,可以作一下课后练习题 关于计算生日的那一题喔!~加油!
18.5 利用 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|two|three}";;
esac
此时,你可以使用『 sh show123.sh two 』的方式来下达指令,就可以收到相对应的响应了。 上面使用的是直接下达的方式,而如果使用的是交互式时,那么将上面第 10, 11 行的 “#” 拿掉,并将 12 行加上批注 (#),就可以让使用者输入参数啰~这样是否很有趣啊?
18.6 利用 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} | tr 'a-z' 'A-Z' # 将参数做大小写转换!;;"two")printit; echo ${1} | tr 'a-z' 'A-Z';;"three")printit; echo ${1} | tr 'a-z' 'A-Z';;*)echo "Usage ${0} {one|two|three}";;
esac
以上面的例子来说,鸟哥做了一个函数名称为 printit ,所以,当我在后续的程序段里面, 只要执行printit 的话,就表示我的 shell script 要去执行『 function printit … 』 里面的那几个程序段落啰!当然啰,上面这个例子举得太简单了,所以你不会觉得 function 有什么好厉害的, 不过,如果某些程序代码一再地在 script 当中重复时,这个 function 可就重要的多啰~ 不但可以简化程序代码,而且可以做成类似『模块』的玩意儿,真的很棒啦!
建议读者可以使用类似 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|two|three}";;
esac
在上面的例子当中,如果你输入『 sh show123-3.sh one 』就会出现『 Your choice is 1
』的字样~ 为什么是 1 呢?因为在程序段落当中,我们是写了『 printit 1 』那个 1 就会成为 function 当中的 $1 喔~ 这样是否理解呢? function 本身其实比较困难一点,如果你还想要进行其他的撰写的话。 不过,我们仅是想要更加了解 shell script 而已,所以,这里看看即可~了解原理就好啰~ ^_^
该系列目录 --> 【BASH】回顾与知识点梳理(目录)
相关文章:
【BASH】回顾与知识点梳理(十八)
【BASH】回顾与知识点梳理 十八 十八. 条件判断式18.1 利用 test 指令的测试功能文件类型判断文件权限侦测两个文件之间的比较两个整数之间的判定判定字符串的数据多重条件判定 18.2 利用判断符号 [ ]18.3 Shell script 的默认参数($0, $1...)shift:造成参数变量号码…...
linux 目录操作命令
目录操作命令 文件列表 ls命令文件列表 ls [选项] [参数]-------------------------------l 详细信息-L 紧接着符号性连接,列出它们指向的文件-a 所有文件,包含隐藏文件(以点号起始的文件)-A 与-a相同,但是不会列出来. 和 ..-c 根据创建时间排…...

React Dva项目小优化之redux-action
之前 我们讲过 models 接下啦 我们来给大家讲一个新的库 这个库的话 有最好 没有影响也不大 它主要是帮助我们处理 action的 我们直接在 GitHub 官网上搜索 redux-action 我们搜出来 第一个就是 从星数来看 还是非常优秀的 我们拉下来 找到这个Documentation 然后点击进去 进…...
Kotlin反射访问androidx.collection.LruCache类私有变量
Kotlin反射访问androidx.collection.LruCache类私有变量 androidx.collection.LruCache类中定义了一个名为map的LinkedHashMap,map存储了所有LruCache的数据,有时候需要遍历访问该LinkedHashMap,取出里面的值,但是LruCache代码实…...

高级进阶多线程——多任务处理、线程状态(生命周期)、三种创建多线程的方式
Java多线程 Java中的多线程是一个同时执行多个线程的进程。线程是一个轻量级的子进程,是最小的处理单元。多进程和多线程都用于实现多任务处理。 但是,一般使用多线程而不是多进程,这是因为线程使用共享内存区域。它们不分配单独的内存区域…...
【 K8S 】 Pod 进阶
目录 //资源限制官网示例:重启策略 //健康检查:又称为探针(Probe) //资源限制 当定义 Pod 时可以选择性地为每个容器设定所需要的资源数量。 最常见的可设定资源是 CPU 和内存大小,以及其他类型的资源。 当为 Pod 中…...

众和转债,宏微转债,阳谷转债上市价格预测
众和转债 基本信息 转债名称:众和转债,评级:AA,发行规模:13.75亿元。 正股名称:新疆众和,今日收盘价:8.14元,转股价格:8.2元。 当前转股价值 转债面值 / 转股…...

MySQL~事务的四大特性和隔离级别
事务的四大特性 1.原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个…...

JMeter处理接口签名之BeanShell实现MD5加密
项目A需要给项目B提供一个接口,这个接口加密了,现在需要测试这个接口,需要怎么编写脚本呢?实现接口签名的方式有两种:BeanShell实现MD5加密和函数助手实现MD5加密,之前已经分享过了函数助手实现MD5加密&…...
【Golang】一文学完 Golang 基本语法
Golang 下载 安装包链接:https://share.weiyun.com/InsZoHHu IDE 下载:https://www.jetbrains.com/go/ 第一个 golang 程序 package mainimport "fmt"func main() {fmt.Println("hello golang") }每个可执行代码都必须包含 Pack…...

《Java-SE-第三十五章》之方法引用
前言 在你立足处深挖下去,就会有泉水涌出!别管蒙昧者们叫嚷:“下边永远是地狱!” 博客主页:KC老衲爱尼姑的博客主页 博主的github,平常所写代码皆在于此 共勉:talk is cheap, show me the code 作者是爪哇岛的新手,水平很有限&…...

Effective Java笔记(33)优先考虑类型安全的异构容器
泛型最常用于集合,如 Set<E >和 Map<K ,V>,以及单个元素的容器 ,如 ThreadLocal<T>和 AtomicReference<T> 。 在所有这些用法中,它都充当被参数化了的容器 。 这样就限制每个容器…...

释放AI创作潜能:从大模型训练到高产力应用
文章目录 每日一句正能量前言什么是人工智能生成内容(AIGC)人工智能生成内容(AIGC)能做什么为什么要用人工智能生成内容(AIGC)创作成果用Java实现冒泡排序算法学生信息收集系统学生请假管理系统需求分析教务…...

Ajax 笔记(一)—— Ajax 入门
笔记目录 1. Ajax 入门1.1 Ajax 概念1.2 axios 使用1.2.1 URL1.2.2 URL 查询参数1.2.3 小案例-查询地区列表1.2.4 常用请求方法和数据提交1.2.5 错误处理 1.3 HTTP 协议1.3.1 请求报文1.3.2 响应报文 1.4 接口文档1.5 案例1.5.1 用户登录(主要业务)1.5.2…...

Android Studio跳过Haxm打开模拟器
由于公司权限限制无法安装Haxm,这个时候我们可以试试Arm相关的镜像去跳过Haxm运行模拟器。解决方案:安装API27以下的Arm Image. #ifdef __x86_64__if (sarch "arm64" && apiLevel >28) {APANIC("Avds CPU Architecture %s i…...

从一个GPU到多个GPU
在多GPU运行应用程序时,需要正确设计GPU之间的通信,GPU间数据传输的效率取决于GPU是如何连接在一个节点上并跨集群的 在多GPU系统里有两种连接方式 多GPU通过单个节点连接到PCIe总线上 多GPU连接到集群中的网络交换机上 /* * 本示例演示了如何使用 Open…...
小白编写一个Chrome
步骤 1:了解插件的基本结构和功能 首先,向小白解释什么是Chrome插件,它是如何工作的,以及它可以做什么。强调插件可以修改网页内容、添加功能等。 步骤 2:准备工作 安装Chrome浏览器:确保小白的计算机上…...

自然语言处理学习笔记(六)————字典树
目录 1.字典树 (1)为什么引入字典树 (2)字典树定义 (3)字典树的节点实现 (4)字典树的增删改查 DFA(确定有穷自动机) (5)优化 1.…...

WPF实战项目十一(API篇):待办事项功能api接口
1、新建ToDoController.cs继承基础控制器BaseApiController,但是一般业务代码不写在控制器内,业务代码写在Service,先新建统一返回值格式ApiResponse.cs: public class ApiResponse{public ApiResponse(bool status, string mess…...
ffmpeg给视频添加时间水印,准确且不模糊
ffmpeg -i {输入文件路径} -vf{drawtext} {输出文件路径} 针对视频模糊,加上 -b:v {输出视频码率};右键属性,可查看离线视频源码率; 针对离线视频文件加上时间水印,时间跳变不正常,加上-re; 整…...

【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...

CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...

dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能
1. 开发环境准备 安装DevEco Studio 3.1: 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK 项目配置: // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...
规则与人性的天平——由高考迟到事件引发的思考
当那位身着校服的考生在考场关闭1分钟后狂奔而至,他涨红的脸上写满绝望。铁门内秒针划过的弧度,成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定",构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...

C# winform教程(二)----checkbox
一、作用 提供一个用户选择或者不选的状态,这是一个可以多选的控件。 二、属性 其实功能大差不差,除了特殊的几个外,与button基本相同,所有说几个独有的 checkbox属性 名称内容含义appearance控件外观可以变成按钮形状checkali…...