Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> Linux 教學

Shell 腳本精準截取 Nginx 訪問日誌指定時間區間

編輯:Linux 教學

問題背景

Nginx access.log 以時間戳記錄每筆請求。需求:提取 2010-08-16 14:31:30 至 2010-08-17 10:12:07 區間的所有日誌行,寫入 log.txt。

方案對比與推薦

方案一:行號定位法(推薦,精準穩健)

#!/bin/bash
# 取得起始行號(首次出現)
n1=$(grep -n '16\/Aug\/2010:14:31:30' access.log | head -1 | cut -d: -f1)
# 取得結束行號(最後出現)
n2=$(grep -n '17\/Aug\/2010:10:12:07' access.log | tail -1 | cut -d: -f1)

# 邊界檢查
if [[ -z "$n1" || -z "$n2" ]]; then
    echo "時間戳未找到,請確認日誌格式"
    exit 1
fi

# sed 按行號範圍輸出
sed -n "${n1},${n2}p" access.log > log.txt
echo "已提取 $((n2-n1+1)) 行至 log.txt"

方案二:sed 地址匹配法(簡潔但有缺陷)

sed -n '/16\/Aug\/2010:14:31:30/,/17\/Aug\/2010:10:12:07/p' access.log > log.txt

缺陷:若同一秒內有大量並發日誌(同時間戳重複),sed 會在首次匹配起始模式到首次匹配結束模式之間輸出,導致漏掉同時間戳後續行或提前結束。

方案三:awk 時間解析法(通用性最強,適合非標準格式)

awk '
# 將 Nginx 時間轉為 epoch 秒
function to_epoch(t) {
    gsub(/[\[\]\/:/, " ", t)
    split(t, a, " ")
    mon["Jan"]=1; mon["Feb"]=2; mon["Mar"]=3; mon["Apr"]=4; mon["May"]=5; mon["Jun"]=6
    mon["Jul"]=7; mon["Aug"]=8; mon["Sep"]=9; mon["Oct"]=10; mon["Nov"]=11; mon["Dec"]=12
    return mktime(a[4]" "mon[a[3]]" "a[2]" "a[5]" "a[6]" "a[7])
}
BEGIN {
    start=to_epoch("16/Aug/2010:14:31:30")
    end=to_epoch("17/Aug/2010:10:12:07")
}
{
    # 假設 $4 為 [$time_local] 格式
    match($4, /\[(.*)\]/, arr)
    if (arr[1] != "") {
        t=to_epoch(arr[1])
        if (t >= start && t <= end) print
    }
}' access.log > log.txt

生產環境最佳實踐

  1. 日誌輪轉:配合 logrotate 按天/大小切割,避免單檔過大導致 grep/sed 效能下降。
  2. 時間索引:大規模日誌建議接入 ELK/EFK、ClickHouse、Loki 等專用平台,支援毫秒級範圍查詢。
  3. 腳本健壯性:加入參數化(起始/結束時間作為參數)、錯誤處理、進度顯示。

結語

對於單機、中小規模日誌,**方案一(行號定位)最佳平衡了精準度與實作簡易度**。核心原則:先定位物理行號,再按範圍輸出,規避邏輯匹配的邊界陷阱。

Copyright © Windows教程網 All Rights Reserved