注意事项

多行注释

shell里没有多行注释,只能每一行加一个 # 号。如果在开发过程中,遇到大段的代码需要临时注释起来,可以把需要注释的代码段用一对花括号括起来,定义成一个函数,但是不对其进行调用,就达到了和注释一样的效果。
比如以下这样:

#!/bin/bash
cd ~
mkdir test_dir
cd test_dir{ # 开始多行注释
for ((i=0; i<10; i++)) ;
do
touch test_$i.txt
done
} # 多行注释结束
单双引号
  • 单引号:
    单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的
    单引号字符串中不能出现单引号,即使是对单引号进行转义后也不行

  • 双引号:
    双引号里可以有变量
    双引号里可以出现转义字符

流程控制不可为空

在shell里,如果 if … else … 里,else的内容为空,就不要写这个分支,否则会报错,这也是shell与其他语言的一个重要区别。

[ 的使用

[ 在shell中是一个可执行程序,跟ls,grep, top等一样,所以在shell 中使用时必须加空格,否则也会报错。

系统变量
$0  # 这个程序执行的名字
$n  # 这个程序第n个参数值,n=1-9
$*  # 这个程序的所有参数
$#  # 这个程序的参数个数,可以用来计算字符串的长度
$$  # 这个程序的pid
$!  # 执行上一个背景指令的pid
$?  # 上一个指令的返回值

语法格式

# if else
if condition
then command1command2...commandN-1
elsecommandN
fi  # if的倒写,表示命令结束
# if else-if else
if condition1
then command1
elif condition2command2
elsecommandN
fi
# for
for var in item1 item2 ... itemN
do command1command2...commandN
done
# c风格的for
for (( EXP1; EXP2; EXP3 ))
do command1command2command3
done
# while
while condition
docommand
done
# until
until condition
do command
done
# case
case $ans in 1)echo "your number is $ans";;   # 两个分号表示break2)echo "your number is 2";;[3-9])echo "your number is $ans"
esac   # case倒过来表示结束

一些代码案例

tomcat启动脚本

#!/bin/bash
# chkconfig:2345 64 36
# description: Tomcat start/stop/restart script.### BEGIN INIT INFO
# Provides: tomcat
# Required-Start:
# Should-Start:
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: start and stop Tomcat
# Description: Tomcat Service start&restart&stop script
### END INIT INFO##Written by zero.##
JAVA_HOME=/usr/local/jdk1.8/
JAVA_BIN=/usr/local/jdk1.8/bin
JRE_HOME=/usr/local/jdk1.8/jre
PATH=$PATH:/usr/local/jdk1.8/bin:/usr/local/jdk1.8/jre/bin
CLASSPATH=/usr/local/jdk1.8/jre/lib:/usr/local/jdk1.8/lib:/usr/local/jdk1.8/jre/lib/charsets.jar
TOMCAT_BIN=/usr/local/tomcat/bin
RETVAL=0
prog="Tomcat"start()
{echo "Starting $prog......"/bin/bash $TOMCAT_BIN/startup.shRETVAL=$?return $RETVAL
}
stop()
{echo "Stopping $prog......"/bin/bash $TOMCAT_BIN/shutdown.shRETVAL=$?return $RETVAL
}
restart(){echo "Restarting $prog......"stopstart
}case "$1" instart)start;;stop)stop;;restart)restart;;*)echo $"Usage: $0 {start|stop|restart}"RETVAL=1
esac
exit $RETVA
拷贝目录

#!/bin/bash
cd /root/
list=(`ls`)for i in ${list[@]}
doif [ -d $i ]thencp -r $i /tmp/fi
统计ip

有一个日志文件,日志片段:如下:

112.111.12.248 – [25/Sep/2013:16:08:31 +0800]formula-x.haotui.com “/seccode.php?update=0.5593110133088248″ 200″http://formula-x.haotui.com/registerbbs.php” “Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1;)”
61.147.76.51 – [25/Sep/2013:16:08:31 +0800]xyzdiy.5d6d.com “/attachment.php?aid=4554&k=9ce51e2c376bc861603c7689d97c04a1&t=1334564048&fid=9&sid=zgohwYoLZq2qPW233ZIRsJiUeu22XqE8f49jY9mouRSoE71″ 301″http://xyzdiy.×××thread-1435-1-23.html” “Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)”

要求: 统计出每个IP的访问量有多少?

awk '{print $1}' 1.log |sort -n |uniq -c |sort -n

解释:sort -n会按照数值而不是ASCII码来排序awk截取出来的IP。然后uniq命令用于报告或忽略文件中的重复行,加上-c选项后会在每列旁边显示该行重复出现的次数,在这一步就完成了统计。不过最后还得再让sort -n排序一下uniq -c统计出来的结果。

统计内存使用

写一个脚本计算一下linux系统所有进程占用内存大小的和。

实现代码:

#!/bin/bash
count=0
# 这个循环会遍历出每个进程占用的内存大小
for i in `ps aux |awk '{print $6}' |grep -v 'RSS'`
do# 将遍历出来的数字进行累加count=$[$count+$i]
done# 就得到所有进程占用内存大小的和了
echo "$count/kb"

也可以使用awk 一条命令计算:

ps aux |grep -v 'RSS TTY' |awk '{sum=sum+$6};END{print sum}'

解释:grep -v是忽略 ‘RSS TTY’ 所存在的那一行,后面的awk声明了一个变量sum,sum将前面命令列出来的数字进行累加,END之后就将累加后的sum打印出来,就得到所有进程占用内存大小的和了。

简单的监控脚本

设计一个简单的脚本,监控远程的一台机器(假设ip为123.23.11.21)的存活状态,当发现宕机时发一封邮件给你自己。

#!/bin/bash
ip="123.23.11.21"
email="user@example"while 1
doping -c10 $ip > /dev/null 2>/dev/nullif [ $? != "0" ]then# 调用一个用于发邮件的脚本python /usr/local/sbin/mail.py $email "$ip down" "$ip is down"fisleep 30
done

mail.py 脚本代码:

#!/usr/bin/env python
#-*- coding: UTF-8 -*-
import os,sys
reload(sys)
sys.setdefaultencoding('utf8')
import getopt
import smtplib
from email.MIMEText import MIMEText
from email.MIMEMultipart import MIMEMultipart
from  subprocess import *
def sendqqmail(username,password,mailfrom,mailto,subject,content):# 邮箱的服务地址gserver = 'smtp.qq.com'gport = 25try:msg = MIMEText(unicode(content).encode('utf-8'))msg['from'] = mailfrommsg['to'] = mailtomsg['Reply-To'] = mailfrommsg['Subject'] = subjectsmtp = smtplib.SMTP(gserver, gport)smtp.set_debuglevel(0)smtp.ehlo()smtp.login(username,password)smtp.sendmail(mailfrom, mailto, msg.as_string())smtp.close()except Exception,err:print "Send mail failed. Error: %s" % err
def main():to=sys.argv[1]subject=sys.argv[2]content=sys.argv[3]#定义QQ邮箱的账号和密码,你需要修改成你自己的账号和密码sendqqmail('1234567@qq.com','aaaaaaaaaa','1234567@qq.com',to,subject,content)
if __name__ == "__main__":main()#####脚本使用说明######
#1. 首先定义好脚本中的邮箱账号和密码
#2. 脚本执行命令为:python mail.py 目标邮箱 "邮件主题" "邮件内容"
批量更改文件名

需求:

找到/123目录下所有后缀名为.txt的文件
批量修改.txt为.txt.bak
把所有.bak文件打包压缩为123.tar.gz
批量还原文件的名字,即把增加的.bak再删除

代码:

#!/bin/bash
now=`date +%F_%T`
mkdir /tmp/123_$now
for txt in `ls /123/*.txt`
domv $txt $txt.bakfor f in $txtdocp $txt.bak /tmp/123_$nowdone
donecd /tmp/
tar czf 123.tar.gz 123_$now/for txt in `ls /123/*.txt.bak`
doname=`echo $txt |awk -F '.' '{OFS="."} {print $1,$2}'`mv $txt $name
done
监控80端口

需求:
写一个脚本,判断本机的80端口(假如服务为httpd)是否开启着,如果开启着什么都不做,如果发现端口不存在,那么重启一下httpd服务,并发邮件通知你自己。脚本写好后,可以每一分钟执行一次,也可以写一个死循环的脚本,30s检测一次。
发邮件的脚本参考【脚本11】的示例代码。
代码:

#!/bin/bash
email="user@example.com"
if netstat -lntp |grep ':80' |grep 'httpd'
thenecho "80 port no problem"exit
else/usr/local/apache2.4/bin/apachectl restartpython mail.py $email "check_80port" "The 80 port is down."n=`ps aux |grep httpd|grep -cv grep`if [ $n -eq 0 ]then/usr/local/apache2/bin/apachectl start 2>/tmp/apache_start.errfiif [ -s /tmp/apache_start.err ]thenpython mail.py  $mail 'apache_start_error' `cat /tmp/apache_start.err`fi
fi
备份数据库

需求:

设计一个shell脚本来备份数据库,首先在本地服务器上保存一份数据,然后再远程拷贝一份,本地保存一周的数据,远程保存一个月。

假定,我们知道mysql root账号的密码,要备份的库为discuz,本地备份目录为/bak/mysql, 远程服务器ip为192.168.123.30,远程提供了一个rsync服务,备份的地址是 192.168.123.30::backup . 写完脚本后,需要加入到cron中,每天凌晨3点执行。
脚本代码:

