Contents

更新服务器DNS

Contents

由于众所周知的原因,国内的云服务器经常出现访问不了github的情况.通过shell脚本实现定时更新的功能

#!/usr/bin/bash

# ==================== 配置参数(根据需求修改)====================
# 远程 Hosts 文件的 URL(替换为你的实际远程地址)
REMOTE_HOSTS_URL="https://gitlab.com/ineo6/hosts/-/raw/master/next-hosts"
# 更新间隔(秒),默认 1 小时(3600 秒)
UPDATE_INTERVAL=3600
# 日志文件路径
LOG_FILE="/var/log/github_hosts_updater.log"
# 备份文件存放目录(自动创建)
BACKUP_DIR="/var/backups/hosts"
# 标记:用于识别脚本自动添加的内容(避免覆盖手动配置)
START_MARKER="# === GitHub Hosts Auto-Update Start === #"
END_MARKER="# === GitHub Hosts Auto-Update End === #"
# ==================== 配置结束 ====================

# 初始化:创建备份目录、日志文件
init() {
    # 创建备份目录(若不存在)
    if [ ! -d "$BACKUP_DIR" ]; then
        mkdir -p "$BACKUP_DIR"
        chmod 700 "$BACKUP_DIR"
    fi
    # 创建日志文件(若不存在)
    if [ ! -f "$LOG_FILE" ]; then
        touch "$LOG_FILE"
        chmod 600 "$LOG_FILE"
    fi
    # 检查 curl 是否安装(用于获取远程文件)
    if ! command -v curl &> /dev/null; then
        log "错误:未安装 curl,请先执行 'sudo apt install curl'(Linux)或 'brew install curl'(macOS)"
        exit 1
    fi
}

# 日志函数:输出时间+内容到日志文件和控制台
log() {
    local msg="$1"
    local time=$(date "+%Y-%m-%d %H:%M:%S")
    echo "[$time] $msg" | tee -a "$LOG_FILE"
}

# 备份当前 hosts 文件(带时间戳,避免覆盖)
backup_hosts() {
    local timestamp=$(date "+%Y%m%d%H%M%S")
    local backup_path="$BACKUP_DIR/hosts_$timestamp.bak"
    # 复制 hosts 到备份目录
    if cp /etc/hosts "$backup_path"; then
        log "已备份 hosts 到:$backup_path"
    else
        log "警告:备份 hosts 失败!"
    fi
}

# 从远程 URL 获取最新 Hosts 内容
fetch_remote_hosts() {
    log "正在从远程获取 Hosts:$REMOTE_HOSTS_URL"
    # 使用 curl 获取远程内容(超时 10 秒,失败重试 2 次)
    local remote_content=$(curl -s --connect-timeout 10 --retry 2 "$REMOTE_HOSTS_URL")
    # 检查获取结果(为空则失败)
    if [ -z "$remote_content" ]; then
        log "错误:获取远程 Hosts 失败(URL 无效或网络异常)"
        return 1
    fi
    echo "$remote_content"
    return 0
}

# 更新本地 hosts 文件(保留手动配置,替换脚本添加的部分)
update_hosts() {
    local new_hosts="$1"
    # 1. 先备份当前 hosts
    backup_hosts

    # 2. 读取现有 hosts 内容,提取「非脚本添加」的部分
    # 若存在标记,则保留标记外的内容;若不存在标记,则保留全部内容
    if grep -q "$START_MARKER" /etc/hosts && grep -q "$END_MARKER" /etc/hosts; then
        # 删除标记及其间内容,只保留手动配置部分
        local pre_content=$(sed "/$START_MARKER/,/$END_MARKER/d" /etc/hosts)
    else
        # 无标记,保留全部内容(并提示首次添加)
        local pre_content=$(cat /etc/hosts)
        log "首次更新:未发现标记,将在 hosts 末尾添加自动配置"
    fi

    # 3. 构建新的 hosts 内容(原有内容 + 新 Hosts + 标记)
    local new_content="$pre_content
$START_MARKER
# 更新时间:$(date "+%Y-%m-%d %H:%M:%S")
$new_hosts
$END_MARKER
"

    # 4. 写入新内容到 hosts(需要 root 权限)
    if echo "$new_content" > /etc/hosts; then
        log "hosts 更新成功!"
        # 刷新 DNS 缓存(不同系统命令不同)
        if [ "$(uname -s)" = "Darwin" ]; then
            # macOS 刷新 DNS
            dscacheutil -flushcache && killall -HUP mDNSResponder
            log "已刷新 macOS DNS 缓存"
        else
            # Linux 刷新 DNS(适用于大多数系统)
            systemctl restart systemd-resolved 2>/dev/null || /etc/init.d/nscd restart 2>/dev/null
            log "已刷新 Linux DNS 缓存"
        fi
    else
        log "错误:写入 hosts 失败(请确保以 root 权限运行脚本)"
        return 1
    fi
}

# 主循环:定时执行更新
main() {
    init
    log "=== GitHub Hosts 自动更新脚本启动 ==="
    log "更新间隔:$((UPDATE_INTERVAL/3600)) 小时 | 远程 URL:$REMOTE_HOSTS_URL"

    while true; do
        log "=== 开始新一轮更新 ==="
        # 获取远程 Hosts
        remote_hosts=$(fetch_remote_hosts)
        if [ $? -eq 0 ]; then
            # 更新本地 hosts
            update_hosts "$remote_hosts"
        fi
        # 等待下一次更新
        log "等待 $((UPDATE_INTERVAL/3600)) 小时后再次更新...\n"
        sleep "$UPDATE_INTERVAL"
    done
}

# 启动主程序
main

然后

nohup github_hosts_updater.sh &  #保持后台运行就可以了