Shell 脚本系列

说明:内容来源于网络,并且通过AI工具重新整理归纳。

本文整理了十个实用的 Shell 脚本案例,涵盖日期处理、磁盘监控、日志分析、密码生成、服务监控、YUM 仓库搭建、用户管理、批量建用户、Nginx 服务脚本等高频运维场景。每个案例均包含完整脚本代码与详细解析,可直接复制使用或按需修改。


目录


案例一:指定日期的加减计算

利用 date -d 参数可以灵活地对日期进行加减操作,是 Shell 脚本中处理日期最常用的方法。

1. 日期减去一天

# 只减去一天,直接写即可
date -d "yesterday 20150401" +%Y%m%d

# 减去指定天数(负数往前数)
date -d "10 day ago 2015-04-01" +%Y-%m-%d

2. 在指定日期上加减小时

# 加上 3 小时 → 输出 2015041615
date -d "20150416 12 3 hour" +"%Y%m%d%H"

# 减去 1 小时 → 输出 2015041611
date -d "20150416 12 -1 hour" +"%Y%m%d%H"

3. 在指定日期上加上分钟

# 加上 10 分钟 → 输出 201504161230
date -d "20150416 12:20 10 minute" +"%Y%m%d%H%M"

4. 常用日期快捷写法

# 本月 / 下月
this_ym=`date +%Y%m`
next_ym=`date -d '1month' +%Y%m`

# 昨天 / 明天
date -d yesterday
date -d tomorrow
date +%Y%m%d -d "+1 day"
date +%Y%m%d --date "+1 day"

# 指定月份
date -d 1May

# 当前时间
date -d now

5. 更多日期偏移用法一览

date +%Y%m%d --date="+1 day"     # 后一天
date +%Y%m%d --date="-1 day"     # 前一天
date +%Y%m%d --date="-1 month"   # 上一月
date +%Y%m%d --date="+1 month"   # 下一月
date +%Y%m%d --date="-1 year"    # 前一年
date +%Y%m%d --date="+1 year"    # 下一年

提示:date -d 功能非常强大,建议熟记,可大幅简化脚本中的日期处理逻辑。


案例二:获取当前日期和时间及磁盘使用情况

习题分析

本题有两个核心知识点:

  1. 如何自动表示当天的日期
  2. 如何获取磁盘使用情况

打印日期用 date 命令:

# 默认格式
date
# 输出:2017年 12月 20日 星期三 16:26:55 CST

# 指定格式(两种写法等价)
date +%Y-%m-%d
# 输出:2017-12-20
date +%F
# 输出:2017-12-20

磁盘使用情况用 df -h 实现:

# df -h
文件系统       容量  已用  可用 已用% 挂载点
/dev/vda1      99G  1.8G   92G    2% /
devtmpfs      911M     0  911M    0% /dev
tmpfs         920M     0  920M    0% /dev/shm
tmpfs         920M  336K  920M    1% /run
tmpfs         920M     0  920M    0% /sys/fs/cgroup
tmpfs         184M     0  184M    0% /run/user/0

习题答案

#! /bin/bash
d=`date +%F`
logfile=$d.log
df -h > $logfile

答案解析

  • 把当天日期赋值给变量 d,从而定义每日的日志文件名
  • > 是输出重定向符号,可把左边命令的结果写入到右边的文件中

扩展知识点

1. 反引号获取命令结果

n=`wc -l /etc/passwd | awk '{print $1}'`
echo $n
# 输出:23

2. date 命令更多用法

date +%H        # 小时 → 16
date +%M        # 分钟 → 38
date +%S        # 秒   → 55
date +%T        # 时间 → 16:39:31
date +%w        # 星期 → 3
date -d "-1 day" +%F   # 一天以前 → 2017-12-19

3. 正确/错误重定向

# >  正确重定向,把正确输出写入文件
# 2> 错误重定向,把错误信息写入文件

ls /etc/passwd /etc/nofile > /tmp/log 2>/tmp/error