#!/bin/bash
PATH=$PATHi:/usr/local/mysql/bin
week=`date +%w`
today=`date +d`
passwd="123456"
backdir="/data/mysql"
r_backupIP="192.168.123.30::backup"exec 1>/var/log/mysqlbak.log 2>/var/log/mysqlbak.log
echo "mysql backup begin at `date +%F %T`."# 本地备份
mysqldump -uroot -p$passwd --default-character-set=utf8 discuz >$backdir/$week.sql
# 同步备份到远程机器
rsync -az $backdir/$week.sql $r_backupIP/$today.sqlecho "mysql backup end at `date +%F %T`."

然后加入cron

0 3 * * * /bin/bash /usr/local/sbin/mysqlbak.sh
自动重启php-fpm服务

服务器上跑的是LNMP环境,近期总是有502现象。502为网站访问的状态码,200正常,502错误是nginx最为普通的错误状态码。由于502只是暂时的,并且只要一重启php-fpm服务则502消失,但不重启的话,则会一直持续很长时间。所以有必要写一个监控脚本,监控访问日志的状态码,一旦发生502,则自动重启一下php-fpm。

我们设定:

access_log /data/log/access.log
脚本死循环,每10s检测一次(假设每10s钟的日志条数为300左右)
重启php-fpm的方法是 /etc/init.d/php-fpm restart
脚本代码:

#!/bin/bash
access_log="/data/log/access.log"
N=10
while :
do# 因为10秒大概产生300条日志记录tail -n300 $access_log > /tmp/log# 拿出log中包含502的日志行数n_502=`grep -c "502" /tmp/log`# 如果行数大于10if [ $n_502 -ge $N ]then# 就记录一下系统状态top -bn1 > /tmp/`date +%H%M%S`-top.logvmstat 1 5 > /tmp/`date +%H%M%S`-vm.log# 然后才重启服务,并把错误信息重定向/etc/init.d/php-fpm restart 2> /dev/null# 重启php-fpm服务后,应先暂缓1分钟,而后继续每隔10s检测一次sleep(60)fisleep(10)
done
文本替换

将memcached里的数据导出到文件中,然后再导入memcached里却发现数据过期了,这是因为导出的数据是带有一个时间戳的,这个时间戳就是该条数据过期的时间点,如果当前时间已经超过该时间戳,那么是导入不进去的。不过可以修改文件中的时间戳来保证导入时数据的有效期。可以写一个简单的脚本批量替换这些文件中的时间戳:

#!/bin/bash
hour=`date -d "+1 hour" +%s`  # 一个小时之后的时间戳
data_time=`cat data.txt |grep add |awk '{print $4}' |sort -n |uniq`
for i in $data_time
dosed -i "s/$i/$hour/g" `grep $i -rl /root/data.txt`
done
启动容器

docker每次关闭都会连带着将运行中的容器关闭,所以每次启动docker后都需要逐个去启动容器,很麻烦,由于是实验用的虚拟机不是线上的机器,所以就直接写了一个很简单的循环来启动容器:

#!/bin/bash
/usr/bin/systemctl start dockerfor i in `docker ps -a |grep 'Exited' |awk '{print $1}'`
do/usr/bin/docker start $i
done
删除文本中的字母

要求:把一个文本文档的前5行中包含字母的行删除掉,同时把6到10行中的全部字母删除掉。

参考答案:假设文本名字叫做1.txt,并且文本行数大于10,脚本如下:

#!/bin/bash
## 先获取该文本的行数
rows=`wc -l 1.txt |awk '{print $1}'`## 对前5行进行处理
for i in `seq 1 5`
do## 使用sed把每一行的内容赋值给变量row=`sed -n "$i"p 1.txt`## 用grep 判定是否匹配字母,-v取反,-q不输出内容if echo $row |grep -vq '[a-zA-Z]'thenecho $rowfi
done## 对6-10行做删除字母处理
for i in `seq 6 10`
dorow=`sed -n "$i"p 1.txt`echo $row |sed 's/[a-zA-Z]//g'
done## 剩余的直接输出
for i in `seq 11 $rows`
dosed -n "$i"p 1.txt
done##若想把更改内容写入到1.txt,还需要把以上内容重定向到一个文本中,然后删除1.txt,再把刚刚重定向的文件更名为1.txt
查找字母数小于6的单词

用shell打印下面这句话中字母数小于6的单词。
Bash also interprets a number of multi-character options.

脚本如下:

#!/bin/bash
for s in Bash also interprets a number of multi-character options
don=`echo $s |wc -c`if [ $n -lt 6 ]thenecho $sfi
done
输入数字执行对应命令

写一个脚本实现如下功能: 输入一个数字,然后运行对应的一个命令。显示命令如下:

*cmd meau** 1—date 2–ls 3–who 4–pwd

当输入1时,会运行date, 输入2时运行ls, 依此类推。

实现脚本如下:

#!/bin/bash
echo "*cmd meau**  1—date 2–ls 3–who 4–pwd"read -p "please input a number 1-4: " n
case $n in1)date;;2)ls;;3)who;;4)pwd;;*)echo "Please input a number: 1-4";;
esac
批量创建用户并设置密码

用shell脚本实现如下需求:
添加user_00 – user_09 10个用户,并且给他们设置一个随机密码,密码要求10位包含大小写字母以及数字,注意需要把每个用户的密码记录到一个日志文件里。

提示:

随机密码使用命令 mkpasswd
在脚本中给用户设置密码,可以使用echo 然后管道passwd命令
实现脚本如下:

#!/bin/bash
for i in `seq 00 09`
douseradd user_$ip=`mkpasswd -s 0 -l 10`echo "user_$i $p" >> /tmp/user0_9.pwecho $p |passwd -stdin user_$i
done
监控httpd进程

在服务器上,写一个监控脚本。

每隔10s去检测一次服务器上的httpd进程数,如果大于等于500的时候,就需要自动重启一下apache服务,并检测启动是否成功?
若没有正常启动还需再一次启动,最大不成功数超过5次则需要理解发邮件通知管理员,并且以后不需要再检测!
如果启动成功后,1分钟后再次检测httpd进程数,若正常则重复之前操作(每隔10s检测一次),若还是大于等于500,那放弃重启并需要发邮件给管理员,然后自动退出该脚本。假设其中发邮件脚本为mail.py
实现脚本如下:

#!/bin/bash
check_service(){n=0for i in `seq 1 5`do# apachectl命令所在路径/usr/local/apache2/bin/apachectl restart 2> /tmp/apache.errif [$? -ne 0 ]thenn=$[$n-1]elsebreakfidoneif [ $n -eq 5 ]then## mail.py的内容参考https://coding.net/u/aminglinux/p/aminglinux-book/git/blob/master/D22Z/mail.pypython mail.py "123@qq.com" "httpd service down" `cat /tmp/apache.err`exitfi
}while :
dot_n=`ps -C httpd --no-heading |wc -l`if [ $t_n -ge 500 ]then/usr/local/apache2/bin/apachectl restartif [ $? -ne 0 ]thencheck_servicefisleep 60t_n=`ps -C httpd --no-heading |wc -l`if [ $t_n -ge 500]thenpython mail.py "123@qq.com" "httpd service somth wrong" "the httpd process is budy."exitfifisleep 10
done
封ip

需求: 根据web服务器上的访问日志,把一些请求量非常高的ip给拒绝掉!

分析: 我们要做的,不仅是要找到哪些ip请求量不合法,并且还要每隔一段时间把之前封掉的ip(若不再继续请求了)给解封。 所以该脚本的关键点在于定一个合适的时间段和阈值。

比如, 我们可以每一分钟去查看一下日志,把上一分钟的日志给过滤出来分析,并且只要请求的ip数量超过100次那么就直接封掉。 而解封的时间又规定为每半小时分析一次,把几乎没有请求量的ip给解封!

参考日志文件片段:

157.55.39.107 [20/Mar/2015:00:01:24 +0800] www.aminglinux.com “/bbs/thread-5622-3-1.html” 200 “-” “Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)”
61.240.150.37 [20/Mar/2015:00:01:34 +0800] www.aminglinux.com “/bbs/search.php?mod=forum&srchtxt=LNMP&formhash=8f0c7da9&searchsubmit=true&source=hotsearch” 200 “-” “Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)”

脚本实现如下:

#!/bin/bash
## 日志文件路径
log_file="/home/logs/client/access.log"
## 当前时间减一分钟的时间
d1=`date -d "-1 minute" +%H:%M`
## 当前时间的分钟段
d2=`date +%M`
## iptables命令所在的路径
ipt="/sbin/iptables"
## 用于存储访问日志里的ip
ips="/tmp/ips.txt"## 封ip
block(){## 把日志文件中的ip过滤出来,去掉重复的ip,并统计ip的重复次数以及对ip进行排序,最后将结果写到一个文件中grep "$d1:" $log_file |awk '{print $1}' |sort -n |uniq -c |sort -n > $ips## 将文件里重复次数大于100的ip迭代出来for ip in `awk '$1 > 100 {print $2}' $ips`do## 通过防火墙规则对这些ip进行封禁$ipt -I INPUT -p -tcp --dport 80 -s $ip -j REJECT## 将已经封禁的ip输出到一个文件里存储echo "`date +%F-%T` $ip" >> /tmp/badip.txtdone
}## 解封ip
unblock(){## 将流量小于15的规则索引过滤出来for i in `$ipt -nvL --line-number |grep '0.0.0.0/0' |awk '$2 < 15 {print $1}' |sort -nr`do## 通过索引来删除规则$ipt -D INPUT $idone## 清空规则中的数据包计算器和字节计数器$ipt -Z
}## 为整点或30分钟就是过了半个小时,就需要再进行分析
if [ $d2 == "00" ] || [ $d2 == "30" ]
thenunblockblock
elseblock
fi
部署前端项目

