您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
Shell在日常工作中的应用实践
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
Shell在日常工作中的应用实践
自猿其说Tech
2022-02-22
IP归属:未知
16880浏览
测试
### 1 Shell可以帮我们做什么 作为一名测试开发工程师,在与linux服务器交互过程中,大都遇到过以下这些问题: 1. 一次申请多台服务器,多台服务器需要安装相同软件,配置相同的环境,同样的操作需要重复多次; 1. 工作中经常会使用命令行命令来完成我们的一些操作,但是有些命令使用率很高,而且很长,每次都全部敲进去势必会浪费很多时间(比如查日志) 长此以往,以上两个问题可能会导致:重复性工作,个人能力得不到任何提高,浪费时间,而且还容易出错,作为一名技术人员,当同一个操作重复了三次,我们就应该考虑是否可以通过工具来帮我们实现。 而shell脚本正好擅长,把复杂问题分解成简单的小问题,然后再把各个部分功能组合起来解决复杂问题。 当然,有些命令我们只能节省三五秒的时间,短期看不到时间的节省,但是从长期来看这个价值将是巨大的。我们可以将这些时间专注于更有意义,更重要的事情 - 提高生命质量。 ### 2 功能介绍 Shell脚本语言的优势在于处理偏操作系统底层的业务,例如,Linux系统内部很多应用是用shell脚本语言开发的,因为有众多的Linux系统命令为它作支撑,特别是Linux正则表达式和三剑客grep、awk、sed等命令。 对于一些常见的系统脚本,使用shell开发会更简单、更快速。就像让软件一键自动化安装、优化,监控报警脚本,软件启动脚本,日志分析脚本等,虽然PHP和Python语言也能做到这些,但是由于掌握难度、开发效率和开发习惯等,可能就不如shell脚本语言流行及有优势了。 shell是一个C语言编写的脚本语言,它是用户与linux的桥梁,用户输入命令交给shell来处理,shell将相应的操作传递给内核,内核把处理的结果输出给用户。 可参考如下流程示意图: ![](//img1.jcloudcs.com/developer.jdcloud.com/44eba410-0702-4c39-a70b-e81e006b7d1220220222142925.png) 简单来说:shell就是一个用户跟操作系统之间交互的命令解释器 ### 3 基本用法 下面首先来介绍下日常使用较多的基本指令: #### 3.1 文件拷贝 ``` cp [选项] 源文件目录 目标文件目录 常用选项 -a --archive 保留链接、文件属性,并递归地拷贝目录,其作用等于dpR选项的组合。 -b 为每个已存在的目标文件创建备份 -f --force 强制复制文件或目录, 不论目的文件或目录是否已经存在 -i --interactive 覆盖文件之前先询问用户 -v --verbose 显示执行过程 ``` #### 3.2 文件合并 有时候,要遇到将多个文件合并成一个的需求,除了重复的复制和粘贴,还可以通过简单指令来辅助实现。 **cat命令** 默认地,cat命令可以直接接收多个参数,这样,通过重定向可以很方便地合并文件: 效果如下: ``` cat aa.txt bb.txt >>all.txt ``` ![](//img1.jcloudcs.com/developer.jdcloud.com/9c9e867d-5f5a-4add-bb5e-f728650313c820220222143339.png) ### 4 案例分享 #### 4.1 自动添加注释 下面我们从日常工作入手,和大家一起来看下,shell有哪些提高工作效率的应用场景~ 首先,我们编写的脚本有时候并不仅仅是我们个人使用,可能是团队内部使用,所以通常在脚本正式编写前,通常需要添加部分注释,注明脚本的作用,创建日期,创建人等信息; 而如果编写脚本比较多的话,这些重复性工作就会成为我们的负担,所以,我们又可以将这些交给shell来帮我们实现,用shell脚本来解决创建shell脚本的重复性工作; 下面给大家分享一个shell脚本的模板文件,把它拷贝到用户的根目录下并命名成 .vimrc 名称,这样我们以后再次打开以 .sh结尾的文件时就会成自动生成一些注释信息,只要稍稍修改一下即可如图所示: vimrc文件是vim的环境设置文件,在启动vim时,当前用户根目录下的.vimrc文件会被自动读取,该文件可以包含一些设置甚至脚本,所以,一般会在.vimrc文件中根据个人喜好进行一些自定义设置; 下面脚本我都注释了具体含义,可参考: ``` set paste #解决粘贴乱序问题 set ignorecase #不区分大小写 set cursorline #设置高亮当前行 set autoindent #自动缩进对齐 autocmd BufNewFile *.sh exec ":call SetTitle()" #如果是.sh文件,自动执行下面的函数 func SetTitle() if expand("%:e") == 'sh' call setline(1,"#!/bin/bash") call setline(2,"#") call setline(3,"#********************************************************************") call setline(4,"#Author: simba") call setline(5,"#Date: ".strftime("%Y-%m-%d")) call setline(6,"#Description: The test script") call setline(7,"#Copyright (C): ".strftime("%Y")." All rights reserved") call setline(8,"#********************************************************************") call setline(9,"") endif endfunc autocmd BufNewFile * normal G #自动将光标设置到末尾 ``` 实现效果展示: ![](//img1.jcloudcs.com/developer.jdcloud.com/86b505ee-d846-4eb8-92c6-ebb43a17d11220220222143521.png) #### 4.2 内存使用率监控预警 日常工作中,经常会遇到由于服务器被日志打满而不得不重启的场景,而对于一些必要的单据,就需要时常注意日志的备份,所以可以编写如下一个脚本,来做提醒; 如下脚本实现了,当服务器内存使用率超过90%时,提示保存日志操作,如果未超过90%,则不作任何处理; ``` #!/bin/bash # #******************************************************************** #Author: liguangxin #Date: 2021-12-10 #Description: 内存使用率监控预警 #Copyright (C): 2021 All rights reserved #******************************************************************** BB=`cat /proc/meminfo|head -3|awk '{print $2}'|paste -s|awk '{printf ("%.2f%\n",100*($1-$2)/$1)}'` BBD=`cat /proc/meminfo|head -3|awk '{print $2}'|paste -s|awk '{printf ("%.0f\n",10000*($1-$2)/$1)}'` if [ $BBD -gt 9000 ];then echo -e "\t\t\t\t\033[33m当前内存使用率 $BB,请注意备份日志~ \033[0m\n" else echo fi ``` 实现效果展示: ![](//img1.jcloudcs.com/developer.jdcloud.com/7116df4c-c920-4b10-b89b-8e956c996a2820220222143610.png) #### 4.3 后台服务启动/停止/重启脚本 对于一些小范围使用,暂未接入j-one部署的服务器,每次后台服务代码更新,代码部署都是一项比较繁琐的工作,而这就到了shell发挥威力的时候了。 实现原理就是,将停止服务和启动服务需要执行的命令写进脚本,通过shell交互来实现,外加一些必要的判断逻辑,比如: - 执行启动服务时判断服务是否正在启动中; - 执行停止服务时判断根据交互条件筛选出的服务是否只有一个; - 执行重启服务时判断服务是否正在启动中; - 保证中间所有交互过程中的唯一性,避免失误将其他人的服务停止 实现脚本如下: ``` #!/bin/bash # #******************************************************************** #Author: liguangxin #Date: 2021-12-10 #Description: 万能启动、停止、重启脚本 #Copyright (C): 2021 All rights reserved #******************************************************************** clear echo -e "\n\n" echo -e "请输入您要执行的任务类型:\n" read -p "1、停止服务; 2、启动服务 3、重启服务 : " ACTION if [ $ACTION == 1 ];then ps -ef|grep java|grep -v jdk|grep -v grep|awk '{print $2, $10}'|column -t read -p "请输入要杀死的进程号:" target taskdes=`ps -ef|grep java|grep $target|wc -l` if [ $taskdes -gt 0 ];then QD=`ps -ef|grep $target|grep -v grep|awk '{print $10}'|cut -f4- -d "/"` QDWC=`ps -ef|grep $target|grep -v grep|awk '{print $10}'|cut -f4- -d "/"|wc -l` if [ $QDWC -gt 1 ];then echo "输入有误,查询到多个后台服务,请执行脚本重新输入~" else read -p "$QD 服务正在运行中,请再次确认是否执行kill操作y/n:" T if [ $T == y ];then echo "请注意,即将执行kill进程操作" TK=`ps -ef|grep java|grep $target|awk '{print $10}'` sleep 1s echo "$TK已被杀死" kill -9 $target echo "后台服务已停止" elif [ $T == n ];then echo "任务结束,未执行任何操作~" else echo "输入错误,请重新执行脚本~" fi fi else echo "未查到您输入的进程号,请重新执行脚本~" fi elif [ $ACTION == 3 ];then ls -l /export/kit/ read -p "请输入要重启包的关键字(有区分度即可):" QQ NUM=`ls -l /export/kit/|grep $QQ|wc -l` if [ $NUM == 0 ];then echo "抱歉,服务器上没有您要查询的包,请先上传jar包~" elif [ $NUM == 1 ];then KL=`ps -ef|grep java |grep $QQ|awk '{print $2}'` QD=`ls -l /export/kit/|grep $QQ|awk '{print $9}'` NN=`ls -l /export/kit/|grep $QQ|awk '{print $9}'|cut -f1-3 -d "."` taskdes=`ps -ef|grep java|grep $QQ|wc -l` if [ $taskdes == 1 ];then kill -9 $KL setsid java -jar /export/kit/$QD >> /export/kit/logs/$NN.log & sleep 1s taskdesp=`ps -ef|grep java|grep $QQ|wc -l` if [ $taskdesp -ne 0 ];then echo "$QD 服务重启成功,脚本执行结束~" echo -e "\n" else echo "$QD 服务启动失败,请查看启动日志~" fi elif [ $taskdes -gt 1 ];then echo "输入关键字不具有唯一性,请重新执行脚本~" else echo "服务未启动,请重新执行脚本启动服务~" fi else echo "根据关键字查询到多个jar包,请启动脚本重新输入" fi elif [ $ACTION == 2 ];then ls -l /export/kit/ read -p "请输入要启动包的关键字(有区分度即可):" QQ NUM=`ls -l /export/kit/|grep $QQ|wc -l` if [ $NUM == 0 ];then echo "抱歉,服务器上没有您要查询的包,请先上传jar包~" elif [ $NUM == 1 ];then KL=`ps -ef|grep java |grep $QQ|awk '{print $2}'` QD=`ls -l /export/kit/|grep $QQ|awk '{print $9}'` NN=`ls -l /export/kit/|grep $QQ|awk '{print $9}'|cut -f1-3 -d "."` taskdes=`ps -ef|grep java|grep $QQ|wc -l` if [ $taskdes -gt 0 ];then sleep 1s echo "$QD 服务运行正常,无需重复启动~" else setsid java -jar /export/kit/$QD >> /export/kit/logs/$NN.log & >/dev/null sleep 2s taskdesp=`ps -ef|grep java|grep $QQ|wc -l` if [ $taskdesp -ne 0 ];then echo "$QD 服务启动成功,脚本执行结束~" echo -e "\n" else echo "$QD 服务启动失败,请查看启动日志~" fi fi else echo "输入关键字不具有唯一性,请重新执行脚本~" fi else echo "输入错误,请执行脚本重新输入 ``` 实现效果展示: 该脚本可在团队内部通用,只需要输入任务类型编号和对应任务关键字两步即可,脚本在执行过程中会自动提示你确认执行任务对象是否正确,以免误伤; ###### 1.停止服务 ![](//img1.jcloudcs.com/developer.jdcloud.com/4358ce1b-01e8-42ce-8d29-8facb833055020220222143741.png) ###### 2.启动服务: ![](//img1.jcloudcs.com/developer.jdcloud.com/bbc8ba42-59ad-470d-a73f-3cc7bcb6176120220222143800.png) ###### 3.重启服务 ![](//img1.jcloudcs.com/developer.jdcloud.com/c64001ce-586b-4c62-b85c-e040080c1e2120220222143819.png) 大大减少了团队服务部署的工作量~ #### 4.4 函数化封装 对于一些比较复杂的功能,或者需要多次执行的功能,shell也支持将功能封装为函数,直接执行函数即可; 比如服务器的部署基本可以分为以下几个步骤: - 服务部署目录创建 - 服务配置部署 - 服务应用部署 - 服务应用启动 则该四个步骤可以通过shell脚本封装为四个函数,如下: ``` #!/bin/bash # #******************************************************************** #Author: liguangxin #Date: 2021-12-13 #Description: The test script #Copyright (C): 2021 All rights reserved #******************************************************************** clear #定义安装目录、及日志信息 . /etc/init.d/functions [ $(id -u) != "0" ] && echo "Error: You must be root to run this script" && exit 1 #smartops根目录,根据自己实际情况自定义配置即可 smartops_dir=/*** #smartops 配置文件目录,根据自己实际情况自定义配置即可 smartops_conf=/*** #smartops 日志目录,根据自己实际情况自定义配置即可 smartops_log=/*** #smartops pid目录,根据自己实际情况自定义配置即可 smartops_pid=/*** #安装日志目录 log_dir=APP.log #传入内容,格式化内容输出。 output_msg() { for msg in $*;do action $msg /bin/true done } #服务部署目录创建 create_dir() { output_msg "服务目录创建" for dirname in $*;do mkdir -p $dirname >/dev/null 2>&1 chown smartops.smartops -R ${dirname} echo "`date +%F' '%H:%M:%S` $dirname check success!" >> ${APP_log}/${log_dir} done } #服务配置部署 code_config(){ output_msg "服务配置部署" unzip -d ${APP_conf} ./APP_conf.zip echo "`date +%F' '%H:%M:%S` ${APP_conf}.zip extract success!" >> ${APP_log}/${log_dir} done } #服务应用部署 code_decomp_auth(){ output_msg "服务应用部署" for AppName in $*;do unzip -d ${APP_dir}/ ./${AppName}.zip && echo "`date +%F' '%H:%M:%S` ${AppName} extract success!,path is ${APP_dir}">>${APP_log}/${log_dir} chmod 755 ${smartops_dir}/${AppName}/${AppName}.jar && echo "`date +%F' '%H:%M:%S` ${AppName}.jar permission exchange success!">>${APP_log}/${log_dir} chmod 755 ${APP_dir}/${AppName}/${AppName}.conf && echo "`date +%F' '%H:%M:%S` ${AppName}.conf permission exchange success!">>${APP_log}/${log_dir} ln -s ${APP_dir}/${AppName}/${AppName}.jar /etc/init.d/${AppName} && echo "`date +%F' '%H:%M:%S` ln of ${AppName}.jar execute success!">>${APP_log}/${log_dir}; done } #服务应用启动 server_start(){ output_msg "服务应用启动" for AppName in $*;do /etc/init.d/${AppName} start && echo "`date +%F' '%H:%M:%S` ${AppName} is ok !">>${APP_log}/${log_dir} sleep 1 done } main(){ create_dir ${APP_dir} ${APP_log} ${APP_pid} ${APP_conf} code_config code_decomp_auth APP1 APP2 APP3 APP4 code_server_start APP1APP2 APP3 APP4 } ``` 将每个模块编写为函数,最后,只需要调用main函数,即可执行上述一系列操作; 以此类推,安装nginx,Java,jenkins等各种软件操作,也可以封装成多个函数,实现自动化一键完成~ ### 5 结语 综上,linux服务器上,所有的重复性工作都可以交给shell来打理,日常工作中增加一个万能助手,何乐而不为呢 shell脚本小巧且功能强大,以上只是给大家分享了些日常使用到的脚本,其他功能大家也可以举一反三,通过各种流程控制组合来实现,让shell来帮我们做工具人,我们就可以腾出更多的时间来做更重要且更有意义的事情了,与君共勉。 ------------ ###### 自猿其说Tech-JDL京东物流技术发展部 ###### 作者:李光新
原创文章,需联系作者,授权转载
上一篇:有限状态机在国际计费中的应用探索
下一篇:Jsplumb使用及总结
相关文章
安全测试之探索windows游戏扫雷
Jmeter压测实战:Jmeter二次开发之JSF采样器实现
Laputa自动化测试框架介绍
自猿其说Tech
文章数
426
阅读量
2149963
作者其他文章
01
深入JDK中的Optional
本文将从Optional所解决的问题开始,逐层解剖,由浅入深,文中会出现Optioanl方法之间的对比,实践,误用情况分析,优缺点等。与大家一起,对这项Java8中的新特性,进行理解和深入。
01
Taro小程序跨端开发入门实战
为了让小程序开发更简单,更高效,我们采用 Taro 作为首选框架,我们将使用 Taro 的实践经验整理了出来,主要内容围绕着什么是 Taro,为什么用 Taro,以及 Taro 如何使用(正确使用的姿势),还有 Taro 背后的一些设计思想来进行展开,让大家能够对 Taro 有个完整的认识。
01
Flutter For Web实践
Flutter For Web 已经发布一年多时间,它的发布意味着我们可以真正地使用一套代码、一套资源部署整个大前端系统(包括:iOS、Android、Web)。渠道研发组经过一段时间的探索,使用Flutter For Web技术开发了移动端可视化编程平台—Flutter乐高,在这里希望和大家分享下使用Flutter For Web实践过程和踩坑实践
01
配运基础数据缓存瘦身实践
在基础数据的常规能力当中,数据的存取是最基础也是最重要的能力,为了整体提高数据的读取能力,缓存技术在基础数据的场景中得到了广泛的使用,下面会重点展示一下配运组近期针对数据缓存做的瘦身实践。
自猿其说Tech
文章数
426
阅读量
2149963
作者其他文章
01
深入JDK中的Optional
01
Taro小程序跨端开发入门实战
01
Flutter For Web实践
01
配运基础数据缓存瘦身实践
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号