shell语言(十分钟带你学会Shell脚本!) 本篇文章没有太多的理论知识,主要分为基础语法案例、常用工具型命令(重点:帮助我们完成复杂需求)、工作中常见的需求(实战案例有源码,工作中可以直接套用),建议花十分钟阅读一遍收藏即可,当工作中需要编写Shell脚本直接套用案例中的脚本模板,足可满足后端开发的大部分需求。 作为一名后端程序员,如果不掌握基础的Shell脚本,那么运维编写的一些简单的脚本根本无法看懂,也不便于与运维进行沟通交流。掌握Shell,可以帮助我们提高日常工作效率,比如快速构建部署项目、管理集群、监控服务器、定时清理日志文件或管理服务器等等。概述 Shell是由C语言编写而成,外号俗称壳。开发者如果想操作Linux系统内核,必须通过Shell脚本进行交互,解释和执行用户命令,不可以绕过Shell直接操作Linux内核。Shell是一门强大的编程语言,容易上手功能强大。 Shell解析器 Linux中有几种常见的解析器,后面的模板都是使用Bash(最常用的解析器)解析器进行编写,查看当前系统支持哪些解析器:catetcshells 查看当前系统使用的Shell解析器:echoSHELL 基础语法与实操案例Shell变量 对于后台开发者,系统环境变量一定不会陌生,这里不做过多赘述。Shell变量分为两种:系统变量、自定义变量。系统变量 常见的系统变量如下: 变量名 解释 PWD 脚本执行的当前所在目录 UID 当前操作的系统用户ID 当前操作用户的PID 当前脚本的参数个数 当前脚本的所有参数 0 当前执行程序的名称 n 当前程序的第N个参数 HOME 当前程序的home目录 USER 查询当前程序使用的操作用户自定义变量 1。变量命令规则 变量名必须是以字母或下划线字符开头,后面字母、数字或下划线字符。切记不用使用特殊符号,给自己带来不必要的麻烦。 2。查看当前Shell所有的环境变量 3。编写自定义变量变量名值如:A1等号两边不要有空格,如果值中间存在空格,请使用单引或者双引号:A39;张三39;撤销变量unsetA定义静态变量,静态变量不可以二次赋值,静态变量不可以unset撤销readonlyB2 4。变量的作用域 普通的变量作用域为当前的执行程序,程序外部不可使用当前定义的变量。通过export可以把变量升级为全局环境变量,这样当前系统所有程序都可以使用这个环境变量。 创建测试脚本:touchtest。sh 赋值执行权限:chmoduxtest。sh 编写脚本:vimtest。sh 定义全局脚本(脚本内容如下):exportusername张三!binbashechousername 5。由于定义了全局变量,所以执行脚本可以正常输出username变量的值,反之脚本中定义的局部变量,其它脚本中不可以正常输出结果。。test。sh运算符 运算符的种类大致可以分为(直接上代码示例)4种。算数运算符!binbasha10b20加法valexprabechoab:val减法valexprabechoab:val乘法valexprabechoab:val除法valexprbaechoba:val取余valexprbaechoba:val等于if〔ab〕thenechoa等于bfiif〔a!b〕thenechoa不等于bfi关系运算符!binbasha10b20等于if〔aeqb〕thenechoaeqb:a等于belseechoaeqb:a不等于bfi不等于if〔aneb〕thenechoaneb:a不等于belseechoaneb:a等于bfi大于if〔agtb〕thenechoagtb:a大于belseechoagtb:a不大于bfi小于if〔altb〕thenechoaltb:a小于belseechoaltb:a不小于bfi大于等于if〔ageb〕thenechoageb:a大于或等于belseechoageb:a小于bfi小于等于if〔aleb〕thenechoaleb:a小于或等于belseechoaleb:a大于bfi布尔运算符!binbasha10b20!非运算,跟java一样if〔a!b〕thenechoa!b:a不等于belseechoab:a等于bfi与运算,跟java里面的amp;amp;一样if〔alt100abgt15〕thenechoa小于100且b大于15:返回trueelseechoa小于100且b大于15:返回falsefi或运算,与java里面的同理if〔alt100obgt100〕thenechoa小于100或b大于100:返回trueelseechoa小于100或b大于100:返回falsefiif〔alt5obgt100〕thenechoa小于5或b大于100:返回trueelseechoa小于5或b大于100:返回falsefi字符串运算符!binbashaabcbefg判断字符串是否相等if〔ab〕thenechoab:a等于belseechoab:a不等于bfi判断字符串不相等if〔a!b〕thenechoa!b:a不等于belseechoa!b:a等于bfin判断字符串长度是否不为0if〔na〕thenechona:字符串长度不为0elseechona:字符串长度为0fi与n相反if〔za〕thenechoza:字符串长度为0elseechoza:字符串长度不为0fi表示检查字符串是否为空if〔a〕thenechoa:字符串不为空elseechoa:字符串为空fi流程控制 ifelse不再做介绍,上述运算符案例中有大量使用,对于后端开发及其简单,流程控制在程序用使用非常频繁。case语法直接套用 最后的)表示默认模式,相当于Java中的default,;;表示命令序列结束,相当于Java中的break。!binbashcase1in1)echo张三;;2)echo李四;;)echo王二;;esacfor循环 案例:从1加到100。!binbashs0for((i0;ilt;100;i))dos〔si〕doneechoswhile循环 案例:从1加到100。!binbashs0i1while〔ile100〕dos〔si〕i〔i1〕done输出值echos函数 Shell脚本和其它编程语言类似,分为系统函数和自定义函数。系统函数 1。basename基本语法basename路径后缀 功能描述:basename命令会删掉所有的前缀包括最后一个(‘’)字符,然后将字符串显示出来。 不加后缀: 加后缀: 如果脚本中需要获取当前路径的后缀名称: 2。dirname基本语法dirname文件绝对路径 功能描述:从给定的包含绝对路径的文件名中去除文件名(非目录的部分),然后返回剩下的路径(目录的部分)。 自定义函数 1。基本语法:〔function〕funname〔()〕{Action;〔returnint;〕} 2。经验技巧 必须在调用函数地方之前,先声明函数,Shell脚本是逐行运行。不会像其它语言一样先编译。 函数返回值,只能通过?系统变量获得,可以显示加return返回,如果不加,将以最后一条命令运行结果,作为返回值。return后跟数值n(0255)。 3。案例实操 函数无返回值:计算两个输入参数的和。 脚本源码:!binbashfunctionsum(){s0s〔12〕echos}read读取控制台的输入,n1,n2用于接收输入内容,p:指定读取值时的提示符;t:指定读取值时等待的时间(秒)readpPleaseinputthenumber1:n1;readpPleaseinputthenumber2:n2;调用方法sumn1n2; 函数有返回值:计算两个输入参数的和(函数返回值,只能通过?系统变量获得)。 !binbashfunctionsum(){read读取控制台的输入,n1,n2用于接收输入内容,p:指定读取值时的提示符;t:指定读取值时等待的时间(秒)readpPleaseinputthenumber1:n1;readpPleaseinputthenumber2:n2;return((n1n2))}调用方法sumecho计算两个数字之和为?!常用的Shell工具 下面列举的几个命令非常实用,命令的具体使用方法请阅读:Linux命令大全,非常重要且命令参数太多,这里不做过多赘述。 awk:非常强大的文本分析功能,开发中使用非常频繁。 sort:对文件进行排序,并将标准结果显示输出。 sed:sed是一种流编辑器,一次处理一行内容。 cut:主要用于剪切字符、字节,并输出结果。开箱即用的Shell脚本 请用Shell脚本写出查找当前文件夹(home)下所有的文本文件内容中包含有字符shen的文件名称。greprshenhomecutd:f1 判断用户输入的是否为IP地址:!binbashfunctioncheckip(){IP1VALIDCHECK(echoIPawkF。39;1lt;255amp;amp;2lt;255amp;amp;3lt;255amp;amp;4lt;255{printyes}39;)ifechoIPgrepE〔09〕{1,3}。〔09〕{1,3}。〔09〕{1,3}。〔09〕{1,3}gt;devnull;thenif〔VALIDCHECKyes〕;thenechoIPavailable。elseechoIPnotavailable!fielseechoFormaterror!fi}checkip192。168。1。1checkip256。1。1。1 定时清空文件内容,定时记录文件大小:!binbash每小时执行一次脚本(任务计划),当时间为0点或12点时,将目标目录下的所有文件内容清空,但不删除文件,其他时间则只统计各个文件的大小,一个文件一行,输出到以时间和日期命名的文件中,需要考虑目标目录下二级、三级等子目录的文件logfiletmpdateHF。logndateHif〔neq00〕〔neq12〕then通过for循环,以find命令作为遍历条件,将目标目录下的所有文件进行遍历并做相应操作foriinfinddatalogtypefdotruegt;idoneelseforiinfinddatalogtypefdodushigt;gt;logfiledonefi 检测网卡流量,并按规定格式记录在日志中:!binbash检测网卡流量,并按规定格式记录在日志中规定一分钟记录一次日志格式如下所示:2019081220:40ens33input:1234bpsens33output:1235bps3while:do设置语言为英文,保障输出结果是英文,否则会出现buglogfiletmpdated。log将下面执行的命令结果输出重定向到logfile日志中execgt;gt;logfiledateFH:Msar命令统计的流量单位为kbs,日志格式为bps,因此要10008sarnDEV159grepAveragegrepens33awk39;{print2,t,input:,t,510008,bps,n,2,t,output:,t,610008,bps}39;echo因为执行sar命令需要59秒,因此不需要sleepdone 计算文档每行出现的数字个数,并计算整个文档的数字总数:!binbash计算文档每行出现的数字个数,并计算整个文档的数字总数使用awk只输出文档行数(截取第一段)nwcla。txtawk39;{print1}39;sum0文档中每一行可能存在空格,因此不能直接用文档内容进行遍历foriinseq1ndo输出的行用变量表示时,需要用双引号linesednipa。txtwcL选项,统计最长行的长度nnecholineseds39;〔09〕39;gwcLechonnsum〔sumnn〕doneechosum:sum 杀死所有脚本:!binbash有一些脚本加入到了cron之中,存在脚本尚未运行完毕又有新任务需要执行的情况,导致系统负载升高,因此可通过编写脚本,筛选出影响负载的进程一次性全部杀死。psauxgrep指定进程名grepvgrepawk39;{print2}39;xargskill9 从FTP服务器下载文件:!binbashif〔ne1〕;thenechoUsage:0filenamefidir(dirname1)file(basename1)ftpnvlt;lt;EOFn自动登录open192。168。1。10ftp服务器useradminpasswordbinary设置ftp传输模式为二进制,避免MD5值不同或。tar。gz压缩包格式错误cddirgetfileEOF 监测Nginx访问日志404情况:场景:1。访问日志文件的路径:datalogaccess。log2。脚本死循环,每10秒检测一次,10秒的日志条数为300条,出现404的比例不低于10(30条)则需要重启phpfpm服务3。重启命令为:etcinit。dphpfpmrestart!binbash监测Nginx访问日志404情况,并做相应动作logdatalogaccess。logN30设定阈值while:do查看访问日志的最新300条,并统计404的次数errtailn300loggrepc39;40439;if〔errgeN〕thenetcinit。dphpfpmrestart2gt;devnull设定60s延迟防止脚本bug导致无限重启phpfpm服务sleep60fisleep10done iptables自动屏蔽访问网站频繁的IP 方法1:根据访问日志(Nginx为例)。!binbashDATE(datedbY:H:M)ABNORMALIP(tailn5000access。loggrepDATEawk39;{a〔1〕}END{for(iina)if(a〔i〕gt;100)printi}39;)先tail防止文件过大,读取慢,数字可调整每分钟最大的访问量。awk不能直接过滤日志,因为包含特殊字符。forIPinABNORMALIP;doif〔(iptablesvnLgrepcIP)eq0〕;theniptablesIINPUTsIPjDROPfidone 方法2:通过TCP建立的连接。!binbashABNORMALIP(netstatanawk39;4:80amp;amp;6ESTABLISHED{gsub(:〔09〕,,5);{a〔5〕}}END{for(iina)if(a〔i〕gt;100)printi}39;)gsub是将第五列(客户端IP)的冒号和端口去掉forIPinABNORMALIP;doif〔(iptablesvnLgrepcIP)eq0〕;theniptablesIINPUTsIPjDROPfidone Expect实现SSH免交互执行命令:登录脚本:catlogin。exp!usrbinexpectsetip〔lindexargv0〕setuser〔lindexargv1〕setpasswd〔lindexargv2〕setcmd〔lindexargv3〕if{argc!4}{putsUsage:expectlogin。expipuserpasswdexit1}settimeout30spawnsshuseripexpect{(yesno){sendyesr;expcontinue}password:{sendpasswdr}}expectuser{sendcmdr}expectuser{sendexitr}expecteof 执行命令脚本:写个循环可以批量操作多台服务器。!binbashHOSTINFOuserinfo。txtforipin(awk39;{print1}39;HOSTINFO)douser(awkvIip39;I1{print2}39;HOSTINFO)pass(awkvIip39;I1{print3}39;HOSTINFO)expectlogin。expipuserpass1done Linux主机SSH连接信息:catuserinfo。txt192。168。1。120root123456 创建10个用户,并分别设置密码,密码要求10位且包含大小写字母以及数字,最后需要把每个用户的密码存在指定文件中:!binbash创建10个用户,并分别设置密码,密码要求10位且包含大小写字母以及数字最后需要把每个用户的密码存在指定文件中前提条件:安装mkpasswd命令生成10个用户的序列(0009)foruinseqw009do创建用户useradduseru生成密码pmkpasswds0l10从标准输入中读取密码进行修改(不安全)echoppasswdstdinuseru常规修改密码echoepnppasswduseru将创建的用户及对应的密码记录到日志文件中echouserupgt;gt;tmpuserpassworddone 扫描主机端口状态:!binbashHOST1PORT2225808080forPORTinPORT;doifechoamp;gt;devnullgt;devtcpHOSTPORT;thenechoPORTopenelseechoPORTclosefidone用Shell打印示例语句中字母数小于6的单词示例语句:Bashalsointerpretsanumberofmulticharacteroptions。!binbashShell打印示例语句中字母数小于6的单词forsinBashalsointerpretsanumberofmulticharacteroptions。donechoswccif〔nlt6〕thenechosfidone