最近做了一个web前端的项目,需要编写一个脚本完成项目的上线。

脚本实现如下:

#!/bin/bash
#
# 使用方法:
# mmall:front_deploy.sh mmall-fe
# admin:front_deploy.sh admin-fe
#GIT_HOME=/developer/git-repository/   # 从git仓库拉取下来的源码的存放路径
DEST_PATH=/product/frontend/    # 项目打包后的发布路径# cd dir
if [ ! -n "$1" ]
thenecho -e "请输入要发布的项目!"exit
fi if [ $1 = "mmall-fe" ]
thenecho -e "===========Enter mall-fe============="cd $GIT_HOME$1
elif [ $1 = "admin-fe" ]
thenecho -e "===========Enter mall-fe============="cd $GIT_HOME$1
else echo -e "输入的项目名没有找到!"exit
fi# clear git dist
echo -e "===========Clear Git Dist============="
rm -rf ./dist# git操作
echo -e "===========git checkout master============="
git checkout masterecho -e "===========git pull============="
git pull# npm install
echo -e "===========npm install============="
npm install --registry=https://registry.npm.taobao.org# npm run dist
echo -e "===========npm run dist============="
npm run distif [ -d "./dist" ]
then# backup destecho -e "===========dest backup============="mv $DEST_PATH$1/dist $DEST_PATH$1/dist.bak# copyecho -e "===========copy============="cp -R ./dist $DEST_PATH$1# echo resultecho -e "===========Deploy Success============="
elseecho -e "===========Deploy Error============="
fi
监控磁盘使用率

写一个shell脚本,检测所有磁盘分区使用率和inode使用率并记录到以当天日期为命名的日志文件里,当发现某个分区容量或者inode使用量大于85%时,发邮件通知你自己。

思路:就是先df -h 然后过滤出已使用的那一列,然后再想办法过滤出百分比的整数部分,然后和85去比较,同理,inode也是一样的思路。

实现代码:

#!/bin/bash
## This script is for record Filesystem Use%,IUse% everyday and send alert mail when % is more than 85%.log=/var/log/disk/`date +%F`.log
date +'%F %T' > $log
df -h >> $log
echo >> $log
df -i >> $logfor i in `df -h|grep -v 'Use%'|sed 's/%//'|awk '{print $5}'`; doif [ $i -gt 85 ]; thenuse=`df -h|grep -v 'Use%'|sed 's/%//'|awk '$5=='$i' {print $1,$5}'`echo "$use" >> usefi
done
if [ -e use ]; then##这里可以使用咱们之前介绍的mail.py发邮件mail -s "Filesystem Use% check" root@localhost < userm -rf use
fifor j in `df -i|grep -v 'IUse%'|sed 's/%//'|awk '{print $5}'`; doif [ $j -gt 85 ]; theniuse=`df -i|grep -v 'IUse%'|sed 's/%//'|awk '$5=='$j' {print $1,$5}'`echo "$iuse" >> iusefi
done
if [ -e iuse ]; thenmail -s "Filesystem IUse% check" root@localhost < iuserm -rf iuse
fi

思路:

df -h、df -i 记录磁盘分区使用率和inode使用率,date +%F 日志名格式
取出使用率(第5列)百分比序列,for循环逐一与85比较,大于85则记录到新文件里,当for循环结束后,汇总超过85的一并发送邮件(邮箱服务因未搭建,发送本地root账户)。
此脚本正确运行前提:

该系统没有逻辑卷的情况下使用,因为逻辑卷df -h、df -i 时,使用率百分比是在第4列,而不是第5列。如有逻辑卷,则会漏统计逻辑卷使用情况。

获取文件列表

有一台服务器作为web应用,有一个目录(/data/web/attachment)不定时地会被用户上传新的文件,但是不知道什么时候会上传。所以,需要我们每5分钟做一次检测是否有新文件生成。

请写一个shell脚本去完成检测。检测完成后若是有新文件,还需要将新文件的列表输出到一个按年、月、日、时、分为名字的日志里。请不要想的太复杂,核心命令只有一个 find /data/web/attachment -mmin -5

思路: 每5分钟检测一次,那肯定需要有一个计划任务,每5分钟去执行一次。脚本检测的时候,就是使用find命令查找5分钟内有过更新的文件,若是有更新,那这个命令会输出东西,否则是没有输出的。固,我们可以把输出结果的行数作为比较对象,看看它是否大于0。

实现代码:

#!/bin/bash
d=`date -d "-5 min" +%Y%m%d%H%M`
basedir=/data/web/attachment
find $basedir/ -type f  -mmin -5 > /tmp/newf.txt
n=`wc -l /tmp/newf.txt`
if [ $n -gt 0 ]; then/bin/mv /tmp/newf.txt /tmp/$dfi
统计常用命令

写一个shell脚本来看看你使用最多的命令是哪些,列出你最常用的命令top10。

思路:我们要用到一个文件就是.bash_history,然后再去sort、uniq,剩下的就不用我多说了吧。很简单一个shell。

一条命令即可:

sort /root/.bash_history |uniq -c |sort -nr |head
统计日志大小

假如我们需要每小时都去执行你写的脚本。在脚本中实现这样的功能,当时间是0点和12点时,需要将目录/data/log/下的文件全部清空,注意只能清空文件内容而不能删除文件。而其他时间只需要统计一下每个文件的大小,一个文件一行,输出到一个按日期和时间为名字的日志里。 需要考虑/data/log/目录下的二级、三级、… 等子目录里面的文件。

实现代码:

#!/bin/bash
logdir="/data/log"
t=`date +%H`
d=`date +%F-%H`
[ -d /tmp/log_size ] || mkdir /tmp/log_size
for log in `find $logdir -type f`
doif [ $t == "0" ] || [ $t == "12" ]thentrue > $logelsedu -sh $log >>/tmp/log_size/$dfi
done
检测文件改动

有两台Linux服务器A和B,假如A可以直接ssh到B,不用输入密码。A和B都有一个目录叫做/data/web/ 这下面有很多文件,当然我们不知道具体有几层子目录,假若之前A和B上该目录下的文件都是一模一样的。但现在不确定是否一致了。固需要我们写一个脚本实现这样的功能,检测A机器和B机器/data/web/目录下文件的异同,我们以A机器上的文件作为标准。比如,假若B机器少了一个a.txt文件,那我们应该能够检测出来,或者B机器上的b.txt文件有过改动,我们也应该能够检测出来(B机器上多了文件我们不用考虑)。

提示: 使用核心命令 md5sum a.txt 算出md5值,去和B机器上的比较。

实现代码:

#!/bin/bash
#假设A机器到B机器已经做了无密码登录设置
dir=/data/web
##假设B机器的IP为192.168.0.100
B_ip=192.168.0.100
find $dir -type f |xargs md5sum >/tmp/md5.txt
ssh $B_ip "find $dir -type f |xargs md5sum >/tmp/md5_b.txt"
scp $B_ip:/tmp/md5_b.txt /tmp
for f in `awk '{print $2}' /tmp/md5.txt`
doif grep -q "$f" /tmp/md5_b.txtthenmd5_a=`grep $f /tmp/md5.txt|awk '{print $1}'`md5_b=`grep $f /tmp/md5_b.txt|awk '{print $1}'`if [ $md5_a != $md5_b ]thenecho "$f changed."fielseecho "$f deleted. "fi
done
统计网卡流量

写一个脚本,检测你的网络流量,并记录到一个日志里。需要按照如下格式,并且一分钟统计一次(只需要统计外网网卡,假设网卡名字为eth0):

2017-08-04 01:11
eth0 input: 1000bps
eth0 output : 200000bps
################
2017-08-04 01:12
eth0 input: 1000bps
eth0 output : 200000bps

提示:使用sar -n DEV 1 59 这样可以统计一分钟的平均网卡流量,只需要最后面的平均值。另外,注意换算一下,1byt=8bit

实现代码:

#!/bin/bash
while :
doLANG=enDATE=`date +"%Y-%m-%d %H:%M"`LOG_PATH=/tmp/traffic_check/`date +%Y%m`LOG_FILE=$LOG_PATH/traffic_check_`date +%d`.log[ -d $LOG_PATH ] || mkdir -p $LOG_PATHecho " $DATE" >> $LOG_FILEsar -n DEV 1 59|grep Average|grep eth0 \ |awk '{print "\n",$2,"\t","input:",$5*1000*8,"bps", \"\t","\n",$2,"\t","output:",$6*1000*8,"bps" }' \ >> $LOG_FILEecho "#####################" >> $LOG_FILE
done
系统-批量杀进程

我们可以使用一条命令,直接杀死所有的sh。

命令如下:

ps aux |grep clearmem.sh |grep -v grep|awk '{print $2}'|xargs kill
判断是否开启80端口

写一个脚本判断你的Linux服务器里是否开启web服务?(监听80端口)如果开启了,请判断出跑的是什么服务,是httpd呢还是nginx又或者是其他的什么?

实现代码:

#!/bin/bashport=`netstat -lnp | grep 80`if [ -z "port" ]; thenecho "not start service.";exit;fiweb_server=`echo $port | awk -F'/' '{print $2}'|awk -F : '{print $1}'`
case $web_server inhttpd ) echo "apache server.";;nginx )echo "nginx server.";;* )echo "other server.";;
esac
监控mysql服务

