Operations 11 min read

Essential Bash Scripts for Nginx, MySQL, and Linux System Monitoring

This guide provides a collection of ready‑to‑use Bash scripts for daily Nginx log rotation, log analysis, server resource monitoring, inode warnings, malicious IP blocking, bulk file renaming, scheduled log clearing, Apache process supervision, and both single‑ and multi‑database MySQL backups.

Liangxu Linux
Liangxu Linux
Liangxu Linux
Essential Bash Scripts for Nginx, MySQL, and Linux System Monitoring

1. Daily Nginx Access Log Rotation

#!/bin/bash
LOG_DIR=/usr/local/nginx/logs
YESTERDAY_TIME=$(date -d "yesterday" +%F)
LOG_MONTH_DIR=$LOG_DIR/$(date +"%Y-%m")
LOG_FILE_LIST="default.access.log"
for LOG_FILE in $LOG_FILE_LIST; do
    [ ! -d $LOG_MONTH_DIR ] && mkdir -p $LOG_MONTH_DIR
    mv $LOG_DIR/$LOG_FILE $LOG_MONTH_DIR/${LOG_FILE}_${YESTERDAY_TIME}
    done
kill -USR1 $(cat /var/run/nginx.pid)

2. Nginx Access Log Analysis Script

#!/bin/bash
# Log format: $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"
LOG_FILE=$1

echo "Top 10 IPs (UV)"
awk '{a[$1]++} END {print "UV:", length(a); for (v in a) print v, a[v]}' $LOG_FILE | sort -k2 -nr | head -10

echo "----------------------"

echo "Top IPs in a time window"
awk '$4>="[01/Dec/2018:13:20:25" && $4<="[27/Nov/2018:16:20:49" {a[$1]++} END {for (v in a) print v, a[v]}' $LOG_FILE | sort -k2 -nr | head -10

echo "----------------------"

echo "Top 10 requested pages (PV)"
awk '{a[$7]++} END {print "PV:", length(a); for (v in a) if (a[v]>10) print v, a[v]}' $LOG_FILE | sort -k2 -nr

echo "----------------------"

echo "Status code count per page"
awk '{a[$7" "$9]++} END {for (v in a) if (a[v]>5) print v, a[v]}' $LOG_FILE | sort -k3 -nr

3. One‑Click Server Resource Utilization Overview

#!/bin/bash
function cpu() {
    NUM=1
    while [ $NUM -le 3 ]; do
        util=$(vmstat | awk '{if(NR==3)print 100-$15"%"}')
        iowait=$(vmstat | awk '{if(NR==3)print $16"%"}')
        echo "CPU - Usage: $util , IO Wait: $iowait"
        let NUM++
        sleep 1
    done
}

function memory() {
    total=$(free -m | awk '{if(NR==2)printf "%.1f", $2/1024}')
    used=$(free -m | awk '{if(NR==2)printf "%.1f", ($2-$NF)/1024}')
    available=$(free -m | awk '{if(NR==2)printf "%.1f", $NF/1024}')
    echo "Memory - Total: ${total}G , Used: ${used}G , Free: ${available}G"
}

function disk() {
    fs=$(df -h | awk '/^\/dev/{print $1}')
    for p in $fs; do
        mounted=$(df -h | awk -v dev="$p" '$1==dev{print $NF}')
        size=$(df -h | awk -v dev="$p" '$1==dev{print $2}')
        used=$(df -h | awk -v dev="$p" '$1==dev{print $3}')
        used_percent=$(df -h | awk -v dev="$p" '$1==dev{print $5}')
        echo "Disk - Mount: $mounted , Size: $size , Used: $used , Usage: $used_percent"
    done
}

function tcp_status() {
    summary=$(ss -antp | awk '{status[$1]++} END {for(i in status) printf i":"status[i]" "}')
    echo "TCP connections - $summary"
}

cpu
memory
disk
tcp_status

4. Real‑Time Inode Usage Alert

#!/bin/bash
# Alert when free inodes drop below 100
PATH=/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/wl/bin
export PATH
for FreeInode in $(df -i | grep -v "Filesystem" | awk '{print $4}'); do
    if [[ $FreeInode -lt 100 ]]; then
        echo -e "$(df -i | grep "$FreeInode")" > /service/script/.FreeInode
        mail -s "FreeInode Warning" wl < /service/script/.FreeInode
    fi