cat /tmp/log      # 输出:/etc/passwd
cat /tmp/error    # 输出:ls: 无法访问/etc/nofile: 没有那个文件或目录

案例三:统计 IP 访问情况并分析访问日志

题目要求

有日志文件 1.log,部分内容如下:

112.111.12.248 - [25/Sep/2013:16:08:31 +0800] "GET /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] "GET /attachment.php?aid=4554&k=9ce51e2c376bc861603c7689d97c04a1" 301 "http://xyzdiy.5d6d.com/thread-1435-1-23.html" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322;)"

需求:统计出每个 IP 的访问量有多少。

习题分析

IP 地址在日志的第一段,使用 awk 提取第一段后,再排序并用 uniq -c 统计即可。

习题答案

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

答案解析

命令 作用
awk '{print $1}' 提取每段的第一列(即 IP 地址)
sort -n 按数字排序(为 uniq 去重做准备)
uniq -c 统计每个 IP 出现的次数
sort -n 按访问量从小到大排序

注意:uniq 去重的前提是先排序。若想按访问量从大到小排,加 -r 选项:sort -nr。


案例四:生成随机密码

方法一:urandom 版本

利用 Linux 内置随机设备文件 /dev/urandom 生成密码,再通过 tr 过滤掉不需要的特殊字符。

#!/bin/bash
# Author: 丁丁历险(Jacob)
# /dev/urandom 是 Linux 内置的随机设备文件
# tr -dc '_A-Za-z0-9' 仅保留大小写字母、数字、下划线
tr -dc '_A-Za-z0-9' < /dev/urandom | head -c 10

执行后输出 10 位随机密码,如 aB3kL9mX2p。

方法二:字符串截取版本

自定义密码字符库,用 $RANDOM 取模实现随机选取。