假设,当前MySQL服务的root密码为123456,写脚本检测MySQL服务是否正常(比如,可以正常进入mysql执行show processlist),并检测一下当前的MySQL服务是主还是从,如果是从,请判断它的主从服务是否异常。如果是主,则不需要做什么。

实现代码:

#!/bin/bash
Mysql_c="mysql -uroot -p123456"
$Mysql_c -e "show processlist" >/tmp/mysql_pro.log 2>/tmp/mysql_log.err
n=`wc -l /tmp/mysql_log.err|awk '{print $1}'`if [ $n -gt 0 ]
thenecho "mysql service sth wrong."
else$Mysql_c -e "show slave status\G" >/tmp/mysql_s.logn1=`wc -l /tmp/mysql_s.log|awk '{print $1}'`if [ $n1 -gt 0 ]theny1=`grep 'Slave_IO_Running:' /tmp/mysql_s.log|awk -F : '{print $2}'|sed 's/ //g'`y2=`grep 'Slave_SQL_Running:' /tmp/mysql_s.log|awk -F : '{print $2}'|sed 's/ //g'`if [ $y1 == "Yes" ] && [ $y2 == "Yes" ]thenecho "slave status good."elseecho "slave down."fifi
fi
获取ip

提示用户输入网卡的名字,然后我们用脚本输出网卡的ip。 看似简单,但是需要考虑多个方面,比如我们输入的不符合网卡名字的规范,怎么应对。名字符合规范,但是根本就没有这个网卡有怎么应对。

代码参考:

#!/bin/bash
while :
doread -p "请输入网卡名: " ee1=`echo "$e" | sed 's/[-0-9]//g'`e2=`echo "$e" | sed 's/[a-zA-Z]//g'`if [ -z $e ]thenecho "你没有输入任何东西"continueelif [ -z $e1 ]thenecho "不要输入纯数字在centos中网卡名是以eth开头后面加数字"continueelif [ -z $e2 ]thenecho "不要输入纯字母在centos中网卡名是以eth开头后面加数字"continueelsebreakfi
doneip() {ifconfig | grep -A1 "$1 " |tail -1 | awk '{print $2}' | awk -F ":" '{print $2}'
}myip=`ip $e`
if [ -z $myip ]
thenecho "抱歉,没有这个网卡。"
elseecho "你的网卡IP地址是$myip"
fi
下载文件

创建一个函数,能接受两个参数:

第一个参数为URL,即可下载的文件;第二个参数为目录,即下载后保存的位置;
如果用户给的目录不存在,则提示用户是否创建;如果创建就继续执行,否则,函数返回一个51的错误值给调用脚本;
如果给的目录存在,则下载文件;下载命令执行结束后测试文件下载成功与否;如果成功,则>返回0给调用脚本,否则,返回52给调用脚本;
提示,在函数中返回错误值给调用脚本,使用return

参考代码:

#!/bin/bash
if [ ! -d $2 ]
thenecho "please make directory"exit 51
ficd $2
wget $1n=`echo $?`
if [ $n -eq 0 ];thenexit 0
elseexit 52
fi
猜数字

写一个猜数字脚本,当用户输入的数字和预设数字(随机生成一个小于100的数字)一样时,直接退出,否则让用户一直输入,并且提示用户的数字比预设数字大或者小。

参考代码:

#!/bin/bash
m=`echo $RANDOM`
n1=$[$m%100]
while :
doread -p "Please input a number: " nif [ $n == $n1 ]thenbreakelif [ $n -gt $n1 ]thenecho "bigger"continueelseecho "smaller"continuefi
done
echo "You are right."
抽签脚本

1、写一个脚本执行后,输入名字,产生随机数01-99之间的数字。
2、如果相同的名字重复输入,抓到的数字还是第一次抓取的结果,
3、前面已经抓到的数字,下次不能在出现相同数字。
4、第一个输入名字后,屏幕输出信息,并将名字和数字记录到文件里,程序不能退出
继续等待别的学生输入。

参考代码:

while :
doread -p  "Please input a name:" nameif [ -f /work/test/1.log ];thenbb=`cat /work/test/1.log | awk -F: '{print $1}' | grep "$name"`if [ "$bb" != "$name" ];then  #名字不重复情况下aa=`echo $RANDOM | awk -F "" '{print $2 $3}'`while :dodd=`cat  /work/test/1.log |  awk -F: '{print $2}'  | grep "$aa"`if [ "$aa"  ==  "$dd" ];then   #数字已经存在情况下echo "数字已存在."aa=`echo $RANDOM | awk -F "" '{print $2 $3}'`elsebreakfidoneecho "$name:$aa" | tee -a /work/test/1.logelseaa=`cat /work/test/1.log |  grep "$name" | awk -F: '{print $2}'` #名字重复echo $aaecho "重复名字."fielseaa=`echo $RANDOM | awk -F "" '{print $2 $3}'`echo "$name:$aa" | tee -a  /work/test/1.logfi
done
打印只有一个数字的行

如题,把一个文本文档中只有一个数字的行给打印出来。

参考代码:

#!/bin/bash
f=/etc/passwd
line=`wc -l $f|awk '{print $1}'`
for l in `seq 1 $line`; don=`sed -n "$l"p $f|grep -o '[0-9]'|wc -l`;if [ $n -eq 1 ]; thensed -n "$l"p $ffi
done
日志归档

类似于日志切割,系统有个logrotate程序,可以完成归档。但现在我们要自己写一个shell脚本实现归档。

举例: 假如服务的输出日志是1.log,我要求每天归档一个,1.log第二天就变成1.log.1,第三天1.log.2, 第四天 1.log.3 一直到1.log.5

参考答案:

#!/bin/bash
function e_df()
{[ -f $1 ] && rm -f $1
}for i in `seq 5 -1 2`
doi2=$[$i-1]e_df /data/1.log.$iif [ -f /data/1.log.$i2 ]thenmv /data/1.log.$i2 /data/1.log.$ifi
donee_df /data/1.log.1
mv /data/1.log  /data/1.log.1
检查错误

写一个shell脚本,检查指定的shell脚本是否有语法错误,若有错误,首先显示错误信息,然后提示用户输入q或者Q退出脚本,输入其他内容则直接用vim打开该shell脚本。

提醒: 检查shell脚本有没有语法错误的命令是 sh -n xxx.sh

参考代码:

#!/bin/bash
sh -n $1 2>/tmp/err
if [ $? -eq "0" ]
thenecho "The script is OK."
elsecat /tmp/errread -p "Please inpupt Q/q to exit, or others to edit it by vim. " nif [ -z $n ]thenvim $1exitfiif [ $n == "q" -o $n == "Q" ]thenexitelsevim $1exitfifi
编写一个名为iffile程序,它执行时判断/bin目录下date文件是否存在?

参考代码:

#!/bin/bash
if [ -f /bin/date  ]
thenecho "/bin/date file exist."
elseecho "/bin/date not exist."
fi
编写一个名为greet的问候程序,它执行时能根据系统当前的时间向用户输出问候信息。设从半夜到中午为早晨,中午到下午六点为下午,下午六点到半夜为晚上。

参考代码:

#!/bin/bash
h=`date +%H`
if [ $h -ge 0 ] && [ $h -lt 12 ]
thenecho "Good morning."
elif [ $h -ge 12 ] && [ $h -lt 18 ]
thenecho "Good afternoon."
elseecho "Good evening."
fi
判断用户登录

编写一个名为ifuser的程序,它执行时带用户名作为命令行参数,判断该用户是否已经在系统中登录,并给出相关信息。

参考代码:

#!/bin/bash
read -p "Please input the username: " user
if who | grep -qw $user
thenecho $user is online.
elseecho $user not online.
fi
更改后缀名

编写一个名为chname的程序,将当前目录下所有的.txt文件更名为.doc文件。

参考代码:

#!/bin/bash
find . -type f -name "*.txt" > /tmp/txt.list
for f in `cat /tmp/txt.list`
don=`echo $f|sed -r 's/(.*)\.txt/\1/'`echo "mv $f $n.doc"
done
检查服务

先判断是否安装http和mysql,没有安装进行安装,安装了检查是否启动服务,若没有启动则需要启动服务。

说明:操作系统为centos6,httpd和mysql全部为rpm包安装。

参考代码:

#!/bin/bash
if_install()
{n=`rpm -qa|grep -cw "$1"`if [ $n -eq 0 ]thenecho "$1 not install."yum install -y $1elseecho "$1 installed."fi
}if_install httpd
if_install mysql-serverchk_ser()
{p_n=`ps -C "$1" --no-heading |wc -l`if [ $p_n -eq 0 ]thenecho "$1 not start."/etc/init.d/$1 startelseecho "$1 started."fi
}chk_httpd
chk_mysqld
监控网卡

1.每10分钟检测一次指定网卡的流量
2.如果流量为0,则重启网卡

参考代码:

#!/bin/bash
LANG=en
n1=`sar -n DEV 1 60 |grep eth0 |grep -i average|awk '{print $5}'|sed 's/\.//g'`
n2=`sar -n DEV 1 60 |grep eth0 |grep -i average|awk '{print $6}'|sed 's/\.//g'`
if [ $n1 == "000" ] && [ $n2 == "000" ]
thenifdown eth0ifup eth0
fi

然后写个cron,10分钟执行一次

监控web可用性

