![02a456d92f58584f1fa602df9be7ae42.png](https://i-blog.csdnimg.cn/blog_migrate/e9b91876077422e490b92bd30dc59082.jpeg)
shell脚本是帮助程序员和系统管理员完成费时费力的枯燥工作的利器,是与计算机交互并管理文件和系统操作的有效方式。区区几行代码,就可以让计算机接近按照你的意图行事。
博智互联为大家整理了100个实例,通过100个实战经典脚本实例,展示了shell脚本编程的实用技术和常见工具用法。大家只需根据自己的需求,将文中这些常见任务和可移植自动化脚本推广应用到其他类似问题上,能解决那些三天两头碰上的麻烦事。
判断用户输入的是否为数字
方法1:#!/bin/bashif [[ $1 =~ ^[0-9]+$ ]]; then echo "Is Number."else echo "No Number."fi方法2:#!/bin/bashif [ $1 -gt 0 ] 2>/dev/null; then echo "Is Number."else echo "No Number."fi方法3:#!/bin/bashecho $1 |awk '{print $0~/^[0-9]+$/?"Is Number.":"No Number."}' #三目运算符12.14 找出包含关键字的文件DIR=$1KEY=$2for FILE in $(find $DIR -type f); do if grep $KEY $FILE &>/dev/null; then echo "--> $FILE" fidone
监控目录,将新创建的文件名追加到日志中
场景:记录目录下文件操作。需先安装inotify-tools软件包。#!/bin/bashMON_DIR=/optinotifywait -mq --format %f -e create $MON_DIR |while read files; do echo $files >> test.logdone
给用户提供多个网卡选择
场景:服务器多个网卡时,获取指定网卡,例如网卡流量#!/bin/bashfunction local_nic() { local NUM ARRAY_LENGTH NUM=0 for NIC_NAME in $(ls /sys/class/net|grep -vE "lo|docker0"); do NIC_IP=$(ifconfig $NIC_NAME |awk -F'[: ]+' '/inet addr/{print $4}') if [ -n "$NIC_IP" ]; then NIC_IP_ARRAY[$NUM]="$NIC_NAME:$NIC_IP" #将网卡名和对应IP放到数组 let NUM++ fi done ARRAY_LENGTH=${#NIC_IP_ARRAY[*]} if [ $ARRAY_LENGTH -eq 1 ]; then #如果数组里面只有一条记录说明就一个网卡 NIC=${NIC_IP_ARRAY[0]%:*} return 0 elif [ $ARRAY_LENGTH -eq 0 ]; then #如果没有记录说明没有网卡 echo "No available network card!" exit 1 else #如果有多条记录则提醒输入选择 for NIC in ${NIC_IP_ARRAY[*]}; do echo $NIC done while true; do read -p "Please enter local use to network card name: " INPUT_NIC_NAME COUNT=0 for NIC in ${NIC_IP_ARRAY[*]}; do NIC_NAME=${NIC%:*} if [ $NIC_NAME == "$INPUT_NIC_NAME" ]; then NIC=${NIC_IP_ARRAY[$COUNT]%:*} return 0 else COUNT+=1 fi done echo "Not match! Please input again." done fi}local_nic
MySQL数据库备份
#!/bin/bashDATE=$(date +%F_%H-%M-%S)HOST=192.168.1.120DB=testUSER=bakPASS=123456MAIL="zhangsan@example.com lisi@example.com"BACKUP_DIR=/data/db_backupSQL_FILE=${DB}_full_$DATE.sqlBAK_FILE=${DB}_full_$DATE.zipcd $BACKUP_DIRif mysqldump -h$HOST -u$USER -p$PASS --single-transaction --routines --triggers -B $DB > $SQL_FILE; then zip $BAK_FILE $SQL_FILE && rm -f $SQL_FILE if [ ! -s $BAK_FILE ]; then echo "$DATE 内容" | mail -s "主题" $MAIL fielse echo "$DATE 内容" | mail -s "主题" $MAILfifind $BACKUP_DIR -name '*.zip' -ctime +14 -exec rm {} ;
Nginx服务管理脚本
场景:使用源码包安装Nginx不含带服务管理脚本,也就是不能使用"service nginx start"或"/etc/init.d/nginx start",所以写了以下的服务管理脚本。#!/bin/bash# Description: Only support RedHat system. /etc/init.d/functionsWORD_DIR=/usr/local/nginxDAEMON=$WORD_DIR/sbin/nginxCONF=$WORD_DIR/conf/nginx.confNAME=nginxPID=$(awk -F'[; ]+' '/^[^#]/{if($0~/pid;/)print $2}' $CONF)if [ -z "$PID" ]; then PID=$WORD_DIR/logs/nginx.pidelse PID=$WORD_DIR/$PIDfistop() { $DAEMON -s stop sleep 1 [ ! -f $PID ] && action "* Stopping $NAME" /bin/true || action "* Stopping $NAME" /bin/false}start() { $DAEMON sleep 1 [ -f $PID ] && action "* Starting $NAME" /bin/true || action "* Starting $NAME" /bin/false}reload() { $DAEMON -s reload}test_config() { $DAEMON -t}case "$1" in start) if [ ! -f $PID ]; then start else echo "$NAME is running..." exit 0 fi ;; stop) if [ -f $PID ]; then stop else echo "$NAME not running!" exit 0 fi ;; restart) if [ ! -f $PID ]; then echo "$NAME not running!" start else stop start fi ;; reload) reload ;; testconfig) test_config ;; status) [ -f $PID ] && echo "$NAME is running..." || echo "$NAME not running!" ;; *) echo "Usage: $0 {start|stop|restart|reload|testconfig|status}" exit 3 ;;esac
用户根据菜单选择要连接的Linux主机
Linux主机SSH连接信息:# cat host.txtWeb 192.168.1.10 root 22DB 192.168.1.11 root 22内容格式:主机名 IP User Port#!/bin/bashPS3="Please input number: "HOST_FILE=host.txtwhile true; do select NAME in $(awk '{print $1}' $HOST_FILE) quit; do [ ${NAME:=empty} == "quit" ] && exit 0 IP=$(awk -v NAME=${NAME} '$1==NAME{print $2}' $HOST_FILE) USER=$(awk -v NAME=${NAME} '$1==NAME{print $3}' $HOST_FILE) PORT=$(awk -v NAME=${NAME} '$1==NAME{print $4}' $HOST_FILE) if [ $IP ]; then echo "Name: $NAME, IP: $IP" ssh -o StrictHostKeyChecking=no -p $PORT -i id_rsa $USER@$IP # 密钥免交互登录 break else echo "Input error, Please enter again!" break fi donedone
将结果分别赋值给变量
应用场景:希望将执行结果或者位置参数赋值给变量,以便后续使用。方法1:for i in $(echo "4 5 6"); do eval a$i=$idoneecho $a4 $a5 $a6方法2:将位置参数192.168.1.1{1,2}拆分为到每个变量num=0for i in $(eval echo $*);do #eval将{1,2}分解为1 2 let num+=1 eval node${num}="$i"doneecho $node1 $node2 $node3# bash a.sh 192.168.1.1{1,2}192.168.1.11 192.168.1.12方法3:arr=(4 5 6)INDEX1=$(echo ${arr[0]})INDEX2=$(echo ${arr[1]})INDEX3=$(echo ${arr[2]})
读取list.txt文件中的账号密码批量创建用户
#!/bin/bash#提前准备账号密码文件list.txt#文件内每行一个账号密码组,使用逗号分隔(username,password)for line in `cat list.txt`do echo $line | awk -F, '{cmd="useradd "$1"; echo "$2" | passwd --stdin "$1; system(cmd)}' #awk中调用系统命令done
显示当前计算机中所有账户的用户名
#!/bin/bashecho "方法①,指定以:为分隔符,打印/etc/passwd文件的第1列"awk -F: '{print $1}' /etc/passwdecho "方法②,指定以:为分隔符,打印/etc/passwd文件的第1列"cut -d: -f1 /etc/passwdecho "方法③使用sed的替换功能,将/etc/passwd文件中第1个:后面的所有内容替换为空(仅显示用户名)"sed "s/:.*//" /etc/passwd
统计当前Linux系统中可以登陆计算机的账户数量
#!/bin/bash#方法1grep:grep "bash$" /etc/passwd | wc -l #一般默认解释器是bash#方法2awk:awk -F: '/bash$/{x++} END{print x}' /etc/passwd#方法3,更为准确for shell in `awk -F: '{print $7}' /etc/passwd` #取出账号文件中各账号的解释器do if [[ "`cat /etc/shells`" =~ "$shell" ]]; then #检测取出的解释器是否包含在可登录解释器中 let n++ fidoneecho $n
找出/etc/passwd中能登录的用户,并将对应在/etc/shadow中第二列密码提出处理
#!/bin/bashshells=`awk -F: '$7!~"nologin"{print $1","$7}' /etc/passwd`for shell in $shellsdo if [[ "`grep -v '^#' /etc/shells`" =~ "`echo $shell | cut -d, -f2`" ]]; then users="$users `echo $shell | cut -d, -f1`" fidonefor user in $usersdo awk -F: -v u=$user '$1~u{print $1,$2}' /etc/shadowdone
自动修改计划任务配置文件
#!/bin/bashread -p "请输入分钟信息(00-59):" minread -p "请输入小时信息(00-24):" hourread -p "请输入日期信息(01-31):" dateread -p "请输入月份信息(01-12):" monthread -p "请输入周几信息(00-06):" weakread -p "请输入计划任务需要执行的命令或脚本:" programecho "$min $hour $date $month $weak $USER $program" >> /etc/crontab
实时监视本机内存、/分区剩余空间,当剩余空间达到阈值发送报警邮件给root管理员
#!/bin/bashdisk_value=1000 #设置/分区监测空间mem_value=500 #设置内存监测空间disk_size=`df -m / | awk '///{print $4}'` #获取/的剩余硬盘空间,单位Mmem_size=`free -m | awk '/Mem/{print $4}'` #获取剩余内存空间,单位Mwhile :do if [ $disk_size -le $disk_value -o $mem_size -le $mem_value ]; then #判断条件 echo "Insufficient resources,资源不足" | mail -s "Warning" root #达到阈值发送邮件给root fidone
批量下载有序文件(pdf、图片、视频等)
#!/bin/bash#本脚本进行有序的网络资料批量下载操作(如01.jpg,02.jpg,03.jpg)url="http://www.test.com"type=jpgDpath=/mnt echo "开始下载..."for num in `seq 10`do echo -n "正在下载$num.$type" curl -s ${url}/${num}.${type} -o $Dpath/${num}.$type #-o选项,curl指定下载文件另存为 if [ $? -eq 0 ]; then echo -e " [e[32mOKe[0m]" else echo -e " [e[31mERRORe[0m]" fi sleep 1done
批量修改文件名
示例:# touch article_{1..3}.html# lsarticle_1.html article_2.html article_3.html目的:把article改为bbs方法1:for file in $(ls *html); do mv $file bbs_${file#*_} # mv $file $(echo $file |sed -r 's/.*(_.*)/bbs1/') # mv $file $(echo $file |echo bbs_$(cut -d_ -f2)done方法2:for file in $(find . -maxdepth 1 -name "*html"); do mv $file bbs_${file#*_}done方法3:# rename article bbs *.html
统计当前目录中以.html结尾的文件总大
方法1:# find . -name "*.html" -exec du -k {} ; |awk '{sum+=$1}END{print sum}'方法2:for size in $(ls -l *.html |awk '{print $5}'); do sum=$(($sum+$size))doneecho $sum
扫描主机端口状态
#!/bin/bashHOST=$1PORT="22 25 80 8080"for PORT in $PORT; do if echo &>/dev/null > /dev/tcp/$HOST/$PORT; then echo "$PORT open" else echo "$PORT close" fidone
自动优化Linux内核参数
#!/bin/bash#此脚本针对RHEL7系列cat >> /usr/lib/sysctl.d/00-system.conf << EOFfs.file-max=65535net.ipv4.tcp_timestamps = 0net.ipv4.tcp_synack_retries = 5net.ipv4.tcp_syn_retries = 5net.ipv4.tcp_tw_recycle = 1net.ipv4.tcp_tw_reuse = 1net.ipv4.tcp_fin_timeout = 30#net.ipv4.tcp_keepalive_time = 120net.ipv4.ip_local_port_range = 1024 65535kernel.shmall = 2097152kernel.shmmax = 2147483648kernek.shmmin = 4096kernel.sem = 5010 641280 5010 128net.core.wmem_default=262144 net.core.wmem_max=262144 net.core.rmem_default=4194304 net.core.rmem_max=4194304 net.ipv4.tcp_fin_timeout = 10 net.ipv4.tcp_keepalive_time = 30 net.ipv4.tcp_window_scaling = 0 net.ipv4.tcp_sack = 0 EOF
统计/var/log有多少个文件,并显示这些文件名
#!/bin/bash#方法1sum=0 #用于统计数量for i in `ls /var/log/`do if [ -f $i ];then let sum++ echo "文件名:$i" fidoneecho "文件总量为:$sum"#方法2echo "文件总量为:"`find /var/log -maxdepth 1 -type f | wc -l`
查找Linux系统中的僵尸进程
#!/bin/bash#awk判断ps命令输出的第8列为z时,显示该进程的PID和进程命令ps aux | awk '{if($8 == "Z" ){print $2,$11}}'
Expect实现SSH免交互执行命令
Expect是一个自动交互式应用程序的工具,如telnet,ftp,passwd等。需先安装expect软件包。方法1:EOF标准输出作为expect标准输入#!/bin/bashUSER=rootPASS=123.comIP=192.168.1.120expect << EOFset timeout 30spawn ssh $USER@$IP expect { "(yes/no)" {send "yes"; exp_continue} "password:" {send "$PASS"}}expect "$USER@*" {send "$1"}expect "$USER@*" {send "exit"}expect eofEOF方法2:#!/bin/bashUSER=rootPASS=123.comIP=192.168.1.120expect -c " spawn ssh $USER@$IP expect { "(yes/no)" {send "yes"; exp_continue} "password:" {send "$PASS"; exp_continue} "$USER@*" {send "df -h exit"; exp_continue} }"方法3:将expect脚本独立出来登录脚本:# cat login.exp#!/usr/bin/expectset ip [lindex $argv 0]set user [lindex $argv 1]set passwd [lindex $argv 2]set cmd [lindex $argv 3]if { $argc != 4 } {puts "Usage: expect login.exp ip user passwd"exit 1}set timeout 30spawn ssh $user@$ipexpect { "(yes/no)" {send "yes"; exp_continue} "password:" {send "$passwd"}}expect "$user@*" {send "$cmd"}expect "$user@*" {send "exit"}expect eof执行命令脚本:写个循环可以批量操作多台服务器#!/bin/bashHOST_INFO=user_info.txtfor ip in $(awk '{print $1}' $HOST_INFO)do user=$(awk -v I="$ip" 'I==$1{print $2}' $HOST_INFO) pass=$(awk -v I="$ip" 'I==$1{print $3}' $HOST_INFO) expect login.exp $ip $user $pass $1doneLinux主机SSH连接信息:# cat user_info.txt192.168.1.120 root 123456
批量修改服务器用户密码
Linux主机SSH连接信息:旧密码# cat old_pass.txt 192.168.18.217 root 123456 22192.168.18.218 root 123456 22内容格式:IP User Password PortSSH远程修改密码脚本:新密码随机生成https://www.linuxprobe.com/books#!/bin/bashOLD_INFO=old_pass.txtNEW_INFO=new_pass.txtfor IP in $(awk '/^[^#]/{print $1}' $OLD_INFO); do USER=$(awk -v I=$IP 'I==$1{print $2}' $OLD_INFO) PASS=$(awk -v I=$IP 'I==$1{print $3}' $OLD_INFO) PORT=$(awk -v I=$IP 'I==$1{print $4}' $OLD_INFO) NEW_PASS=$(mkpasswd -l 8) # 随机密码 echo "$IP $USER $NEW_PASS $PORT" >> $NEW_INFO expect -c " spawn ssh -p$PORT $USER@$IP set timeout 2 expect { "(yes/no)" {send "yes";exp_continue} "password:" {send "$PASS";exp_continue} "$USER@*" {send "echo '$NEW_PASS' |passwd --stdin $USER exit";exp_continue} }"done生成新密码文件:# cat new_pass.txt 192.168.18.217 root n8wX3mU% 22192.168.18.218 root c87;ZnnL 22
while循环检测所处网段主机的开关机状态
#!/bin/bashi=1while [ $i -le 254 ]do ping -c2 -i0.2 -W1 192.168.4.$i &>/dev/null if [ $? -eq 0 ] ;then echo -e "e[32;1m192.168.4.$i is up[0m" else echo "192.168.4.$i is down" fi let i++done
循环关闭局域网中所有主机
#!/bin/bash#假设本机为192.168.4.100,编写脚本关闭除自己外的其他所有主机#脚本执行,需要提前给所有其他主机传递ssh密钥,满足无密码连接for i in {1..254}do [ $i -eq 100 ] && continue echo "正在关闭192.168.4.$i..." ssh 192.168.4.$i poweroffdone
自动添加防火墙规则(开启服务或端口
#!/bin/bash#适用于RHEL7系列#firewall-cmd --get-services #可以查看firewall支持哪些服务#cat /etc/services #可以查看服务、端口的对应表services="ssh http nfs" #设置服务ports="22 80 2049" #设置端口号 ssh、http 、 nfs#循环将每个服务添加到防火墙规则中for service in $servicesdo echo "添加 $service 服务到 防火墙" firewall-cmd --add-service=${service}done#循环将每个端口添加到防火墙规则中for port in $portsdo echo "添加 $port 端口到 防火墙" firewall-cmd --add-port=${port}/tcpdone#将以上设置的临时防火墙规则,转换为永久有效的规则(确保重启后有效)firewall-cmd --runtime-to-permanent
自动配置rsyncd服务器的配置文件rsyncd.conf
#!/bin/bash#man rsyncd.conf #可查看更多选项[ ! -d /home/ftp ] && mkdir /home/ftpecho 'uid = nobody gid = nobodyuse chroot = yesmax connections = 4pid file = /var/run/rsyncd.pidexclude = lost+found/transfer logging = yestimeout = 900ignore nonreadable = yesdont compress = *.gz *.tgz *.zip *.z *.Z *.rpm *.deb *.bz2[ftp] path = /home/ftp comment = share' > /etc/rsyncd.conf
一键配置VNC远程桌面服务器(无密码仅查看版本)
#!/bin/bash##脚本配置的VNC服务器,客户端无需密码即可连接#客户端仅有查看远程桌面的权限,没有鼠标、键盘的操作权限rpm --quiet -q tigervnc-server #--quiet安静,-q查询,vnc的安装情况if [ $? -ne 0 ];then yum -y install tigervnc-serverfix0vncserver AcceptKeyEvents=0 AlwaysShared=1 AcceptPointerEvents=0 SecurityTypes=None rfbport
fdisk命令自动对磁盘分区、格式化、挂载
#!/bin/bash#对虚拟机的vdb磁盘进行分区格式化,使用<> /etc/fstabmount -a
自动创建逻辑卷
#!/bin/bash#清屏,显示警告信息,创建将磁盘转换为逻辑卷会删除数据clearecho -e "033[33m !!!!!!警告(Warning)!!!!!!033[0m"echoecho "+++++++++++++++++++++++++++++++++++++++++++++++++" echo "脚本会将整个磁盘转换为 PV,并删除磁盘上所有数据!!!" echo "This Script will destroy all data on the Disk" echo "+++++++++++++++++++++++++++++++++++++++++++++++++" lsblk #显示当前的块设备echoread -p "请问是否继续 y/n?:" sure[ $sure != y ] && exit #测试用户输入的是否为y,若不是则退出脚本#提示用户输入相关参数(磁盘、卷组名等数据),并测试用户是否输入了这些值,#若没输入,则脚本退出read -p "请输入磁盘名称,如/dev/vdb:" disk[ -z $disk ] && echo "没有输入磁盘名称" && exit #-z后边的字符串长度为0则为真read -p "请输入卷组名称:" vg_name[ -z $vg_name ] && echo "没有输入卷组名称" && exitread -p "请输入逻辑卷名称:" lv_name[ -z $lv_name ] && echo "没有输入逻辑卷名称" && exitread -p "请输入逻辑卷大小:" lv_size[ -z $lv_size ] && echo "没有输入逻辑卷大小" && exit#使用命令创建逻辑卷pvcreate $diskvgcreate $vg_name $disklvcreate -L ${lv_size}M -n ${lv_name} ${vg_name}# lsblk $disk