#!/bin/bash
# Author: 丁丁历险(Jacob)
# 密码库:数字 + 大小写字母
key="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
num=${#key}        # 统计密码库长度
pass=''            # 初始密码为空

# 循环 8 次,生成 8 位随机密码
for i in {1..8}
do
    index=$[RANDOM%num]
    pass=$pass${key:$index:1}
done

echo $pass

方法三:UUID 版本(16 进制密码)

#!/bin/bash
uuidgen

方法四:进程 ID 版本(纯数字密码)

#!/bin/bash
echo $$

小结:四种方法各有适用场景——urandom 最简单、字符串截取最可控、UUID 最标准、PID 最轻量。


案例五:定时监控 HTTP 服务运行状态

注意:监控方法可以为端口检测、进程检测、URL 模拟访问,或三种方法综合。

版本一:基于进程判断

#!/bin/sh
. /etc/init.d/functions

HTTPPRONUM=`ps -ef | grep http | grep -v grep | wc -l`

if [[ $HTTPPRONUM -lt 1 ]]; then
    action "httpd is not running" /bin/false
    action "httpd is not running" /bin/false >/tmp/httpd.log
    httpdctl restart >/dev/null 2>&1
    action "httpd is restart" /bin/true
    mail -s "`uname -n`'s httpd restarted at `(date)`" 31333741@qq.com
    exit 1
else
    action "httpd is running" /bin/true
    exit 0
fi

测试效果:

[root@localhost scripts]# apachemon
httpd is running [确定]

[root@localhost scripts]# pkill httpd
[root@localhost scripts]# apachemon
httpd is not running [失败]
httpd is restart [确定]

版本二:基于 URL 判断(改进版)

用 wget --spider 模拟访问,更能反映服务的真实可用性。

#!/bin/sh
# created by oldboy 20110523
. /etc/init.d/functions

wget --quiet --spider http://10.0.0.161/index.htm

if [ $? -ne 0 ]; then
    action "httpd is not running" /bin/false >/tmp/httpd.log
    httpdctl restart >/dev/null 2>&1
    action "httpd is restart" /bin/true >>/tmp/httpd.log
    mail -s "`uname -n`'s httpd restarted at `(date)`" 31333741@qq.com
    exit 1
fi

版本三:多条件综合判断

同时检测 端口 + 进程 + URL,任一条件不满足即判定为异常。

#!/bin/sh
. /etc/init.d/functions

HTTPPORTNUM=`netstat -lnt | grep 80 | grep -v grep | wc -l`
HTTPPRONUM=`ps -ef | grep http | grep -v grep | wc -l`
wget --quiet --spider http://10.0.0.161/index.htm
RETVAL=$?

if [ $RETVAL -ne 0 ] || [ $HTTPPORTNUM -ne 1 ] || [ $HTTPPRONUM -lt 1 ]; then
    action "httpd is not running" /bin/false
    httpdctl restart >/dev/null 2>&1
    action "httpd is restart" /bin/true
    mail -s "`uname -n`'s httpd restarted at `(date)`" 31333741@qq.com
    exit 1
else
    action "httpd is running" /bin/true
    exit 0
fi

建议:生产环境推荐版本三,三重检测更可靠。


案例六:监控 httpd 服务 80 端口状态

根据端口判断服务是否启动,未启动则自动拉起并邮件告警。

#!/bin/bash
# 实时每隔3秒监控HTTP服务状态,服务异常输出报警信息并尝试启动

web=`netstat -ant | grep 80 | awk '{print $4}' | awk -F : '{print $4}'`
apachelog=/var/log/httpd/service.log

if [ "$web" = "80" ]; then
    echo "HTTPD正在运行"
else
    echo "Web服务重新启动"
    echo "`date` Web服务重新启动" >> $apachelog
    /etc/init.d/httpd start &> /dev/null
    sleep 2
    echo `date` 重新启动 | /usr/sbin/sendmail -v telephonenumber@139.com > /dev/null
fi

说明:将脚本加入 crontab 定时执行即可实现持续监控。换成其他服务端口也只需修改端口号和启动命令。


案例七:开机自动挂载本地 YUM 仓库

适用于无网络环境下快速搭建本地 YUM 源。

#!/bin/bash
# 自动搭建 yum 本地仓库

# 定义挂载函数
cdrom() {
    mount /dev/cdrom /media/cdrom &> /dev/null
    [ $? -eq 0 ] && echo "挂载成功"
}

# 创建挂载目录
[ -d /media/cdrom ] || mkdir /media/cdrom

# 如果已挂载则先卸载再重新挂载
mount | grep -q /dev/sr0
if [ $? -eq 0 ]; then
    umount /dev/sr0
    cdrom
else
    cdrom
fi

# 备份原有 repo 文件
[ -d /etc/yum.repos.d/bak ] || mkdir /etc/yum.repos.d/bak
ls -l /etc/yum.repos.d/ | grep repo > /dev/null
if [ $? -ne 0 ]; then
    echo "正在移动文件到备份目录"
    mv /etc/yum.repos.d/*.repo bak
else
    echo "文件已经移动过!"
fi

# 创建本地仓库配置文件
[ -e /etc/yum.repos.d/centos-media.repo ] || cat > /etc/yum.repos.d/centos-media.repo << 'EOF'
[media]
name=CentOS-$releasever - Media
baseurl=file:///media/cdrom/
gpgcheck=0
enabled=1
EOF

# 清除缓存并统计软件包
echo "清除yum缓存"
yum clean all &> /dev/null
echo "YUM已经搭建完成"
echo -n "共有软件包:"
yum repolist | tail -1 | awk -F':' '{print $2}'

# 写入 fstab 实现开机自动挂载
cat /etc/fstab | grep /dev/sr0 &> /dev/null
if [ $? -ne 0 ]; then
    echo "正在写入开机自动挂载"
    echo "/dev/sr0  /media/cdrom  iso9660  defaults  0  0" >> /etc/fstab
fi

提示:执行完毕后即可使用 yum install 安装软件,无需联网。


案例八:Shell 自动化管理账号脚本

帮助管理员通过菜单交互式地创建、删除、锁定、解锁用户。

#!/bin/bash
# 用户管理程序
# date: 2018-6-6

echo "====== 用户管理程序 ======"
echo "1. 创建用户"
echo "2. 删除用户"
echo "3. 锁定用户"
echo "4. 解锁用户"
echo "5. 退出脚本"
read -p "请输入您的操作选择(1-5):" sn

case $sn in
    1)
        read -p "请输入创建用户名:" nu
        useradd $nu
        echo "123456" | passwd --stdin $nu
        [ $? -eq 0 ] && echo "用户 $nu 创建成功"
        ;;
    2)
        read -p "请输入要删除的用户名:" nl
        userdel -r $nl
        [ $? -eq 0 ] && echo "已成功删除 $nl 用户"
        ;;
    3)
        read -p "请输入要锁定的用户名:" use
        STAT=$(passwd -S $use | awk '{print $2}')
        if [ "$STAT" == "PS" ]; then
            passwd -l $use
            echo "用户 $use 已锁定"
        elif [ "$STAT" == "LK" ]; then
            echo "用户 $use 已经是锁定状态"
        fi
        ;;
    4)
        read -p "请输入要解锁的用户名:" jie
        STAT=$(passwd -S $jie | awk '{print $2}')
        if [ "$STAT" == "LK" ]; then
            passwd -u $jie
            echo "用户 $jie 已解锁"
        else
            echo "用户 $jie 未被锁定"
        fi
        ;;
    5)
        read -p "是否确认退出(yes):" tu
        [ "$tu" == "yes" ] && exit 0
        ;;
    *)
        echo "无效选择"
        ;;
esac

注意:原文脚本中部分变量引用顺序有误(如先取 STAT 再读 use),整理版已修正执行顺序。


案例九:自动创建多个新用户并设置密码

批量创建用户是企业中最常见的运维需求之一,以下提供三个版本。

脚本一:批量创建 100 个用户

#!/bin/bash
for name in $(seq 1 100)
do
    useradd "user$name"
    if [ $? -eq 0 ]; then
        echo "创建 user$name 成功!"
    fi
done

脚本二:添加到指定组并设置密码

#!/bin/bash
groupadd users 2>/dev/null
for i in {1..100}
do
    useradd user$i -g users
    echo "user$i:password" | chpasswd
done

脚本三:简洁版(推荐)

#!/bin/bash
for i in {1..100}
do
    useradd student$i
    echo "123456" | passwd --stdin student$i
done

安全提醒:生产环境请使用随机密码(参考案例四),而非统一弱密码。


案例十:编写 Nginx 服务启动程序

源码安装的 Nginx 默认没有 service nginx start 支持,需要自行编写 init 脚本。提供两个版本。

脚本一:完整版(推荐)

1. 创建脚本并赋权

touch /etc/init.d/nginx
chmod +x /etc/init.d/nginx

2. 脚本内容

#!/bin/bash
# chkconfig: - 85 15
# description: Nginx server control script
# processname: nginx
# config file: /usr/local/nginx/conf/nginx.conf
# pid file: /usr/local/nginx/logs/nginx.pid

# source function library
. /etc/rc.d/init.d/functions

NGINX_NAME="nginx"
NGINX_PROG="/usr/local/sbin/nginx"
NGINX_PID_FILE="/usr/local/nginx/logs/nginx.pid"
NGINX_CONF_FILE="/usr/local/nginx/conf/nginx.conf"
NGINX_LOCK_FILE="/var/lock/subsys/nginx.lock"

# 检查当前用户
[ "$USER" != "root" ] && exit 1

start() {
    status
    if [[ $? -eq 0 ]]; then
        echo "Nginx (PID $(cat $NGINX_PID_FILE)) already started."
        return 1
    fi
    echo -n "Starting $NGINX_NAME: "
    daemon $NGINX_PROG -c $NGINX_CONF_FILE
    retval=$?
    echo
    [ $retval -eq 0 ] && touch $NGINX_LOCK_FILE
    return $retval
}

stop() {
    status
    if [[ $? -eq 1 ]]; then
        echo "Nginx server already stopped."
        return 1
    fi
    echo -n "Stopping $NGINX_NAME: "
    killproc $NGINX_PROG
    retval=$?
    echo
    [ $retval -eq 0 ] && rm -f $NGINX_LOCK_FILE
    return $retval
}

restart() {
    stop
    sleep 1
    start
}

reload() {
    echo -n "Reloading $NGINX_NAME: "
    killproc $NGINX_PROG -HUP
    retval=$?
    echo
    return $retval
}

status() {
    netstat -anpt | grep "/nginx" | awk '{print $6}' &> /dev/null
    if [[ $? -eq 0 ]]; then
        [[ -f $NGINX_LOCK_FILE ]] && return 0 || return 1
    fi
    return 1
}

_status() {
    status
    if [[ $? -eq 0 ]]; then
        state=$(netstat -anpt | grep "/nginx" | awk '{print $6}')
        echo "Nginx server status is: $state"
    else
        echo "Nginx server is not running"
    fi
}

test() {
    $NGINX_PROG -t -c $NGINX_CONF_FILE
    return $?
}

case "$1" in
    start)   start ;;
    stop)    stop ;;
    reload)  reload ;;
    restart) restart ;;
    status)  _status ;;
    test)    test ;;
    *)
        echo "Usage: { start | stop | reload | restart | status | test }"
        exit 1
        ;;
esac

3. 注册为系统服务并设置开机启动

chkconfig --add nginx
chkconfig --level 3 nginx on

脚本二:精简版

1. 确保 Nginx 记录 PID

编辑 /usr/local/nginx/conf/nginx.conf,取消注释:

pid  logs/nginx.pid;

2. 脚本内容

#!/bin/bash
# chkconfig: 2345 99 20
# description: Nginx Server Control Script

PROG="/usr/local/nginx/sbin/nginx"
PIDF="/usr/local/nginx/logs/nginx.pid"

case "$1" in
    start)
        if [ -f $PIDF ]; then
            echo "Nginx 正在运行..."
        else
            $PROG
        fi
        ;;
    stop)
        if [ -f $PIDF ]; then
            kill -3 $(cat $PIDF)
            rm -f $PIDF
        else
            echo "Nginx 已经停止"
        fi
        ;;
    restart)
        $0 stop
        $0 start
        ;;
    reload)
        if [ -f $PIDF ]; then
            kill -1 $(cat $PIDF)
        else
            echo "Nginx 未运行,无法重载"
        fi
        ;;
    status)
        if [ -f $PIDF ]; then
            echo "Nginx 正在运行 (PID: $(cat $PIDF))"
        else
            echo "Nginx 已停止"
        fi
        ;;
    *)
        echo "Usage: $0 (start|stop|restart|reload|status)"
        exit 1
        ;;
esac
exit 0

3. 赋权并注册服务

chmod +x /etc/init.d/nginx
chkconfig --add nginx
chkconfig --level 3 nginx on

# 验证
service nginx status    # Nginx 正在运行
service nginx stop      # 停止
service nginx start     # 启动
service nginx restart   # 重启

总结速查表

案例 核心命令/技术 适用场景
一、日期计算 date -d 日志切割、定时任务日期处理
二、磁盘监控 df -h + 重定向 磁盘空间巡检、日报生成
三、日志分析 awk + sort + uniq -c 访问日志统计、安全分析
四、随机密码 /dev/urandom / $RANDOM / uuidgen 批量建用户、安全初始化
五、HTTP 监控 进程/端口/URL 三重检测 Web 服务高可用保障
六、端口监控 netstat + 条件判断 通用服务存活检测
七、YUM 仓库 挂载 ISO + yum.repos.d 离线环境软件安装
八、账号管理 useradd / passwd -l/-u 交互式用户运维
九、批量建用户 for 循环 + chpasswd 企业批量账号初始化
十、Nginx 脚本 init 脚本 + chkconfig 源码安装 Nginx 的服务化管理

提示:以上脚本建议在测试环境验证后再上生产,部分涉及系统级操作(如用户删除、服务重启)请谨慎执行。

posted @ 2019-08-18 15:26  睡到自然醒的猪  阅读(291)  评论(0)    收藏  举报