写一个shell脚本,通过curl -I 返回的状态码来判定所访问的网站是否正常。比如,当状态码为200时,才算正常。

参考代码:

#/bin/bashurl="http://www.apelearn.com/index.php"
sta=`curl -I $url 2>/dev/null |head -1 |awk '{print $2}'`if [ $sta != "200" ]
thenpython /usr/local/sbin/mail.py xxx@qq.com "$url down." "$url down"
fi
统计分析日志

已知nginx访问的日志文件在/usr/local/nginx/logs/access.log内

请统计下早上10点到12点 来访ip最多的是哪个?

日志样例:

111.199.186.68 – [15/Sep/2017:09:58:37 +0800]  “//plugin.php?id=security:job” 200 “POST //plugin.php?id=security:job HTTP/1.1″”http://a.lishiming.net/forum.php?mod=viewthread&tid=11338&extra=page%3D1%26filter%3Dauthor%26orderby%3Ddateline” “Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3141.7 Safari/537.36” “0.516”203.208.60.208 – [15/Sep/2017:09:58:46 +0800] “/misc.php?mod=patch&action=ipnotice&_r=0.05560809863330207&inajax=1&ajaxtarget=ip_notice” 200 “GET /misc.php?mod=patch&action=ipnotice&_r=0.05560809863330207&inajax=1&ajaxtarget=ip_notice HTTP/1.1″”http://a.lishiming.net/forum.php?mod=forumdisplay&fid=65&filter=author&orderby=dateline” “Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3141.7 Safari/537.36” “0.065”

实现这个需求使用如下命令即可:

grep '15/Sep/2017:1[0-2]:[0-5][0-9]:' /usr/local/nginx/logs/access.log|awk '{print $1}'|sort -n|uniq -c |sort -n|tail -n1
给文档增加内容

在文本文档1.txt第5行(假设文件行数大于5)后面增加如下内容:

# This is a test file.# Test insert line into this file.

参考命令:

sed -i "5a # This is a test file.\n# Test insert line into this file." 1.txt
备份etc下面文件

设计一个shell程序,在每月第一天备份并压缩/etc目录的所有内容,存放在/root/bak目录里,且文件名为如下形式”yymmdd_etc.tar.gz”,yy为年,mm为月,dd为日。

参考代码:

#!/bin/sh
if [ ! -d /root/bak ]
thenmkdir /root/bak
fi
prefix=`date +%y%m%d`
d=`date +%d`
if [ $d == "01" ]
thencd /etc/tar czf  /root/bak/$prefix_etc.tar.gz ./
fi
计算单词重复次数

将文件内所有的单词的重复次数计算出来,只需要列出重复次数最多的10个单词。

假设文档名字叫做a.txt,使用如下命令即可:

sed 's/[^a-zA-Z]/ /g' a.txt|xargs -n1 |sort |uniq -c |sort -nr |head
a.txt有b.txt没有

有两个文件a.txt和b.txt,需求是,把a.txt中有的并且b.txt中没有的行找出来,并写入到c.txt,然后计算c.txt文件的行数。

参考代码:

#!/bin/bash
n=`wc -l a.txt|awk '{print $1}'`
[ -f c.txt ] && rm -f c.txt
for i in `seq 1 $n`
dol=`sed -n "$i"p a.txt`if ! grep -q "^$l$" b.txtthenecho $l >>c.txtfi
done
wc -l c.txt

或者用grep实现

grep -vwf b.txt a.txt > c.txt; wc -l c.txt
杀死进程

把当前用户下所有进程名字中含有”java”的进程关闭。

参考答案:

ps -u $USER |awk '$NF ~ /java/ {print $1}'|xargs kill
备份数据表

用shell实现,以并发进程的形式将mysql数据库所有的表备份到当前目录,并把所有的表压缩到一个压缩包文件里。

假设数据库名字为mydb,用户名为zero,密码为passwd。

提示: 在shell中加上&可以将命令丢到后台,从而可以同时执行多条命令达到并发的效果。

参考代码:

#!/bin/bash
pre=`date +%F`
for d in `mysql -uaming -ppasswd mydb -e "show tables"|grep -v 'Tables_in_'`
domysqldump -uaming -ppasswd mydb $d > $d.sql &
done
tar czf $pre.tar.gz *.sql
rm -f *.sql
监控节点

一个网站,使用了cdn,全国各地有几十个节点。需要你写一个shell脚本来监控各个节点是否正常。

假如:

监控的url为www.xxx.com/index.php
源站ip为88.88.88.88
参考代码:

#!/bin/bash
url="www.xxx.com/index.php"
s_ip="88.88.88.88"
curl -x $s_ip:80 $url > /tmp/source.html 2>/dev/nullfor ip in `cat /tmp/ip.txt`
docurl -x $ip:80 $url 2>/dev/null >/tmp/$ip.html[ -f /tmp/$ip.diff ] && rm -f /tmp/$ip.difftouch /tmp/$ip.diffdiff /tmp/source.html /tmp/$ip.html > /tmp/$ip.diff 2>/dev/nulln=`wc -l /tmp/$ip.diff|awk '{print $1}'`if [ $n -lt 0 ]thenecho "node $ip sth wrong."fi
done

【脚本79】自动添加项目
需求背景:
服务器上,跑的lamp环境,上面有很多客户的项目,每个项目就是一个网站。 由于客户在不断增加,每次增加一个客户,就需要配置相应的mysql、ftp以及httpd. 这种工作是重复性非常强的,所以用脚本实现非常合适。

mysql增加的是对应客户项目的数据库、用户、密码,ftp增加的是对应项目的用户、密码(使用vsftpd,虚拟用户模式),httpd就是要增加虚拟主机配置段。

参考代码:

#!/bin/bash
webdir=/home/wwwroot
ftpudir=/etc/vsftpd/vuuser
mysqlc="/usr/bin/mysql -uroot -xxxxxx"
httpd_config_f="/usr/local/apache2/conf/extra/httpd-vhosts.conf"add_mysql_user()
{mysql_p=`mkpasswd -s 0 -l 12`echo "$pro $mysql_p" >/tmp/$pro.txt$mysqlc <<EOFgrant all on $p.* to "$pro"@'127.0.0.1' identified by "$mysql_p";
EOF
}add_ftp_user(){ftp_p=`mkpasswd -s 0 -l 12`echo "$pro" >> /root/login.txtecho "$ftp_p" >> /root/login.txtdb_load -T -t hash -f /root/login.txt  /etc/vsftpd/vsftpd_login.dbcd $ftpudircp aaa $pro   //这里的aaa是一个文件,是之前的一个项目,可以作为配置模板sed -i "s/aaa/$pro/" $pro  //把里面的aaa改为新的项目名字/etc/init.d/vsftpd restart
}config_httpd(){mkdir $webdir/$prochown vsftpd:vsftpd $webdir/$proecho -e "<VirtualHost *:80> \n     DocumentRoot "/home/internet/www/$pro/" \n     ServerName $dom \n    #ServerAlias \n</VirtualHost> " >> $httpd_config_f/usr/local/apache2/bin/apachectl graceful
}read -p "input the project name: " pro
read -p "input the domain: " domadd_mysql_user
add_ftp_user
config_httpd
处理日志

写一个脚本查找/data/log目录下,最后创建时间是3天前,后缀是*.log的文件,打包后发送至192.168.1.2服务上的/data/log下,并删除原始.log文件,仅保留打包后的文件

参考代码:

#!/bin/bash
find /data/log -name “*.log” -mtime +3 > /tmp/file.list
cd /data/log
tar czvf log.tar.gz `cat /tmp/file.list|xargs`
rsync -a log.tar.gz  192.168.1.2:/data/log  # 这一步需要提前做一个免密码登录
for f in `cat /tmp/file.list`
dorm -f $f
done
处理文本

有如下文本,其中前5行内容为

1111111:13443253456
2222222:13211222122
1111111:13643543544
3333333:12341243123
2222222:12123123123
用shell脚本处理后,按下面格式输出:

[1111111]
13443253456
13643543544
[2222222]
13211222122
12123123123
[3333333]
12341243123
参考代码:

#! /bin/bash
sort -n filename |awk -F ':' '{print $1}'|uniq >id.txtfor id in `cat id.txt`; doecho "[$id]"awk -v id2=$id -F ':' '$1==id2 {print $2}' filename  #另外的方式为: awk -F ':' '$1=="'$id'" {print $2}' filename
done
清理日志

要求:两类机器一共300多台,写个脚本自动清理这两类机器里面的日志文件。在堡垒机批量发布,也要批量发布到crontab里面。

A类机器日志存放路径很统一,B类机器日志存放路径需要用匹配(因为这个目录里除了日志外,还有其他文件,不能删除。匹配的时候可用.log)

A类:/opt/cloud/log/ 删除7天前的
B类: /opt/cloud/instances/ 删除15天前的

要求写在一个脚本里面。不用考虑堡垒机上的操作,只需要写出shell脚本。

参考代码:

#!/bin/bash
dir1=/opt/cloud/instances/
dir2=/opt/cloud/log/if [ -d $dir1 ];thenfind $dir1 -type f -name "*.log" -mtime +15 |xargs rm -felif [ -d $dir2 ];thenfind $dir2 -type f -mtime +7 |xargs rm -ffi
监控磁盘io

阿里云的机器,今天收到客服来的电话,说服务器的磁盘io很重。于是登录到服务器查看,并没有发现问题,所以怀疑是间歇性地。

正要考虑写个脚本的时候,幸运的抓到了一个线索,造成磁盘io很高的幕后黑手是mysql。此时去show processlist,但未发现队列。原来只是一瞬间。