done

5. Block Malicious IPs from Nginx Access Logs

#!/bin/bash
DATE=$(date +%d/%b/%Y:%H:%M)
# Get the last 10000 lines, filter current minute, count per IP, flag >500 requests
ABNORMAL_IP=$(tail -n10000 access.log | grep $DATE | awk '{a[$1]++} END {for(i in a) if(a[i]>500) print i}')
for IP in $ABNORMAL_IP; do
    if [ $(iptables -vnL | grep -c "$IP") -eq 0 ]; then
        iptables -I INPUT -s $IP -j DROP
    fi
done

6. Batch Rename File Extensions

#!/bin/bash
# Usage: script.sh old_ext new_ext
for i in `ls *.$1`; do
    mv $i ${i%.*}.$2
done

7. Scheduled Log Truncation and Size Recording

#!/bin/bash
logfile=/tmp/$(date +%H-%F).log
hour=$(date +%H)
if [ $hour -eq 00 ] || [ $hour -eq 12 ]; then
    for i in `find /data/log/ -type f`; do
        > $i
    done
else
    for i in `find /data/log/ -type f`; do
        du -sh $i >> $logfile
    done
fi

8. Apache (httpd) Process Monitoring and Auto‑Restart

#!/bin/bash
check_service() {
    j=0
    for i in `seq 1 5`; do
        /usr/local/apache2/bin/apachectl restart 2>/var/log/httpderr.log
        if [ $? -eq 0 ]; then
            break
        else
            j=$((j+1))
        fi
        if [ $j -eq 5 ]; then
            mail.py exit
        fi
    done
}
while :; do
    n=$(pgrep -l httpd | wc -l)
    if [ $n -gt 500 ]; then
        /usr/local/apache2/bin/apachectl restart
        if [ $? -ne 0 ]; then
            check_service
        else
            sleep 60
            n2=$(pgrep -l httpd | wc -l)
            if [ $n2 -gt 500 ]; then
                mail.py exit
            fi
        fi
    fi
    sleep 10
done

9. Single‑Loop MySQL Database Backup

#!/bin/bash
DATE=$(date +%F_%H-%M-%S)
HOST=localhost
USER=backup
PASS=123.com
BACKUP_DIR=/data/db_backup
DB_LIST=$(mysql -h$HOST -u$USER -p$PASS -s -e "show databases;" 2>/dev/null | egrep -v "Database|information_schema|mysql|performance_schema|sys")
for DB in $DB_LIST; do
    BACKUP_NAME=$BACKUP_DIR/${DB}_$DATE.sql
    if ! mysqldump -h$HOST -u$USER -p$PASS -B $DB > $BACKUP_NAME 2>/dev/null; then
        echo "$BACKUP_NAME backup failed!"
    fi
done

10. Multi‑Loop MySQL Database Backup (per table)

#!/bin/bash
DATE=$(date +%F_%H-%M-%S)
HOST=localhost
USER=backup
PASS=123.com
BACKUP_DIR=/data/db_backup
DB_LIST=$(mysql -h$HOST -u$USER -p$PASS -s -e "show databases;" 2>/dev/null | egrep -v "Database|information_schema|mysql|performance_schema|sys")
for DB in $DB_LIST; do
    BACKUP_DB_DIR=$BACKUP_DIR/${DB}_$DATE
    [ ! -d $BACKUP_DB_DIR ] && mkdir -p $BACKUP_DB_DIR >/dev/null
    TABLE_LIST=$(mysql -h$HOST -u$USER -p$PASS -s -e "use $DB;show tables;" 2>/dev/null)
    for TABLE in $TABLE_LIST; do
        BACKUP_NAME=$BACKUP_DB_DIR/${TABLE}.sql
        if ! mysqldump -h$HOST -u$USER -p$PASS $DB $TABLE > $BACKUP_NAME 2>/dev/null; then
            echo "$BACKUP_NAME backup failed!"
        fi
    done
done
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

AutomationmysqlNGINX
Liangxu Linux
Written by

Liangxu Linux

Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.