住房和建设建设局网站,潍坊网站建设 58,电子商务网络平台建设,北京优化公司司文章目录 什么是shell脚本为什么要学习shell脚本第一个脚本编写与执行 简单的shell脚本练习简单案例脚本的执行方式差异(source、sh script、./script) 如何使用shell脚本的判断式利用test命令的测试功能利用判断符号[ ]shell脚本的默认变量($0、$1...) shell脚本的条件判断式利… 文章目录 什么是shell脚本为什么要学习shell脚本第一个脚本编写与执行 简单的shell脚本练习简单案例脚本的执行方式差异(source、sh script、./script) 如何使用shell脚本的判断式利用test命令的测试功能利用判断符号[ ]shell脚本的默认变量($0、$1...) shell脚本的条件判断式利用 if...then利用case...esac判断利用function功能 $()和\$(())讲解shell脚本的循环while do done、until do done(不定循环)for...do...done(固定循环)for...do...done的数值处理搭配随机数与数组的实验shell脚本的跟踪与调试 什么是shell脚本
什么是shell脚本呢(shell script程序化脚本)呢就字面上的意义我们将它分为两部分就是在命令行下面让我们与系统沟通的一个工具接口。那么【script】是啥字面意思就是【脚本、剧本】的意思。
整句话来说shell脚本就是利用shell的功能所写的一个【程序】这个程序是使用纯文本文件将一些shell的语法与命令(含外部命令)写在里面搭配正则表达式、管道命令与数据流重定向等功能达到我们所想要处理的目的。
shell脚本可以简单地被看成批处理文件也可以被说成是一个程序文件且这个程序语言由于都是利用shell与相关工具命令所以不需要编译即可执行。
为什么要学习shell脚本
为什么要学习shell脚本呢如果你不从事IT工作只想要【会用】Linux而已那么不需要学shell脚本也无所谓。但是如果你是真的想要玩明白Linux那么shell脚本肯定要学因为shell脚本给我带来以下好处
自动化管理Shell脚本可以帮助管理员自动化管理一些重复性的任务例如备份数据、批量修改文件、自动化部署等从而提高工作效率和减少错误。系统管理Shell脚本可以帮助管理员进行系统管理例如监测系统性能、管理进程、设置定时任务等。脚本编写Shell脚本是一种非常简单易懂的编程语言掌握Shell脚本可以帮助开发人员快速编写一些小型脚本程序例如数据处理、文本解析等。维护脚本在Linux系统中很多应用程序都是通过脚本来启动和停止的例如Apache、MySQL等因此掌握Shell脚本可以帮助管理员更好地维护这些应用程序。调试程序Shell脚本是一种非常容易调试的编程语言可以帮助开发人员快速定位程序中的错误并进行修复。
第一个脚本编写与执行
shell脚本其实就是纯文本文件我们可以编辑这个文件然后让这个文件来帮我们一次执行多个命令或是利用一些运算与逻辑判断来帮我们完成某些功能。在shell脚本的编写中需要注意下面的事项
命令是从上而下、从左向右地分析执行命令、选项与参数间的多个空格都会被忽略掉空白行也将被忽略掉并且【TAB】按键所产生的空白行视为空格键如果读取到一个Enter符号就尝试开始执行该行(或该串)命令至于如果一行内容太多则可以用【\Enter】来扩展至下一行【#】可作为注释任何加在#后面的数据将全部视为注释文字
这样一来我写的脚本程序就会被一行一行地执行现在我们假设你写的这个程序文件名是/home/csq/shell.sh 那如何执行这个文件呢
直接命令行执行shell.sh 文件必须要具备可读与可执行的权限(rx)然后 绝对路径使用/home/csq/shell.sh 来执行命令相对路径假设工作目录在/home/csq下则使用【./shell.sh】来执行变量【PATH】功能通过【bash shell.sh】或【sh shell.sh】来执行
重点就是让shell.sh文件必须要具备可读与可执行的权限才行
那么【sh shell.sh】也可以执行这是因为/bin/sh其实就是/bin/bash(链接文件)使用sh shell.sh就是告诉系统我想要直接以bash的功能来执行shell.sh 这个文件内相关命令的意思。所以说只要你shell.sh具有可读可执行的权限就能用 sh 的参数。 学过语言的都知道一开始学都是从输出【Hello World】开始的我们可以编一个输出Hello world的shell脚本
[rootchenshiren ~]# mkdir /tmp/shelldir ; cd /tmp/shelldir/
[rootchenshiren shelldir]# vim hello-world.sh
#!/bin/bash
# 说明
# 在屏幕上输出hello,world!
# 时间: 2024/3
PATH$PATH:/tmp/shelldir
export PATH
echo -e Hello World! \a \r
exit0第一行 #!/bin/bash 在声明这个使用的shell名称
因为我们使用的是bash所以必须要以【#!/bin/bash】来声明这个文件内使用bash的语法。这样【#!】开头的行被称为shebang行。那么当这个程序被执行时它就能够加载bash的相关环境配置文件(这个文件一般来说是~/.bashrc)并且执行bash来使我们下面的命令能够执行。这很重要在很多错误的情况中如果没有设置好这行那么该程序可能会无法执行因为系统可能无法判断该程序需要声明shell来执行。
程序说明内容
整个脚本当中除了第一行【#!】是用来声明shell之外其他的 # 都是【注释】用途。所以上面的程序当中第二行以下就是用来说明整个程序的基本数据。一般来说建议你一定要养成习惯说明该脚本的(1)内容与功能(2)版本信息(3)作者联络方式(4)建文件日期(5)历史记录等等这样有助于未来程序的改写与调试。(建议大家注释使用英文注释主要原因是为了兼容性和可移植性)下面案例的注释部分我写成中文方便阅读
主要环境变量的声明
建议务必要将一些重要的环境变量设置好PATH与LANG(如果有使用到输出相关的信息时)是当中最重要的。如此一来我们这个程序在进行时可以直接执行一些外部命令而不必写绝对路径。
主程序部分
将主要的程序写好即可在这个例子当中就是echo 那一行
执行结果告知(定义返回值)
一个命令的成功与否可以使用$?这个变量来观察那么我们也可以利用exit这个命令来让程序中断并且返回一个数值给系统。在此例中使用exit 0代表退出脚本并且返回一个0给系统所以我执行完这个脚本后若接着执行echo $? 则得到0的值。
接下来执行一下写的脚本
[rootchenshiren shelldir]# chmod x hello-world.sh ; ./hello-world.sh
Hello World! 简单的shell脚本练习
简单案例 交互式脚本变量内容由用户决定 很多时候我们需要用户输入一些内容好让程序可以顺利运行。大家应该都有安装过软件的经验安装的时候它不是会问你【安装到哪个目录去】吗那个让用户输入数据的操作就是让用户输入变量内容。
请你以read命令的用途编写一个脚本它可以让用户输入1. first name 2. last name 最后在屏幕上显示【Your full name is】的内容
[rootlocalhost shelldir]# vim showname.sh
#!/bin/bash
# 说明
# 用户输入字节的姓和全名程序会输出用户的姓和全名是什么
# 时间2024/3
PATH$PATH:/tmp/shelldir
export PATH
read -p 输入姓名 xm
read -p 输入全名 qm
echo -e 你姓$xm \n全名叫$qm \a执行结果如下你能够发现用户自己输入的变量可以让程序所使用并且将它显示到屏幕上。
[rootchenshiren shelldir]# chmod x showname.sh ; ./showname.sh
输入姓名c
输入全名csq
你姓c
全名叫csq 随日期变化利用date建立文件 假设我的服务器内有数据库数据库每天的数据都不太一样因此当我备份时我希望每天的数据备份成不同的文件名这样才能让旧的数据也能够保存下来不被覆盖。应该怎么做呢
假设我想要建立三个空文件(通过touch)文件名最开头由用户输入决定假如用户输入filename而今天的日期是2023/04/27我想要以前天、昨天、今天的日期来建立这些文件即filename_2023/04/25、filename_2023/04/26、filename_2023/04/27那该如何是好
[rootlocalhost shelldir]# vim create_3_filename.sh
#!/bin/bash
# 说明根据用户输入文件名字程序自动创建3个文件以前天昨天今天来命名
# 时间2024/3
PATH$PATH:/tmp/shelldir
export PATH
unset fileuser
unset filename
echo 输入文件名我将使用touch创建三个文件
read -p 输入文件名 fileuserfilename${fileuser:-filename} # 如果没有输入文件名默认文件名会设置为filenamedate1$(date --date2 days ago %Y%m%d) # 设置时间
date2$(date --date1 days ago %Y%m%d)
date3$(date %Y%m%d)file1${filename}_${date1} # 设置文件名
file2${filename}_${date2}
file3${filename}_${date3}
touch ${file1} # 创建文件
touch ${file2}
touch ${file3}ls -al ${PWD}/${fileuser}* # 打印创建的文件
echo -e \a执行结果如下
[rootchenshiren shelldir]# chmod x create_3_filename.sh ; ./create_3_filename.sh
输入文件名我将使用touch创建三个文件
输入文件名csq
-rw-r--r--. 1 root root 0 3月 15 15:18 /tmp/shelldir/csq_20240313
-rw-r--r--. 1 root root 0 3月 15 15:18 /tmp/shelldir/csq_20240314
-rw-r--r--. 1 root root 0 3月 15 15:18 /tmp/shelldir/csq_20240315[rootchenshiren shelldir]# sh create_3_filename.sh
输入文件名我将使用touch创建三个文件
输入文件名
-rwxr-xr-x. 1 root root 790 3月 15 15:17 /tmp/shelldir/create_3_filename.sh
-rw-r--r--. 1 root root 0 3月 15 15:18 /tmp/shelldir/csq_20240313
-rw-r--r--. 1 root root 0 3月 15 15:18 /tmp/shelldir/csq_20240314
-rw-r--r--. 1 root root 0 3月 15 15:18 /tmp/shelldir/csq_20240315
-rw-r--r--. 1 root root 0 3月 15 15:18 /tmp/shelldir/filename_20240313
-rw-r--r--. 1 root root 0 3月 15 15:18 /tmp/shelldir/filename_20240314
-rw-r--r--. 1 root root 0 3月 15 15:18 /tmp/shelldir/filename_20240315
-rwx------. 1 root root 147 3月 15 13:56 /tmp/shelldir/hello-world.sh
-rwxr-xr-x. 1 root root 265 3月 15 14:17 /tmp/shelldir/showname.sh 数值运算简单的加减乘除 例如我们要让用户输入两个变量然后将两个变量的内容相乘最后输出相乘的接管那可以怎么做呢
[rootchenshiren shelldir]# vim cf.sh
#!/bin/bash
# 说明:
# 用户输入两个数字程序算出两数乘积输出结果
# 时间: 2024/3
PATH${PATH}:/tmp/shelldir
export PATH
read -p 输入第一个数 number1
read -p 输入第二个数 number2
result$((${number1}*${number2}))
echo -e \n乘积结果${result} \a执行结果
[rootchenshiren shelldir]# chmod x cf.sh ; ./cf.sh
输入第一个数10
输入第二个数20000乘积结果200000 在数值运算上面可以使用【declare -i product${onenumber}*${twonumber}】也可以使用上面的方式来进行。比较建议使用这种方法
var$((运算内容))这种方法很容易记忆。未来你可以使用这种方式来计算。至于数值运算上的处理则有、-、*、/、%等。%是取余的举例来说15对7取余数结果就是152*71所以余数就是1
[rootlocalhost shelldir]# echo $((15 % 7))
1如果你要计算含有小数点的数据时其实可以通过bc这个命令的协助
[rootlocalhost shelldir]# echo 123.456*789.123 | bc
97421.969数值运算通过bc计算Pi(圆周率) 其实计算Pi小数点以下位数可以无限地扩展下去而bc提供了一个运算Pi的函数要使用该函数必须通过bc -l来调用才行。也因为这个小数点的位数可以无限扩展运算的特性存在所以我们可以通过下面这个小脚本来让用户输入一个【小数点位】让Pi能够更准确
[rootchenshiren shelldir]# vim pi.sh
#!/bin/bash
# 说明
# 计算圆周率
# 时间2024/3
PATH${PATH}:/tmp/shelldir
export PATH
echo 此程序将计算圆周率
read -p 请输入计算圆周率后几位小数默认(10): pi
num${pi:-10}
echo 开始计算请等待
time echo scale${num};4*a(1) | bc -lq[rootchenshiren shelldir]# chmod x pi.sh ; ./pi.sh
此程序将计算圆周率
请输入计算圆周率后几位小数默认(10):20
开始计算请等待
3.14159265358979323844real 0m0.001s
user 0m0.001s
sys 0m0.001s上述数据中那个4*a(1) 是bc主动提供的一个计算Pi的函数至于scale就是要bc计算几个小数点位数的意思。scale的数值越大代表Pi要被计算得越精确当然用掉的时间就会越多。
脚本的执行方式差异(source、sh script、./script)
不同的脚本执行方式会造成不一样的结果尤其对bash的环境影响很大。脚本的执行除了前面使用的【sh script】 以外还可以利用 【source 】或小数点【.】来执行。那么这些执行方式有何不同呢 利用直接执行的方式来执行脚本 当使用直接命令执行(不论是绝对路径/相对路径还是${PATH}内)或是利用bash(或sh)来执行脚本时该脚本都会使用一个新的bash环境来执行脚本内的命令。也就是说使用这种执行方式时其实脚本是在子进程的bash内执行的。和export差不多重点在于【当子进程完成后在子进程内的各项变量或操作会结束而不会传回到父进程中】
我们使用上面缩写的showname.sh脚本做个实验这个脚本可以让用户设置两个变量分别是firstname与lastname。想一想如果你直接执行该行命令该命令帮你设置的firstname会不会生效呢
[rootchenshiren shelldir]# echo ${nickname} ${fullname}两个变量并不存在
[rootchenshiren shelldir]# sh showname.sh
输入姓名c
输入全名csq
你姓c
全名叫csq
[rootlocalhost shelldir]# echo ${nickname} ${fullname} 事实上这两个变量在父进程的bash中还是不存在的上面的结果很奇怪怎么我已经利用showname.sh设置号的变量竟然在bash环境下面无效。我们以下图说明 当你使用直接执行的方法来处理时系统会给予一个新的bash让我们来执行showname.sh 里面的命令因此你的nickname和fullname等变量其实是在下图的子进程bash内执行的当showname.sh执行完毕后子进程bash内的所有数据便被删除因此【echo ${nickname} ${fullname}】时就看不到任何东西了。 利用source来执行脚本在父进程中执行 如果你使用source 来执行命令那就不一样了同样的脚本我们来执行看看
[rootchenshiren shelldir]# source showname.sh
输入姓名c
输入全名csq
你姓c
全名叫csq
[rootchenshiren shelldir]# echo ${xm},${qm}
c,csq # 有数据产生了竟然生效了没错因为source对脚本的执行方式可以使用下面的图例来说明showname.sh会在父进程中执行因此各项操作都会在原本的bash内生效。这也是为啥你不注销系统而要让某些写入~/.bashrc的设置生效时需要使用【source ~/.bashrc】而不能使用【bash ~/.bashrc】。 如何使用shell脚本的判断式
利用test命令的测试功能
当我们要检测上面某些文件或是相关的属性时利用test这个命令来进行检测
例如我要检查/csq 是否存在时
[rootlocalhost ~]# test -e /csq
[rootlocalhost ~]# echo $?
1【test -e /csq】并没有显示任何信息当我们输入了 echo $? 返回值不是0 代表是错误信息我们也可以利用 及||来展销整个结果
例如我换一种方法检测一下/csq是否存在
[rootlocalhost ~]# test -e /csq echo this is exist || echo this is no exist
this is no exist最终结果告诉我们是【exist】还是【no exist】那我们知道 -e 是测试一个【东西】存在不存在 关于某个文件名的【文件类型】判断 例如test -e filename 表示存在否
测试的参数代表的意义-e该【文件名】是否存在-f该【文件名】是否存在且位文件(file)-d该【文件名】是否存在且位目录-b该【文件名】是否存在且为一个block device 设备-c该【文件名】是否存在且为一个 character device-S该【文件名】是否存在且为一个socket文件-p该【文件名】是否存在且为一个FIFO文件-L该【文件名】是否存在且为一个链接文件 关于文件的权限检测 例如test -r filename 表示可读否
测试的参数代表的意义-r检测该文件名是否存在且具有【可读】的权限-w检测该文件名是否存在且具有【可写】的权限-x检测该文件名是否存在且具有【可执行】的权限-u检测该文件名是否存在且具有【SUID】的属性-g检测该文件名是否存在且具有【SGID】的属性-k检测该文件名是否存在且具有【SBIT】的属性-s检测该文件名是否存在且为【非空文件】 两个文件之间的比较 例如test file1 -nt file2
测试的参数代表的意义-nt判断【file1】是否比【file2】新-ot判断【file1】是否比【file2】旧-ef判断【file1】与【file2】是否为同一个文件可用在判断硬链接上面。主要意义在于判定两个文件是否均指向同一个inode 关于两个整数之间的比较 例如test n1 -eq n2
测试的参数代表的意义-eq两数值相等-ne两数值不相等-gtn1 大于 n2-ltn1 小于 n2-gen1 大于等于 n2-len1 小于等于 n2 判定字符串的数据 测试的参数代表的意义test -z string判定字符串是否为0若string为空字符串则为truetest -n string判断字符串是否非为0若string为空字符串则为falsetest str1 str2判定str1是否等于str2若相等则返回truetest str1 ! str2判定str1是否不等于str2若相等则返回true 多重条件判定 例如test -r filename -a -x filename
测试的参数代表的意义-a两条件同时成立。例如test -r file -a -x 则file同时具有r与x权限时才返回true-o两条件任何一个成立例如test -r file -o -x 则file具有r或x权限时旧可返回true!反相状态例如test ! -x file 当file不具有x时返回true 例题① 首先我让用户输入一个文件名我们判断
整个文件是否存在若不存在则给予一个【文件不存在】的信息并中断程序若这个文件存在则判断它是个文件或目录结果输出【这个文件是普通文件】或【这个文件是目录文件】判断一下执行者的身份对这个文件或目录所拥有者的权限并输出权限输出
[rootchenshiren shelldir]# vim panduan.sh
#!/bin/bash
# 说明
# 用户输入一个文件程序判断 1.文件是否存在 2. 是目录还是文件 3. 文件权限是什么
# 时间: 2024/3PATH${PATH}:/tmp/shelldir
export PATH
read -p 请输入一个文件: filename
test -z ${filename} echo 你必须输入一个文件 exit 0
test ! -e ${filename} echo 你输入的${filename}不存在无法判断 exit 0test -f ${filename} filetype普通文件
test -d ${filename} filedir目录文件
test -r ${filename} readfile可读
test -w ${filename} writefile可写
test -x ${filename} executefile可执行echo -e 这个文件是: ${filetype}${filedir} \n \$LOGNAME用户对这个${filetype}${filedir}拥有的权限有:${readfile},${writefile},${executefile} \r\a执行结果(如果文件存在)
[rootchenshiren shelldir]# chmod ax panduan.sh
[rootchenshiren shelldir]# sh panduan.sh
请输入一个文件: /root/pay.txt
这个文件是: 普通文件 root用户对这个普通文件拥有的权限有:可读,可写, 执行结果(如果文件不存在)
[rootchenshiren shelldir]# sh panduan.sh
请输入一个文件: dd
你输入的dd不存在无法判断执行结果(在其他用户上面执行)
[csqchenshiren shelldir]$ sh panduan.sh
请输入一个文件: /etc
这个文件是: 目录文件 csq用户对这个目录文件拥有的权限有:可读,,可执行 利用判断符号[ ]
其实除了test之外我们还可以利用【[ ]】判断符号来进行数据判断。举例来说如果我想知道${HOME}这个变量是否为空可以这样做
[rootlocalhost ~]# [ -z $HOME ] ; echo $?
1使用中括号必须要特别注意因为中括号用在很多地方包括通配符和正则表达式等。所以说如果要在bash的语法当中使用中括号作为shell的判断式时必须要注意中括号的两端需要有空格符来分隔。假设空格键使用【□】符号来表示那你这些地方都需要有空格
[rootlocalhost ~]# [□$HOME□□$MAIL□]你会发现上面的判断式中使用了两个等号【】其实在bash当中使用一个等号和两个等号的结果是一样的。不过在一般常用程序的写法中一个等号代表【变量的设置】两个等号则代表【逻辑判断(是与否之意)】。由于问在中括号内重点在于【判断】而非【设置变量】因此建议你使用两个等号
上面的例子在说明两个字符串【$HOME】与【$MAIL】是否相同相当于【 test ${HOME} ${MAIL}】。而如果没有空白分隔例如[${HOME}${MAIL}]我们的bash就会显示错误信息。
中括号[]内的每个组件都需要有空格来分隔在括号内的变量最好都以双引号括起来在中括号内的常数最好都以单或双引号括起来 例题① 当执行一个程序的时候这个程序会让用户选择Y或N如果用户输入Y或y时就显示【OK继续】如果用户输入N或n时就显示【NO不继续】如果不是Y/yN/n之内的字符就显示【我不知道你输入的是什么】
[rootlocalhost shelldir]# vim continue.sh
#!/bin/bash
# 程序说明
# 这个程序就是让用户做选择
# 时间
# 2023/04/29
PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:~/shelldir
export PATH
read -p 请输入一个Y/y,N/n我们来进行下一步 yn
[ ${yn} Y -o ${yn} y ] echo OK,继续 exit 0
[ ${yn} N -o ${yn} n ] echo NO,不继续 exit 0
echo -e \n我不知道你输入的是什么\a由于输入正确(Yes)的方法有大小写之分不论输入大写或小写y都是可以的此时判断式内就得要两个判断才行由于是任何一个成立即可(大写或小写的y)所以这里使用-o连接两个判断。
执行结果如下(输入Y)
[rootlocalhost shelldir]# sh continue.sh
请输入一个Y/y,N/n我们来进行下一步Y
OK,继续执行结果如下(没输入)
[rootlocalhost shelldir]# sh continue.sh
请输入一个Y/y,N/n我们来进行下一步我不知道你输入的是什么shell脚本的默认变量($0、$1…)
我们知道命令可以带有选项参数例如 【ls -al】可以查看包含隐藏文件的所有的属性与权限。那么shell脚本能不能再脚本文件名后面带有参数呢
例如如果你想要重新启动系统网络可以这样做
[rootchenshiren ~]# file /usr/sbin/NetworkManager
/usr/sbin/NetworkManager: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]92544dc17cde0154660e122ab223eacc47a0173a, for GNU/Linux 3.2.0, stripped
# 使用file来查询后系统告知这个文件是个bash可执行的文件
[rootchenshiren ~]# /usr/sbin/NetworkManager restartrestart是重启的意思上面的命令可以【重新启动 /usr/sbin/NetworkManager这个程序】。那么如果你再 【/usr/sbin/NetworkManager】加个个stop 就直接关闭服务了。
那么脚本怎么完成这个功能呢其实脚本针对参数已经设置好了一些变量名称对应如下
/path/to/scriptname opt1 opt2 opt3 opt4$0 $1 $2 $3 $4执行的脚本文件名为 $0这个变量第一个接的参数就是$1。所以只要我们在脚本里面善用$1的话就可以很简单地立即执行某些命令功能了。除了这些变量之外还有一些较为特殊的变量可以在脚本内使用来调用这些参数
$#代表后接的参数【个数】以上表为例这里显示为【4】$代表【$1 $2 $3 $4】之意每个变量都是独立的(用双引号括起来)$*代表【“$1c$2c$3c$4c”】其中c为分隔符默认为空格所以本例中代表【“$1 $2 $3 $4”】
$和$*一般情况下可以直接记忆$ 例题① 假设我要执行要个可以携带的参数脚本执行该脚本后屏幕会显示如下数据
程序的文件名是什么共有几个参数如果文件名后面没跟参数就提示他没跟参数若参数的个数小于2则告知用户参数数量太少全部的参数内容是什么第一个参数是什么第二个参数是什么
[rootchenshiren shelldir]# vim args_shibie.sh
#!/bin/bash
# 程序说明
# 展示以下执行shell脚本的文件名以及参数
# 时间
# 2024/3
PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
export PATH
echo 这个文件名是${0}
echo 共有$#个参数
[ $# 0 ] echo 文件名后面没跟参数 exit 0
[ $# -lt 2 ] echo 只有一个参数 $1 , 参数太少 exit 0
echo 全部的参数内容是$
echo 仅展示前3个参数内容
echo 第一个参数是$1
echo 第二个参数是$2
echo 第三个参数是: $3执行结果(跟了参数)
[rootchenshiren shelldir]# sh args_shibie.sh csq csq1 csq2 csq3 csq4
这个文件名是args_shibie.sh
共有5个参数
全部的参数内容是csq csq1 csq2 csq3 csq4
仅展示前3个参数内容
第一个参数是csq
第二个参数是csq1
第三个参数是: csq2执行结果(没跟参数)
[rootchenshiren shelldir]# sh args_shibie.sh
这个文件名是args_shibie.sh
共有0个参数
文件名后面没跟参数shift造成参数变量号码偏移 除此之外脚本后面所接的变量是否能够进行偏移(shift)呢什么是偏移我们举例来说明还是用上面的例题。
[rootlocalhost shelldir]# vim args_shibie.sh
#!/bin/bash
# 程序说明
# 显示shift造成的参数变量号码偏移了多少
# 时间
# 2024/3
PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
export PATH
echo 共有$#个参数
echo 全部的参数内容是$
shift
echo 共有$#个参数
echo 全部的参数内容是$
shift 3
echo 共有$#个参数
echo 全部的参数内容是$执行结果如下
[rootlocalhost shelldir]# sh args_shibie.sh aa bb cc dd ff gg hh
共有7个参数
全部的参数内容是aa bb cc dd ff gg hh
共有6个参数
全部的参数内容是bb cc dd ff gg hh
共有3个参数
全部的参数内容是ff gg hh看到上面的结果就可以知道shift会移动变量而且shift后面可以接数字代表拿掉最前面的几个参数的意思。上面执行结果中第一次进行shift后它的显示情况是【 aa bb cc dd ff gg hh 】所以只剩下6个参数第二次直接拿掉三个就变成【 aa bb cc dd ff gg hh 】
shell脚本的条件判断式
很多时候我们必须要根据某些数来判断程序该如何举例来说我们在之前的练习中让用户输入Y/N的时候必须要执行不同的信息输入可以使用 和||的方式那么如果我们要执行一堆命令呢
利用 if…then
这个if…then是最常见的条件判断式了。简单来说当符合某个条件判断的时候就允许他进行某项任务。这个if…then的判断还有多层次的情况。 单层、简单条件判断式 如果你只有一个判断式要进行。那么我们可以简单地这样看
if [ 条件判断式 ]; then当条件成立时可以进行的命令工作内容
fi # 将if 反过来写就成为fi意思就是结束if如果我们有多个条件判别的话就像之前的案例所写的就是将【多个条件写入一个中括号内的情况】我们还可以有多个括号隔开。而括号和括号之间则以或||隔开
代表AND||代表or
所以我们之前使用中括号判别到底输入的是不是Y/y就可以这样修改
[ ${yn} Y -o ${yn} y ]上面的案例可以替换为
[ ${yn} Y ] || [ ${yn} y ]那么我们再来用if…then的样式来看看 上图是我们之前案例的脚本内容我们来做一下修改
[rootchenshiren shelldir]# vim yesno.sh
#!/bin/bash
# 说明: 让用户输入Y或N,程序判断输入的是Y还是N,再做出输出
# 时间: 2024/3
PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:~/shelldir
export PATH
read -p 请输入一个Y/y,N/n我们来进行下一步 yn
if [ ${yn} Y -o ${yn} y ];thenecho OK,继续exit 0
fiif [ ${yn} N -o ${yn} n ];thenecho NO,不继续exit 0
fi
echo -e \n我不知道你输入的是什么\a执行结果(输入Y看结果)
[rootchenshiren shelldir]# sh yesno.sh
请输入一个Y/y,N/n我们来进行下一步y
OK,继续执行结果(什么也没输入)
[rootchenshiren shelldir]# sh yesno.sh
请输入一个Y/y,N/n我们来进行下一步我不知道你输入的是什么多重、复杂条件判断式 就像上述例子一样如果该数据需要进行多种不同的判断我只想执行一次${yn}的判断就好不想做多次if的判断那样应该怎么做呢
一个条件判断分成功执行与失败执行(else)
if [ 条件判断式 ]; then当条件判断式成立时可执行的命令
else当条件判断式不成立时可执行的命令
fi如果你遇到非常复杂的判断情况则可以使用这个语法
if [ 条件判断式 ]; then当条件判断式成立时可执行的命令
elif [ 条件判断式2 ]; then当条件判断式2成立时可执行的命令
else 当条件判断式1与2均不成立时可执行的命令
fi要注意的是【elif】也是个判断式因此【elif】后面都要接then来处理。else则是最后没有成立的结果不用加then。我们可以将之前的案例改为
[rootchenshiren shelldir]# vim yesno.sh
#!/bin/bash
# 说明: 让用户输入Y或N,程序判断输入的是Y还是N,再做出输出
# 时间: 2024/3
PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:~/shelldir
export PATH
read -p 请输入一个Y/y,N/n我们来进行下一步 yn
if [ ${yn} Y -o ${yn} y ];thenecho OK,继续exit 0
elif [ ${yn} N -o ${yn} n ];thenecho NO,不继续exit 0elseecho -e \n我不知道你输入的是什么\a
fi程序是不是变得非常容易看懂了可以避免重复判断的状况。 例题1 一般来说如果你不希望用户键盘输入额外的数据则可以使用【$1】参数功能让用户执行命令时将参数带进去。现在我想让用户输入【hello】这个关键字时利用参数的方法可以这样依序设计
判断$1是否为hello如果是的话就显示你好最近过的好吗如果没有加任何参数就提示用户必须要使用的参数执行法而如果加入的参数不是hello就提醒用户仅能使用hello为参数。
[rootchenshiren shelldir]# vim howareyou.sh
#!/bin/bash
# 说明: 判断允许该脚本是否加了hello参数
# 时间:2024/3PATH${PATH}:/tmp/shelldir
export PATHif [ ${1} hello -o ${1} HELLO ];thenecho -e 你好,最近过的好吗?exit 0
elif [ -z ${1} ];thenecho -e \n必须使用的参数执行法\aexit 0
elseecho -e \n仅能识别hello为参数\a
fi执行结果(输入hello)
[rootchenshiren shelldir]# sh howareyou.sh hello
你好,最近过的好吗?执行结果(什么也没输入)
[rootchenshiren shelldir]# sh howareyou.sh必须使用的参数执行法执行结果(随便添加了一个参数)
[rootchenshiren shelldir]# sh howareyou.sh ppp仅能识别hello为参数接下来再来了解一个命令利用这个命令来做几个实验学一个叫netstat的命令这个命令可以查询到目前主机开启的网络服务端口利用【netstat -tuln】来获取目前主机启动的服务
如果没有netstat这条命令可以使用
yum install -y net-tools来安装相关的命令
[rootlocalhost ~]# 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 127.0.0.1:323 0.0.0.0:*
udp6 0 0 ::1:323 :::* 输出的内容中最重要的就是 【Local Address(本地主机的IP与端口对应)】那个字段它代表的是本机所启动的网络服务IP的部分说明的是该服务器位于哪个接口上。若为【127.0.0.1】则是针对本机开发若是【0.0.0.0】或【:::】则代表对整个internet开放。每个端口都有其特点的网络服务几个常见的端口与相关网络服务的关系是
80WWW22ssh21ftp25mail111RPC(远程过程调用)631CPUS(打印机服务功能)
假设我的主机要检测比较常见的21、22、25及80端口时那我如何通过netstat去检测我的主机是否开启了这四个主要的网络服务端口由于每个服务的关键词都是接在冒号【:】后面所以可以使用类似【:80】的方式来检测
[rootchenshiren shelldir]# vim netstat.sh
#!/bin/bash
# 说明: 检测21 22 25 80 端口是否存在
# 时间: 2024/3
PATH${PATH}:/tmp/shelldir
export PATH
testfile/tmp/shelldir/testfile.txt
netstat -tulnp ${testfile}testing$(grep :80 ${testfile})
if [ ${testing} ! ] ;thenecho 存在80端口
elseecho 不存在80端口
fi
testing$(grep :25 ${testfile})
if [ ${testing} ! ] ;thenecho 存在25端口
elseecho 不存在25端口
fi
testing$(grep :22 ${testfile})
if [ ${testing} ! ] ;thenecho 存在22端口
elseecho 不存在22端口
fi
testing$(grep :21 ${testfile})
if [ ${testing} ! ] ;thenecho 存在21端口
elseecho 不存在21端口
fi
[rootchenshiren shelldir]# sh netstat.sh
不存在80端口
不存在25端口
存在22端口
不存在21端口例题2 条件判断式还可以搞的更复杂。举例来说每当过年的时候都能收到压岁钱写个脚本每当我输入过年的时间的时候都会提示我还有几个月过年。
先让用户输入过年的时间用现在的时间对比过年时间计算出离过年还剩下多少天
可以利用【date --date“YYYYMMDD”%s】转成秒数后就很容易操作了
[rootchenshiren shelldir]# vim guonian.sh
#!/bin/bash
# 说明:
# 用户输入一个时间计算还需要多久过年
# 时间: 2024/3PATH${PATH}:/tmp/shelldir
export PATH
declare -i date_xianzai1$(date %Y%m%d)
read -p 请输入准确的过年时间: time
date_pd$( echo ${time} |grep -E ^[0-9]{8}$)
if [ $date_pd ];thenecho -e \n请输入正确时间\aexit 1
elif [ $date_pd -lt $date_xianzai1 ];thenecho -e \n请输入正确时间\aexit 1
fideclare -i date_shuru$(date --date${time} %s)
declare -i date_xianzai$(date %s)
declare -i date_js$(( ${date_shuru}-${date_xianzai} ))
declare -i date_js_hs$(( ${date_js} /60/60/24))
declare -i date_js_hours$(( ${date_js} /60/60%24 ))
echo -e 离过年还有${date_js_hs}天${date_js_hours}小时
[rootchenshiren shelldir]# sh guonian.sh
请输入准确的过年时间:20250129
离过年还有318天5小时上面的程序可以计算离过年还剩下多少天。其中的【${date_hsstdin}】变量中的 /60/60/24 就是将秒数换算成天数。
%24这个符号是取模运算符表示取除以24一天的小时数的余数
利用case…esac判断
上面提到的【if … then … fi】对于变量的判断是以【比对】的方式来分辨的如果符合状态就进行某些操作并且通过较多层次(就是 elif …)的方式来进行多个变量的程序代码编写。【case…esac】语句用于对变量或参数多重比较如果匹配成功就执行一段语句否则执行其他语句
它的语法格式如下
case $变量名称 in # 关键字为case还有变量前面有美元符号第一个变量内容) # 每个变量内容建议用双引号括起来关键字则为右圆括号程序段 ;; # 每个类别结尾使用两个连续的分号来处理第二个变量内容)程序段;;*) # 最后一个变量内容都会用*来代表所有其他值exit 1;;
esac # 最终的case结尾是【case】反过来写。来修改以下上述的例题1他应该会变成这样
#!/bin/bash
PATH${PATH}:/tmp/shelldir
export PATHcase ${1} inhello)echo hello,how are you?;;)echo 你没有加hello参数;;*)echo 你参数输入错了;;
esac执行结果
[rootchenshiren shelldir]# sh howareyou.sh
你没有加hello参数
[rootchenshiren shelldir]# sh howareyou.sh hello
hello,how are you?
[rootchenshiren shelldir]# sh howareyou.sh dadaadc
你参数输入错了例题1 让用户输入one、two、three并且将用户的变量显示到屏幕上如果输入的不是one、two、three就告诉用户只有这三种选择
这个是直接执行式
[rootchenshiren shelldir]# vim ots.sh
#!/bin/bash
# 说明: 直接执行程序程序后面只能跟one、two、three三个参数
# 时间: 2024/3
PATH${PATH}:/tmp/shelldir
export PATHcase ${1} inone)echo 这是参数one;;two)echo 这是参数two;;three)echo 这是参数three;;esac
[rootchenshiren shelldir]# sh ots.sh one
这是参数one
[rootchenshiren shelldir]# sh ots.sh two
这是参数two
[rootchenshiren shelldir]# sh ots.sh three
这是参数three这个是交互式
[rootchenshiren shelldir]# vim ots.sh
read -p 输入参数: arg
#!/bin/bash
# 说明: 用户输入一个参数程序判断有没有这个参数
# 时间: 2024/3
PATH${PATH}:/tmp/shelldir
export PATHread -p 输入参数: arg
case ${arg} inone)echo 这是参数one;;two)echo 这是参数two;;three)echo 这是参数three;;*)echo -e 请输入正确的参数\a;;esac
[rootchenshiren shelldir]# sh ots.sh
输入参数: 0
请输入正确的参数
[rootchenshiren shelldir]# sh ots.sh
输入参数: one
这是参数one利用function功能
什么是【函数(function)】功能简单来说其实函数可以在shell脚本当中做出一个类似自定义执行命令的东西最大的功能就是可以简化我们很多的程序代码。举例来说上面的案例中每个输入结果one、two、three其实输出的内容都一样那么我就可以使用function来简化
function的语法是这样的
function fname () {程序段
}那个fname就是我们自定义的执行命令名称而程序段就是我们要它执行的内容了。要注意的是shell脚本的执行方式是由上到下从左至右因此在shell脚本当中的function的设置一定要在程序的最前面这样才能够在执行时被找到可用的程序段 例题1 我们将上述的case…esac的例题改一下自定义一个名为printit的函数来使用
[rootchenshiren shelldir]# vim function.sh
#!/bin/bash
# 程序说明
# 使用function来选择参数
# 时间2024/3
PATH${PATH}:/tmp/shelldir
export PATHfunction print () {echo -n 你选择的参数为:
}case ${1} inone)print; echo ${1} | tr a-z A-Z;;two)print; echo ${1} | tr a-z A-Z;;three)print; echo ${1} | tr a-z A-Z;;*)echo -e \n只能选择{one|two|three}参数\a;;
esac上面的例子中声明了一个函数 print 所以当我们在后续的程序段中只要执行print的话就表示我的shell脚本要去执行【function print】里面的那几个程序段。
另外function也拥有内置变量的它的内置变量与shell脚本很类似函数名称$0后续接的变量也是以$1、$2..来替换【function fname () {程序段}】内的$0$1…等与shell脚本的$0是不同的。
以上面的例题1来改变一下进行说明假如我执行【sh function.sh one】表示shell脚本内的$1为one这个字符串但是在print( )内的$1则与这个one无关。
[rootchenshiren shelldir]# vim function.sh
#!/bin/bash
# 程序说明
# 使用function来选择参数
# 时间2024/3
PATH${PATH}:/tmp/shelldir
export PATHfunction print () {echo -n 你选择的参数为: ${1}
}case ${1} inone)print 1;;two)print 2;;three)print 3;;*)echo -e \n只能选择{one|two|three}参数\a;;
esac
[rootchenshiren shelldir]# sh function.sh one
你选择的参数为: 1[rootchenshiren shelldir]# 在上面的例子中如果你输入【sh function.sh one】就会出现【你选择的参数为1】如下图 因为我们在程序段落中我们写了【printit 1】那个1就会成为function当中的$1。
$()和$(())讲解
$()是命令替换的语法将括号内的命令执行后的结果作为一个整体返回。例如
echo $(ls)$(())是算数扩展语法用于执行算术运算。例如
echo $((23))$(( ))内可以使用的算术运算符包括 、-、*、/、%、、、、|、^ 等。而$()内可以执行任何命令包括算术运算。 因此$()用于执行命令替换$(( ))用于执行算术运算。两者的语法和用途不同。
shell脚本的循环
除了if…then…fi 这种条件判断式之外循环可能是程序当中最重要的一环了。循环可以不断地执行某个程序段落直到用户设置的条件完成为止。所以重点是那个【条件的完成】是什么除了这种依据判断式完成与否的不定循环之外还有另外一种已经固定要跑多少次循环状态可称为固定循环状态。
while do done、until do done(不定循环)
一般来说不定循环最常见的就是下面的两种状态了
while [ condition ] # 中括号内的状态就是判断式
do # do是循环的开始程序段
done # done 是循环的结束while的中文是【当…时】所以这种方式说的是【当condition条件成立时就进行循环直到condition的条件不成立才停止】的意思还有另外一种不定循环的方式
until [ condition ]
do程序段
done这种方式恰恰与while相反它说的是【当condition条件成立时就终止循环否则就持续进行循环的程序段】 例题1 假设我要让用户输入yes或是YES才结束程序的执行否则就一直告诉用户输入字符串
#!/bin/bash
# 程序说明
# 输入yes/YES停止输出字符串
# 时间
# 2024/03
PATH${PATH}:/tmp/shelldir
export PATH
while [ ${yn} ! yes -a ${yn} ! YES ]
doread -p 请输入yes/YES来停止该程序 yn
doneecho -e \n您已经停止了该程序\a上面这个例题当中【当 ${yn} 这个变量不是 “yes” 且 ${yn} 也不是 YES时就进行循环的程序】如果输入了yes 或YES 就退出循环。 例题2 我们改变一下上述案例使用 until do done 的形式循环程序
[rootchenshiren shelldir]# vim yes.sh
#!/bin/bash
# 说明: 当用户输入yes或YES程序停止循环
# 时间: 2024/3
PATH${PATH}:/tmp/shelldir
export PATHuntil [ ${yn} yes -o ${yn} YES ]
doread -p 请输入yes/YES来停止该程序: yn
doneecho -e \n您已经停止了该程序\a上面这个例题当中【当 ${yn} 这个变量是YES 或是 “yes” 就退出循环】否则就持续进行循环。 例题3 如果我要计算12…100的结果呢利用循环
[rootchenshiren shelldir]# vim 100.sh
#!/bin/bash
# 说明: 求1...100的和
# 时间: 2024/3
PATH${PATH}:/tmp/shelldir
export PATH
s0
i0
while [ ${i} ! 100 ]
doi$(( $i1 ))s$(( $s$i ))
done echo 12...100${s}当你的执行结果为5050这个数据时就对了。 例题4 如果让用户自行输入一个数字让程序123…加到你输入的数字为止该如何编写呢
[rootchenshiren shelldir]# vim 100.sh
#!/bin/bash
# 说明: 输入一个数字该程序将从1到你输入的这个数字为止
# 时间: 2024/3
PATH${PATH}:/tmp/shelldir
export PATH
read -p 请输入一个数字: number
s0
i0
while [ ${i} ! ${number} ]
doi$(( $i1 ))s$(( $s$i ))
doneecho 12...加到你输入的这个数字和为${s}
[rootchenshiren shelldir]# sh 100.sh
请输入一个数字:20000
12...加到你输入的这个数字和为200010000是不是很简单也可以使用until do done 来测试一下
for…do…done(固定循环)
相对于while、until的循环方式是必须要【符合某个条件】的状态for这种语法则是【已经知道要进行几次循环】的状态
它的语法为
for var in con1 con2 con3 .....
do程序段
done以上面的例子来说这个${var}的变量内容在循环工作时
第一次循环时${var} 的内容为 con1第二次循环时${var} 的内容为 con2第三次循环时${var} 的内容为 con3…… 例题1 假设我有3种动物分别是dog、cat、sheep()我想每一行都输出这样【There are dogs…】之类的字样
#!/bin/bash
# 程序说明
# 该程序判断动物园有哪些动物
# 时间
# 2024/3
PATH${PATH}:/tmp/shelldir
export PATH
echo -e 今天的动物园有哪些动物呢
for animal in dog cat tiger
doecho 有${animal}s
done例题2 如果我想要找到/etc/passwd 内的第一个字段能不能通过管道命令的cut识别出单纯的账号名称后以id分别检查用户的标识符与特殊参数(id 用户名)
#!/bin/bash
# 程序说明
# 该程序识别passwd的账户名称后用id 账户名称查看用户标识符与特殊参数
# 时间
# 2024/3
PATH${PATH}:/tmp/shelldir
export PATH
cutpasswd$(cut -d : -f1 /etc/passwd ) # 选取账号名称
for username in ${cutpasswd} # 开始循环
doid ${username}
done执行结果如下
[rootlocalhost shelldir]# sh userid.sh
uid0(root) gid0(root) 组0(root)
uid1(bin) gid1(bin) 组1(bin)
uid2(daemon) gid2(daemon) 组2(daemon)
uid3(adm) gid4(adm) 组4(adm)
uid4(lp) gid7(lp) 组7(lp)
uid5(sync) gid0(root) 组0(root)
uid6(shutdown) gid0(root) 组0(root)
uid7(halt) gid0(root) 组0(root)
uid8(mail) gid12(mail) 组12(mail)
uid11(operator) gid0(root) 组0(root)
uid12(games) gid100(users) 组100(users)
.....
...例题3 假如我利用ping这个可以判断网络状态的命令来进行网络状态的实际检测时我想要检测的域名是本机所在的192.168.124.1~192.168.124.100网段1~100 总不会我在for后面输入 1到100吗
[rootchenshiren shelldir]# vim ip_ping.sh
#!/bin/bash
# 说明: 检测网段
# 时间: 2024/3
PATH${PATH}:/tmp/shelldir
export PATHnetwork192.168.200
for jcwd in $(seq 1 100)
do ip${network}.${jcwd}ping -c 1 -w 1 ${ip} /dev/null 21if [ $? -eq 0 ];thenecho ${ip}通else echo ${ip}不通fi
done # 和done# seq 1 100是一个用于生成从1到100的整数序列的命令例题4 我想要让用户输入某个目录文件名然后我找出某目录内的文件名权限应该怎么做呢
[rootchenshiren shelldir]# vim cat_rwx.sh
do
#!/bin/bash
# 说明: 查看目录下的文件权限
# 时间: 2024/3
PATH${PATH}:/tmp/shelldir
export PATH
read -p 输入目录或文件名: dir
if [ ${dir} -o ! -d ${dir} ];thenecho 该${dir}目录不存在exit 1
fi
filelist$(ls ${dir})
for filename in ${filelist}
do test -r ${dir}/${filename} perm可读test -w ${dir}/${filename} perm${perm},可写test -x ${dir}/${filename} perm${perm},可执行echo 这个文件${dir}/${filename}本机用户拥有的权限是${perm}
donefor…do…done的数值处理
除了上述方法之外for循环还有另外一种写法语法如下
for (( 初始值; 限制值; 赋值运算 ))
do 程序段
done这种语法适合于数值方面的运算当中for后面括号内的三串内容意义是
初始值某个变量在循环当中的起始值直接类似 i1 设置好;限制值当变量的值在这个限制值的范围内就继续进行循环例如 i100赋值运算每做一次循环变量也变化例如ii1
值得注意的是在【赋值运算】的设置上如果每次增加1则可以使用类似【i】的方式就是i每次循环都会增加1的意思。 例题 从1累加到用户输入的数值
[rootchenshiren shelldir]# vim user_number.sh
#!/bin/bash
# 说明: 从1累加到用户输入的数值
# 时间: 2024/3
PATH${PATH}:/tmp/shelldir
export PATHread -p 请输入一个数值: numbers0
for (( i1; i${number}; ii1 ))
dos$(($s$i))
doneecho 12....${number}${s}搭配随机数与数组的实验
说到随机数肯定会用到系统给我提供的这个变量${RANDOM}
${RANDOM} 是一个 Bash 内置的环境变量用于生成一个随机整数。每次调用 ${RANDOM} 时都会生成一个 0 到 32767之间的随机整数。 可以使用以下方式来获取 ${RANDOM} 的值
echo ${RANDOM}也可以将 ${RANDOM} 的值赋值给变量
my_random${RANDOM}
echo ${my_random}由于 ${RANDOM} 只是一个环境变量所以它的值只在当前 Shell 进程中有效。如果需要在脚本中生成多个随机数可以在需要的地方调用 ${RANDOM}。 例题1 假如你在家你不知道吃什么饭选择困难就很烦那你就可以写个脚本脚本搭配随机数来告诉你今天中午吃啥好执行这个脚本后直接跟你说要吃什么。
应该怎么做呢首先你得要将全部的店家输入到一组数组当中再通过随机数的处理去获取可能的数值再将搭配到的数值显示出来即可。
#!/bin/bash
# 程序说明
# 打印今天中午吃什么饭
# 时间
# 2024/3
PATH${PATH}:/tmp/shelldir
export PATH
eat[1]红烧肉 # 定义一个数组
eat[2]糖醋排骨
eat[3]小炒牛肉
eat[4]小炒五花肉
eat[5]平菇炒香干
eat[6]香菇炒芹菜
eat[7]喝西北风
eat[8]奥里给
eat[9]泡面
eatnum9 # 定义变量9表示午餐可选的菜品
check$(( ${RANDOM} * ${eatnum} /32767 1)) # 通过随机数计算出今天中午吃什么菜
echo 你中午吃${eat[${check}]}上面案例中最重要的就是随机数了【${RANDOM} * ${eatnum} /32767 1 】计算出 check 变量的值${RANDOM} 表示 Bash 内置的环境变量用于生成一个随机整数每次调用 ${RANDOM} 时都会生成一个 0 到 32767 之间的随机整数这里将其乘以菜品数量再除以 32767最后加 1得到一个 1 到 9 之间的随机整数。
当我们执行上述案例时就知道自己要吃啥了非常的方便。 例题2 那么如果我想吃3个菜呢而且不能重复一样的那应该怎么做
#!/bin/bash
PATH${PATH}:/tmp/shelldir
export PATH
eat[1]红烧肉
eat[2]糖醋排骨
eat[3]小炒牛肉
eat[4]小炒五花肉
eat[5]平菇炒香干
eat[6]香菇炒芹菜
eat[7]喝西北风
eat[8]奥里给
eat[9]泡面
eatnum9
eated0
while [ ${eated} -lt 3 ]; do check$(( ${RANDOM} * ${eatnum} /327671 ))mycheck0if [ ${eated} -ge 1 ]; thenfor i in $(seq 1 ${eated})doif [ ${eatedcon[$i]} $check ]; thenmycheck1fidonefiif [ ${mycheck} 0 ]; thenecho 你可以吃${eat[${check}]}eated$((${eated} 1 ))eatedcon[${eated}]${check}fi
done代码解释
这段脚本用于随机选择三种食物输出供用户选择。脚本中的变量和数组含义如下 PATH环境变量指定可执行文件的搜索路径。 eat数组包含九种食物。 eatnum变量表示数组元素个数。 eated变量表示已经选择的食物数量初始值为0。 check变量用于存储随机选择的食物在数组中的索引。 mycheck变量用于判断已经选择的食物中是否已经包含了当前选中的食物。 eatedcon数组用于存储已经选择的食物在数组中的索引。
while循环中的逻辑如下 当已经选择的食物数量小于3时进行循环。 生成一个随机数check表示在数组中的索引。 判断当前选中的食物是否已经被选择过如果是则跳过否则输出当前选中的食物并将eated加1同时将选中的食物在数组中的索引存入eatedcon数组中。
执行结果
[rootk8s-master-node1 shelldir]# sh noon_what_eat.sh
你可以吃奥里给
你可以吃平菇炒香干
你可以吃糖醋排骨shell脚本的跟踪与调试
脚本文件在执行之前最怕就是出现语法错误的问题。那么我们如何调试呢有没有办法不需要通过直接执行脚本文件就可以判断是否有问题我们直接用bash的相关参数
sh [-nvx] scripts.sh
选项与参数
-n不要执行脚本仅查询语法的问题
-v再执行脚本前先将脚本文件的内容输出到屏幕上
-x将使用到的脚本内容显示到屏幕上使用案例 测试dir_perm.sh 有无语法问题
[rootk8s-master-node1 shelldir]# sh -n dir_rwx.sh
# 若没有语法问题则不会显示任何信息将noon_what_eat.sh 的执行过程全部列出来
[rootk8s-master-node1 shelldir]# sh -x noon_what_eat.sh PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/bin:/root/shelldirexport PATHeat[1]红烧肉eat[2]糖醋排骨eat[3]小炒牛肉eat[4]小炒五花肉eat[5]平菇炒香干eat[6]香菇炒芹菜eat[7]喝西北风eat[8]奥里给eat[9]泡面eatnum9eated0[ 0 -lt 3 ]check2
.....
.......