只好继续来写脚本,思路是,每5s检测一次磁盘io,当发现问题去查询mysql的processlist。

提示:你可以用iostat -x 1 5 来判定磁盘的io,主要看%util

参考代码:

#!/bin/bash
while :
don=`iostat -x 1 5 |tail -n3|head -n1 |awk '{print $NF}'|cut -d. -f1`if [ $n -gt 70 ]thenecho "`date` util% is $n%" >>/tmp/mysql_processlist.logmysql -uroot -pxxxxxx -e "show  full processlist" >> /tmp/mysql_processlist.logfisleep 5
done
截取tomcat日志

写一个截取tomcat catalina.out日志的脚本。

tomcat实例t1-t4:

[root@server ~]# tree -L 1 /opt/TOM/
/opt/TOM/
├── crontabs
├── t1
├── t2
├── t3
└── t45 directories, 0 files

catalina.out日志路径:

[root@server ~]# find /opt/TOM/ -name catalina.out
/opt/TOM/t1/logs/catalina.out
/opt/TOM/t3/logs/catalina.out
/opt/TOM/t4/logs/catalina.out
/opt/TOM/t2/logs/catalina.out

要求:

这个脚本可以取tomcat实例t1-t4的日志
这个脚本可以自定义取日志的起始点,比如取今天早上10点之后到现在的数据
这个脚本可以自定义取日志的起始点和终点,比如取今天早上9点到晚上8点的数据
catalina.out 日志片段:

Mar 29, 2016 1:52:24 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler [“http-bio-8080”]
Mar 29, 2016 1:52:24 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler [“ajp-bio-8009”]
Mar 29, 2016 1:52:24 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 2102 ms

参考代码:

#!/bin/bashexport LANG=en_US.UTF-8
export PATH=$PATH
IPADD=`/sbin/ifconfig | grep "inet addr" | head -1 | awk '{print $2}'| awk -F '.' '{print $NF}'`
LOGFILE="/opt/TOM/$1/logs/catalina.out"
YEAR=`date +%Y`
DATE=`date +%m%d_%H%M`
TOMCAT=$1
BEGIN_TIME=$YEAR$2
END_TIME=$YEAR$3##judge is  a.m.or p.m.
TIME_HOUR1=`echo ${BEGIN_TIME:9:2}`cut_log() {N_DATE1=`echo $1 | sed 's/_/ /g'`D_DATE1=`echo $2 | sed 's/_/ /g'`E_DATE1=`echo $3 | sed 's/_/ /g'`[ $4 ] && N_DATE2=`echo $4 | sed 's/_/ /g'`[ $5 ] && D_DATE2=`echo $5 | sed 's/_/ /g'`[ $6 ] && E_DATE2=`echo $6 | sed 's/_/ /g'`BEGIN=`grep -nE "${N_DATE1}|${D_DATE1}|${E_DATE1}" ${LOGFILE} | head -1 | cut -d : -f1`[ "$N_DATE2" ] && END=`grep -nE "${N_DATE2}|${D_DATE2}|${E_DATE2}" ${LOGFILE} | tail -1 | cut -d : -f1`[ ! -z "${TIME_HOUR1}" ] && if [ ${TIME_HOUR1} -gt 12 ] ; thenBEGIN1=`grep -nE "${N_DATE1}|${D_DATE1}|${E_DATE1}" ${LOGFILE} |grep " PM " |grep "${E_DATE1}" | head -1 | cut -d : -f1`if [ ! -z "${BEGIN1}" ] ; then[ "${BEGIN1}" -gt "${BEGIN}" ] ; BEGIN=${BEGIN1}fifiif [ "$BEGIN" ] && [ -z "$END" ] ; thenif [ "$N_DATE2" ]; thenecho  "${END_TIME}时间点没有访问日志,请重新设置时间点."elsesed -n "${BEGIN},[        DISCUZ_CODE_0        ]quot;p ${LOGFILE} > /home/gcweb/${IPADD}_${TOMCAT}_${DATE}.logfielif [ "$END" ];then[ "$BEGIN" ] || BEGIN=1sed -n "${BEGIN},${END}"p ${LOGFILE} > /home/gcweb/${IPADD}_${TOMCAT}_${DATE}.logelse[ "$END_TIME" != "$YEAR" ] && echo "该时段 ${BEGIN_TIME}~${END_TIME} 没有日志."[ "$END_TIME" = "$YEAR" ] && echo "该时段 ${BEGIN_TIME}~now 没有日志."fiif [ -s /home/gcweb/${IPADD}_${TOMCAT}_${DATE}.log ]; thencd /home/gcweb  &&  tar -zcf ${IPADD}_${TOMCAT}_${DATE}.tar.gz ${IPADD}_${TOMCAT}_${DATE}.logrm -f /home/gcweb/${IPADD}_${TOMCAT}_${DATE}.logsz /home/gcweb/${IPADD}_${TOMCAT}_${DATE}.tar.gzecho "Success to get logs."rm -f /home/gcweb/${IPADD}_${TOMCAT}_${DATE}.tar.gzfi
}
get_time() {case "$1" in4)      N_DATE=`date -d "$2" +"%Y-%m-%d" 2>/dev/null`D_DATE=`date -d "$2" +"%Y/%m/%d" 2>/dev/null`E_DATE=`date -d "$2" +"%h %e,_%Y" 2>/dev/null|sed 's/ /_/g'`echo $N_DATE $D_DATE $E_DATE;;      7)      TIME=`echo $2 | awk -F'_' '{print $1,$2}'`N_DATE=`date -d "$TIME" +"%Y-%m-%d_%H" 2>/dev/null`D_DATE=`date -d "$TIME" +"%Y/%m/%d_%H" 2>/dev/null`E_DATE=`date -d "$TIME" +"%h %e,_%Y %l" 2>/dev/null|sed 's/ /_/g'`echo  "$N_DATE"  "$D_DATE" "$E_DATE";;9)     TIME=`echo $2 | awk -F'_' '{print $1,$2}'`N_DATE=`date -d "$TIME" +"%Y-%m-%d_%H:%M" 2>/dev/null`D_DATE=`date -d "$TIME" +"%Y/%m/%d_%H:%M" 2>/dev/null`E_DATE=`date -d "$TIME" +"%h %e,_%Y %l:%M" 2>/dev/null|sed 's/ /_/g'`echo  "$N_DATE" "$D_DATE" "$E_DATE";;*)      echo 1;;esac
}
check_arguments () {if [ "$1" == 1 ] || [ -z  "$1" ] ;thenecho "你输入时间参数的格式无法识别, usage: 0108、0108_10、0108_1020"exit 3fi
}
check_tomcat () {if [ ! -s "${LOGFILE}" ] ;thenecho "tomcat_name: ${TOMCAT} is not exist"echo "you can choose:"/bin/ls  /home/gcweb/usr/local/fiif [ $1 -lt 2 ] || [ ! -s "${LOGFILE}" ];thenecho "usage: $0 tomcat_name {begin_time|begin_time end_time}"exit 2fi
}
case "$#" in0)echo "usage: $0 tomcat_name {begin_time|begin_time end_time}"exit 1;;1)check_tomcat $#;;2)check_tomcat $#len=`echo $2 | awk '{print length($0)}'`A_DATE=$(get_time  $len $BEGIN_TIME)eval  $( echo $A_DATE |awk '{print "N_DATE="$1,"D_DATE="$2,"E_DATE="$3}')check_arguments "${N_DATE}"cut_log "${N_DATE}" "${D_DATE}" "${E_DATE}";;3)check_tomcat $#len1=`echo $2 | awk '{print length($0)}'`len2=`echo $3 | awk '{print length($0)}'`A_DATE=$(get_time ${len1}  $BEGIN_TIME)eval  $( echo $A_DATE |awk '{print "N_DATE1="$1,"D_DATE1="$2,"E_DATE1="$3}')check_arguments "${N_DATE1}"A_DATE=$(get_time ${len2}  $END_TIME)eval  $( echo $A_DATE |awk '{print "N_DATE="$1,"D_DATE="$2,"E_DATE="$3}')check_arguments "${N_DATE}"cut_log ${N_DATE1} ${D_DATE1} ${E_DATE1} "${N_DATE}" "${D_DATE}" "${E_DATE}";;*)echo "usage: $0 tomcat_name {begin_time|begin_time end_time};你使用的参数太多哦.";;
esac
批量同步代码

需求背景是:

一个业务,有3台服务器(A,B,C)做负载均衡,由于规模太小目前并未使用专业的自动化运维工具。有新的需求时,开发同事改完代码会把变更上传到其中一台服务器A上。但是其他2台服务器也需要做相同变更。

写一个shell脚本,把A服务器上的变更代码同步到B和C上。

其中,你需要考虑到不需要同步的目录(假如有tmp、upload、logs、caches)

参考代码:

#!/bin/bash
echo "该脚本将会把A机器上的/data/wwwroot/www.aaa.com目录同步到B,C机器上";
read -p "是否要继续?(y|n) "rs() {rsync -azP \--exclude logs \--exclude upload \--exclude caches \--exclude tmp \
www.aaa.com/ $1:/data/wwwroot/www.aaa.com/}if [ $REPLY == 'y' -o $REPLY == 'Y' ]
thenecho "即将同步……"sleep 2cd /data/wwwroot/rs B机器iprs C机器ipecho "同步完成。"
elif [ $REPLY == 'n' -o $REPLY == 'N' ]
thenexit 1
elseecho "请输入字母y或者n"
fi
统计并发量

需求背景:

需要统计网站的并发量,并绘图。
思路:

借助zabbix成图
通过统计访问日志每秒的日志条数来判定并发量
zabbix获取数据间隔30s
说明: 只需要写出shell脚本即可,不用关心zabbix配置。

假设日志路径为:

/data/logs/www.aaa.com_access.log

日志格式如下:

112.107.15.12 - [07/Nov/2017:09:59:01 +0800] www.aaa.com "/api/live.php" 200"-" "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)"

参考代码:

#!/bin/bash
log=/data/logs/www.aaa.com_access.log
t=`date -d "-1 second" +%Y:%H:%M:%S`
#可以大概分析一下每分钟日志的量级,比如说不超过3000
n=tail -3000 $log |grep -c "$t"
echo $n
重启tomcat服务

在生产环境中,经常遇到tomcat无法彻底关闭,也就是说用tomcat自带shutdown.sh脚本无法将java进程完全关掉。所以,需要借助shell脚本,将进程杀死,然后再启动。

写一个shell脚本,实现上述功能。彻底杀死一个进程的命令是 kill -9 pid.

参考代码:

#!/bin/bash
###功能: 重启 tomcat 进程
###要求:对于tomcat中的某些应用,使用shutdown.sh是无法完全停掉所有服务的 实际操作中都需要kill掉tomcat再重启
##
### root can not run this script.
##if [ $USER = root ]
thenecho "root cann't run this script!please run with other user!"exit 1
fi##
### check the Parameter
##if [[ $# -ne 1 ]]
thenecho "Usage:$0 tomcatname"exit 1
fi##
### only one process can run one time
##TMP_FILE_U=/tmp/.tmp.ps.keyword.$USER.956327.txt
#echo $TMP_FILE_U
KEYWORD1="$0"
KEYWORD2="$1"# 使用赋值会多fork出一个进程,所以要先重定向到一个文本,再统计.ps ux |grep "$KEYWORD1"|grep "\<$KEYWORD2\>"|grep -v "grep" > $TMP_FILE_U
Pro_count=`cat $TMP_FILE_U |wc -l`
if [ $Pro_count -gt 1 ]
thenecho "An other process already running ,exit now!"exit 1
fi###################################################
#                                                 #
#               begin of the script               #
#                                                 #
#####################################################
### set the Parameter
##TOM=`echo $1|sed 's#/##g'`
TOMCAT_DIRECTORY=~/usr/local/$TOM
STARTUP_SCRIPT=$TOMCAT_DIRECTORY/bin/startup.sh
TOMCAT_LOG=$TOMCAT_DIRECTORY/logs/catalina.out
CONF_FILE=$TOMCAT_DIRECTORY/conf/server.xml
TEMPFILE=/tmp/.tmpfile.x.89342.c4r3.tmp##
### check if the tomcat directory exist
##if [ ! -d "$TOMCAT_DIRECTORY" ]
thenecho "the tomcat \"$TOM\" not exist.check again!"exit 1
fi##
### log roteta and delete log one week ago
##rotate_log(){
TIME_FORMART=$(date +%Y%m%d%H%M%S)
LOG_DIR=$(dirname $TOMCAT_LOG)
mv $TOMCAT_LOG ${TOMCAT_LOG}_${TIME_FORMART}
find $LOG_DIR -type f -ctime +7 -exec rm -rf {} \;
}##
### function start the tomcat
##start_tomcat()
{
#echo start-tomcat-func
if [ -x  "$STARTUP_SCRIPT" ]
thenrotate_log$STARTUP_SCRIPTsleep 1tail -f $TOMCAT_LOG
elseif [ -e $STARTUP_SCRIPT ]thenchmod +x $STARTUP_SCRIPT
#               echo "permition added!"if [ -x  "$STARTUP_SCRIPT" ]thenrotate_log$STARTUP_SCRIPTsleep 1tail -f $TOMCAT_LOGelseecho "The script not have excute permision,Couldn't add permision to Script!"exit 1fielseecho "error,the script \"startup.sh\" not exist!"exit 1fi
fi
}##
### function stop the tomcat
##stop_tomcat()
{
rm -rf $TEMPFILE
ps ux |grep /$TOM/ |grep -v "grep /$TOM/"|grep java > $TEMPFILE
Pro_Count=`cat $TEMPFILE|wc -l`
PIDS=`cat $TEMPFILE|awk '{print $2}'`
rm -rf $TEMPFILE
#echo $Pro_Countif [ $Pro_Count -eq 0 ]
thenecho "The tomcat not running now!"
elseif [ $Pro_Count -ne 1 ]thenecho "The have $Pro_Count process running,killed!"kill -9 `echo $PIDS`WC=`ps aux | grep "/$TOM/" | grep -v "grep /$TOM/" | grep java |wc -l`[ $WC -ne 0 ] && (echo "kill process failed!";exit 1)elseecho "Process killed!"kill -9 `echo $PIDS`WC=`ps aux | grep "/$TOM/" | grep -v "grep /$TOM/" | grep java |wc -l`[ $WC -ne 0 ] && (echo "kill process failed!";exit 1)fi
fi
}###########################
####                   ####
####  The main script  ####
####                   ####
###########################echo -e "are you sure restart $TOM?(y or n)"
read ANS
if [ "$ANS"a != ya ]
thenecho -e "bye! \n"exit 1
fistop_tomcat
echo "start tomcat ..."
sleep 2
start_tomcat
# end
域名到期提醒

写一个shell脚本,查询指定域名的过期时间,并在到期前一周,每天发一封提醒邮件。

思路: 大家可以在linux下使用命令“whois 域名”,如”whois xxx.com”,来获取该域名的一些信息。

提示: whois命令,需要安装jwhois包

参考代码:

#!/bin/bash
t1=`date +%s`
is_install_whois()
{which whois >/dev/null 2>/dev/nullif [ $? -ne 0 ]thenyum install -y jwhoisfi
}notify()
{e_d=`whois $1|grep 'Expiry Date'|awk '{print $4}'|cut -d 'T' -f 1`e_t=`date -d "$e_d" +%s`n=`echo "86400*7"|bc`e_t1=$[$e_t-$n]if [ $t1 -ge $e_t1 ] && [ $t1 -lt $e_t ]then/usr/local/sbin/mail.py aming_test@163.com "Domain $1 will be expire." "Domain $1 expire date is $e_d."fi
}is_install_whois
notify xxx.com
自动增加公钥

写一个shell脚本,当我们执行时,提示要输入对方的ip和root密码,然后可以自动把本机的公钥增加到对方机器上,从而实现密钥认证。

参考代码:

#!/bin/bash
read -p "Input IP: " ip
ping $ip -w 2 -c 2 >> /dev/null## 查看ip是否可用
while [ $? -ne 0 ]
doread -p "your ip may not useable, Please Input your IP: " ipping $ip -w 2 -c 2 >> /dev/null
done
read -p "Input root\'s password of this host: " password## 检查命令子函数check_ok() {
if [ $? != 0 ]
thenecho "Error!."exit 1
fi
}## yum需要用到的包
myyum() {
if ! rpm -qa |grep -q "$1"
thenyum install -y $1check_ok
elseecho $1  already installed
fi
}for p in openssh-clients openssh expect
domyyum $p
done## 在主机A上创建密钥对if [ ! -f ~/.ssh/id_rsa ] || [ ! -f ~/.ssh/id_rsa.pub ]
thenif [ -d ~/.ssh ]thenmv ~/.ssh/  ~/.ssh_oldfiecho -e "\n" | ssh-keygen -t rsa -P ''check_ok
fi## 传私钥给主机Bif [ ! -d /usr/local/sbin/rsync_keys ]
thenmkdir /usr/local/sbin/rsync_keys
fi
cd /usr/local/sbin/rsync_keys
if [ -f rsync.expect ]
thend=`date +%F-%T`mv rsync.expect $d.expect
fi#创建远程同步的expect文件cat >  rsync.expect <<EOF
#!/usr/bin/expect
set host [lindex \$argv 0]
#主机B的密码
set passwd [lindex \$argv 1]
spawn rsync -av /root/.ssh/id_rsa.pub root@\$host:/tmp/tmp.txt
expect {
"yes/no" { send "yes\r"; exp_continue}
"password:" { send "\$passwd\r" }
}
expect eof
spawn ssh root@\$host
expect {
"password:" { send "\$passwd\r" }
}
expect "]*"
send "\[ -f /root/.ssh/authorized_keys \] && cat /tmp/tmp.txt >>/root/.ssh/authorized_keys \r"
expect "]*"
send "\[ -f /root/.ssh/authorized_keys \] || mkdir -p /root/.ssh/ \r"
send "\[ -f /root/.ssh/authorized_keys \] || mv /tmp/tmp.txt /root/.ssh/authorized_keys\r"
expect "]*"
send "chmod 700 /root/.ssh; chmod 600 /root/.ssh/authorized_keys\r"
expect "]*"
send "exit\r"
EOFcheck_ok
/usr/bin/expect /usr/local/sbin/rsync_keys/rsync.expect $ip $password
echo "OK,this script is successful. ssh $ip  to test it"
自动封/解封ip

需求背景:

discuz论坛,每天有很多注册机注册的用户,然后发垃圾广告帖子。虽然使用了一些插件但没有效果。分析访问日志,发现有几个ip访问量特别大,所以想到可以写个shell脚本,通过分析访问日志,把访问量大的ip直接封掉。

但是这个脚本很有可能误伤,所以还需要考虑到自动解封这些ip。

思路:

可以每分钟分析1次访问日志,设定一个阈值,把访问量大的ip用iptables封掉80端口
每20分钟检测一次已经被封ip的请求数据包数量,设定阈值,把没有请求的或者请求量很小的解封
参考代码:

#! /bin/bash
## To block the ip of bad requesting.
## Writen by aming 2017-11-18.log="/data/logs/www.xxx.com.log"
tmpdir="/tmp/badip"
#白名单ip,不应该被封
goodip="27.133.28.101"[ -d $tmpdir ] || mkdir -p $tmpdirt=`date -d "-1 min"  +%Y:%H:%M`#截取一分钟以前的日志
grep "$t:" $log > $tmpdir/last_min.log#把一分钟内日志条数大于120的标记为不正常的请求
awk '{print $1}' $tmpdir/last_min.log |sort -n |uniq -c |sort -n |tail |awk '$1>120 {print $2}'|grep -v "$good_ip"> $tmpdir/bad.ipd3=`date +%M`#每隔20分钟解封一次ip
if [ $d3 -eq "20" ] || [ $d3 -eq "40" ] || [ $d3 -eq "00" ]
then/sbin/iptables -nvL INPUT|grep 'DROP' |awk '$1<10 {print $8}'>$tmpdir/good.ipif [ -s $tmpdir/good.ip ]thenfor ip in `cat $tmpdir/good.ip`do/sbin/iptables -D INPUT -p tcp --dport 80 -s $ip -j DROPd4=`date +%Y%m%d-%H:%M`echo "$d4 $ip unblock" >>$tmpdir/unblock.ipdonefi#解封后,再把iptables的计数器清零/sbin/iptables -Z INPUT
fiif [ -s $tmpdir/bad.ip ]
thenfor ip in `cat $tmpdir/bad.ip`do/sbin/iptables -A INPUT -p tcp --dport 80 -s $ip -j DROPd4=`date +%Y%m%d-%H:%M`echo "$d4 $ip block" >>$tmpdir/block.ipdone
fi
单机部署SpringBoot项目

有一台测试服务器,经常需要部署SpringBoot项目,手动部署太麻烦,于是写了个部署脚本

脚本代码:

#!/bin/bash
# git仓库路径
GIT_REPOSITORY_HOME=/app/developer/git-repository
# jar包发布路径
PROD_HOME=/prod/java-back
# 应用列表
APPS=(app1 app2 app3)if [ ! -n "$1" ]
thenecho -e "请输入要发布的项目!"exit
fi# cd dir
for((i=0;i<${#APPS[@]};i++))
doecho $1 ${APPS[i]}if [ $1 = ${APPS[i]} ]thenecho -e ===========Enter $1=============cd ${GIT_REPOSITORY_HOME}/$1breakfi
doneif [ `pwd` != ${GIT_REPOSITORY_HOME}/$1 ]
thenecho -e "输入的项目名没有找到!"exit
fiecho "==========git切换分之到master==============="
git checkout masterecho "==================git fetch======================"
git fetchecho "==================git pull======================"
git pullecho "===========选择线上环境编译并跳过单元测试===================="
mvn clean package -Dmaven.test.skip=true -Pprodjar_path=${GIT_REPOSITORY_HOME}/$1/target/*-0.0.1-SNAPSHOT.jar
echo ${jar_path}
if [ -f ${jar_path} ]
then# backup destecho -e "===========jar backup============="mv ${PROD_HOME}/$1/*-0.0.1-SNAPSHOT.jar ${PROD_HOME}/$1/$1-0.0.1-SNAPSHOT.jar.back# copyecho "======拷贝编译出来的jar包拷贝到$PROD_HOME======="if [ -d ${PROD_HOME}/$1 ]then/bin/cp ${GIT_REPOSITORY_HOME}/$1/target/*-0.0.1-SNAPSHOT.jar  ${PROD_HOME}/$1elsemkdir ${PROD_HOME}/$1/bin/cp ${GIT_REPOSITORY_HOME}/$1/target/*-0.0.1-SNAPSHOT.jar  ${PROD_HOME}/$1fiecho "============停止项目============="jar_name=`jps |awk -F" " '{ print $2 }'| egrep ^$1.*jar$`pid=`jps |grep ${jar_name} | awk -F" " '{ print $1 }'`echo ${pid}kill -15 ${pid}echo "================sleep 10s========================="for i in 1 2 3 4 5 6 7 8 9 10doecho ${i}"s"sleep 1sdoneecho "============启动项目============="nohup java -jar ${PROD_HOME}/$1/*-0.0.1-SNAPSHOT.jar > /dev/null 2>&1 &echo -e "===========Deploy Success============="
elseecho -e "===========Deploy Error============="
fi

linux shell的一些注意点,以及一些代码示例相关推荐

  1. linux shell中实现循环日期的实例代码

    这篇文章主要介绍了linux shell中实现循环日期的实例代码,文中还给大家提到了LINUX SHELL遍历日期(指定输入两个日期)的实现方法,感兴趣的朋友跟随小编一起看看吧 下面通过一段代码给大家 ...

  2. Linux Shell学习笔记:exit退出状态代码

    inux提供$?特殊变量来保存最后一条命令执行结束的退出状态.执行完一条命令后,立即执行echo$?,可以查看最后一条命令的退出状态值. 正常的情况下,命令成功执行完成的退出状态是0,如果非0,则命令 ...

  3. Linux: shell命令 eval (有图有代码有真相!!!)

    一.eval 命令定义 shell中的eval命令将会首先扫描命令行进行所有的替换,然后再执行命令.该命令使用于那些一次扫描无法实现其功能的变量. 该命令对变量进行两次扫描.这些需要进行两次扫描的变量 ...

  4. linux欢迎信息打印本机ip,Linux shell 登录显示欢迎信息或机器信息(示例代码)

    写这个脚本的背景:因为管理的机器越来越多,每次登录机器都可以显示相应机器的信息防止误操作. 同时个人工作的环境有多个跳板机, 也容易操作错误,  所以每台机器上加个登录显示. 创建登录显示脚本 修改 ...

  5. Linux Shell中判断进程是否存在的代码

    1 利用pgrep 匹配名字 复制代码 代码如下: if test $( pgrep -f $1 | wc -l ) -eq 0 then echo "进程不存在" else ec ...

  6. 一个很牛的 linux shell脚本编写的俄罗斯方块游戏实例代码,非常牛逼

    本文转载:https://www.qiansw.com/shell-tetris-game.html 可以将下面代码直接保存为sh文件. #!/bin/bash   # Tetris Game # 1 ...

  7. linux进度条脚本,linux shell进度条实现方法

    linux shell进度条实现方法 直接看代码吧,很简单 复制代码 代码如下: #!/bin/bash b='' i=0 while [ $i -le  100 ] do printf " ...

  8. linux shell $0怎么输出,linux shell中$0,$?,$!等的特殊用法

    一.Shell脚本中$0.$?.$!.$$.$*.$#.$@等的意义说明 (1)$$ Shell本身的PID(ProcessID,即脚本运行的当前进程ID号) (2)$! Shell最后运行的后台Pr ...

  9. linux shell for 循环变量,shell for循环总结

    1 shell for循环语法 for 变量 in 列表 do command1 command2 ... commandN done 1.1 读取列表中的值 #!/bin/bash for test ...

  10. 学习笔记之Linux Shell脚本教程:30分钟玩转Shell脚本编程

    Linux Shell脚本教程:30分钟玩转Shell脚本编程 http://c.biancheng.net/cpp/shell/ 转载于:https://www.cnblogs.com/pegasu ...

最新文章

  1. Redis数据类型:散列类型
  2. Springboot,SSM框架比较,区别
  3. Verilog中for 语句怎么用
  4. 装箱与拆箱及其性能损失问题
  5. conductor任务域
  6. 《openssl 编程》之错误处理
  7. Java中字符串中子串的查找共有四种方法(indexof())
  8. PHP substr_replace() 函数
  9. 谷歌技术三宝之BigTable(转)
  10. qpoases算法复杂度_数值优化(Numerical Optimization)学习系列-无梯度优化(Derivative-Free Optimization)...
  11. springboot添加ojdbc6报错
  12. hutool中的threadutil_Hutool
  13. 百度网盘视频加速播放
  14. CSS中的伪类选择器、颜色、度量单位、文本字体及文本样式设置
  15. UE5 Metahuman使用Live Link Face动画不匹配的问题修复
  16. IDEA2021.2安装与配置(持续更新)
  17. oracle创建用户saler,Linux环境下sqlldr一个csv文件
  18. Proteus8 发生关键仿真错误
  19. 我的k8s随笔:Kubernetes 1.17.0 部署
  20. 浏览器中优秀的收藏夹书签

热门文章

  1. 你能给芯片产业贡献什么价值?李力游博士发问
  2. 用英语翻译那是计算机房,几点了用英语怎么说
  3. springcloud-eureka详解
  4. 本周首次公开专利数量最多的主要公司(2022.08.01~2022.08.07日)
  5. Wordpress主题XIU5.6去授权破解版版免费分享
  6. python axes_Python Matplotlib.axes.Axes.axis()用法及代码示例
  7. python axes函数_Python Matplotlib.axes.Axes.axvline()用法及代码示例
  8. 程序设计入门——C语言 习题汇总
  9. 为什么搜狗拼音老是打出繁体字?怎么切换成简体字?
  10. eclispe简体与繁体字切换