1. 创建脚本文件 map.sh

nano /root/map.sh

map.sh 脚本内容:

#!/bin/bash
# ============================================================
# map.sh — xBlog 网站地图管理脚本
# 使用方式:bash /root/map.sh
# ============================================================

# ============================
# 配置区域(按需修改)
# ============================
SITE_DIR="/opt/1panel/www/sites/xblog/index"   # 网站根目录(宿主机路径)
SITE_DIR_CONTAINER="/www/sites/xblog/index"    # 网站根目录(容器内路径)
SITE_URL="https://xblog.itxgo.com"             # 博客域名,末尾不加斜杠
SITEMAP_SECRET="xblog_sitemap_2026"            # 手动触发密钥
FILE_OWNER="1000:1000"                         # 网站文件所有者(与其他网站文件保持一致)
PHP_BIN="docker exec php php"                  # PHP 执行命令(容器环境)
SCRIPT_PATH="$(realpath "$0")"                 # 本脚本自身路径(卸载时使用)
# ============================

# 派生路径(无需修改)
PHP_SCRIPT="$SITE_DIR/generate_sitemap.php"
ROBOTS_FILE="$SITE_DIR/robots.txt"
SITEMAP_FILE="$SITE_DIR/sitemap.xml"
DB_FILE="$SITE_DIR/blog.db"

# ============================================================
# 颜色输出
# ============================================================
GREEN="\033[0;32m"
RED="\033[0;31m"
YELLOW="\033[1;33m"
CYAN="\033[0;36m"
RESET="\033[0m"

ok()   { echo -e "${GREEN}✅ $1${RESET}"; }
err()  { echo -e "${RED}❌ $1${RESET}"; }
info() { echo -e "${CYAN}ℹ️  $1${RESET}"; }
warn() { echo -e "${YELLOW}⚠️  $1${RESET}"; }

# ============================================================
# 功能函数
# ============================================================

# 写入 generate_sitemap.php
write_php() {
    cat > "$PHP_SCRIPT" << PHPEOF
<?php
/**
 * generate_sitemap.php — 网站地图生成脚本
 * 由 map.sh 自动生成,请勿手动修改配置项
 */

define('SITEMAP_BASE_URL', '${SITE_URL}');
define('SITEMAP_OUTPUT',   __DIR__ . '/sitemap.xml');
define('SITEMAP_SECRET',   '${SITEMAP_SECRET}');

function generateSitemap(): int|false
{
    \$dbPath = __DIR__ . '/blog.db';
    if (!file_exists(\$dbPath)) {
        error_log('[Sitemap] 数据库文件不存在: ' . \$dbPath);
        return false;
    }

    try {
        \$db = new PDO('sqlite:' . \$dbPath);
        \$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        \$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
    } catch (PDOException \$e) {
        error_log('[Sitemap] 数据库连接失败: ' . \$e->getMessage());
        return false;
    }

    try {
        \$stmt = \$db->query("SELECT id, updated_at FROM articles ORDER BY updated_at DESC");
        \$articles = \$stmt->fetchAll();
    } catch (PDOException \$e) {
        error_log('[Sitemap] 文章查询失败: ' . \$e->getMessage());
        return false;
    }

    \$xml  = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
    \$xml .= '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";

    \$xml .= buildUrlEntry(SITEMAP_BASE_URL . '/', date('Y-m-d'), 'daily', '1.0');

    foreach (\$articles as \$article) {
        \$url     = SITEMAP_BASE_URL . '/article.php?id=' . (int)\$article['id'];
        \$lastmod = formatSitemapDate(\$article['updated_at']);
        \$xml .= buildUrlEntry(\$url, \$lastmod, 'monthly', '0.8');
    }

    \$xml .= '</urlset>';

    \$result = file_put_contents(SITEMAP_OUTPUT, \$xml, LOCK_EX);
    if (\$result === false) {
        error_log('[Sitemap] 写入 sitemap.xml 失败,请检查目录权限: ' . SITEMAP_OUTPUT);
        return false;
    }

    return count(\$articles);
}

function buildUrlEntry(string \$loc, string \$lastmod, string \$changefreq, string \$priority): string
{
    return "  <url>\n"
        . "    <loc>" . htmlspecialchars(\$loc, ENT_XML1 | ENT_QUOTES, 'UTF-8') . "</loc>\n"
        . "    <lastmod>{\$lastmod}</lastmod>\n"
        . "    <changefreq>{\$changefreq}</changefreq>\n"
        . "    <priority>{\$priority}</priority>\n"
        . "  </url>\n";
}

function formatSitemapDate(?string \$datetime): string
{
    if (empty(\$datetime)) return date('Y-m-d');
    \$ts = strtotime(\$datetime);
    return \$ts ? date('Y-m-d', \$ts) : date('Y-m-d');
}

if (basename(__FILE__) === basename(\$_SERVER['SCRIPT_FILENAME'] ?? '') || php_sapi_name() === 'cli') {
    if (php_sapi_name() === 'cli') {
        \$ok = generateSitemap();
        echo \$ok !== false
            ? "[Sitemap] 生成成功: " . SITEMAP_OUTPUT . "(共 {\$ok} 篇文章)" . PHP_EOL
            : "[Sitemap] 生成失败,请检查错误日志。" . PHP_EOL;
        exit(\$ok !== false ? 0 : 1);
    }

    \$key = \$_GET['key'] ?? '';
    if (!hash_equals(SITEMAP_SECRET, \$key)) {
        http_response_code(403);
        exit('403 Forbidden: 密钥错误');
    }

    \$ok = generateSitemap();
    header('Content-Type: text/plain; charset=utf-8');
    echo \$ok !== false
        ? "✅ sitemap.xml 已生成:" . SITEMAP_OUTPUT . "\n📄 共收录 {\$ok} 篇文章"
        : "❌ 生成失败,请查看 PHP 错误日志。";
    exit();
}
PHPEOF
}

# 写入 robots.txt
write_robots() {
    cat > "$ROBOTS_FILE" << ROBOTEOF
User-agent: *
Allow: /
Disallow: /admin/
Disallow: /uploads/
Disallow: /blog.db
Disallow: /generate_sitemap.php
Disallow: /composer.phar

Sitemap: ${SITE_URL}/sitemap.xml
ROBOTEOF
}

# 检查是否已安装
is_installed() {
    [ -f "$PHP_SCRIPT" ]
}

# ============================================================
# 菜单功能:1 安装地图
# ============================================================
do_install() {
    echo ""
    if is_installed; then
        warn "检测到已安装,请使用「2 更新地图」手动刷新,或直接重新安装(将覆盖现有文件)。"
        read -rp "是否继续重新安装?[y/N] " confirm
        [[ "$confirm" =~ ^[Yy]$ ]] || { info "已取消。"; return; }
    fi

    # 检查网站目录
    if [ ! -d "$SITE_DIR" ]; then
        err "网站目录不存在:$SITE_DIR"
        info "请检查脚本顶部 SITE_DIR 配置项。"
        return 1
    fi

    # 检查数据库
    if [ ! -f "$DB_FILE" ]; then
        err "未找到数据库文件:$DB_FILE"
        return 1
    fi

    info "正在写入 generate_sitemap.php ..."
    write_php && chown "$FILE_OWNER" "$PHP_SCRIPT" && ok "generate_sitemap.php 已写入" || { err "写入失败,请检查目录权限。"; return 1; }

    info "正在写入 robots.txt ..."
    write_robots && chown "$FILE_OWNER" "$ROBOTS_FILE" && ok "robots.txt 已写入" || { err "写入失败。"; return 1; }

    info "正在生成初始 sitemap.xml ..."
    $PHP_BIN "$SITE_DIR_CONTAINER/generate_sitemap.php"
    if [ -f "$SITEMAP_FILE" ]; then
        chown "$FILE_OWNER" "$SITEMAP_FILE"
        ok "sitemap.xml 已生成:$SITEMAP_FILE"
    else
        err "sitemap.xml 生成失败,请检查 PHP 错误日志。"
    fi

    echo ""
    ok "安装完成!"
    info "请在 1panel 面板「计划任务」中添加定时更新任务"
    info "Sitemap 地址:${SITE_URL}/sitemap.xml"
}

# ============================================================
# 菜单功能:2 更新地图
# ============================================================
do_update() {
    echo ""
    if ! is_installed; then
        err "尚未安装,请先选择「1 安装地图」。"
        return 1
    fi

    info "正在更新 sitemap.xml ..."
    $PHP_BIN "$SITE_DIR_CONTAINER/generate_sitemap.php"
    if [ -f "$SITEMAP_FILE" ]; then
        ok "sitemap.xml 更新成功"
        info "文件路径:$SITEMAP_FILE"
    else
        err "更新失败,请检查 PHP 错误日志。"
    fi
}

# ============================================================
# 菜单功能:3 卸载地图
# ============================================================
do_uninstall_map() {
    echo ""
    if ! is_installed; then
        warn "未检测到已安装的网站地图文件。"
        return
    fi

    warn "将删除以下文件:"
    echo "  - $PHP_SCRIPT"
    echo "  - $ROBOTS_FILE"
    echo "  - $SITEMAP_FILE"
    read -rp "确认卸载?[y/N] " confirm
    [[ "$confirm" =~ ^[Yy]$ ]] || { info "已取消。"; return; }

    [ -f "$PHP_SCRIPT" ]   && rm -f "$PHP_SCRIPT"   && ok "已删除 generate_sitemap.php"
    [ -f "$ROBOTS_FILE" ]  && rm -f "$ROBOTS_FILE"  && ok "已删除 robots.txt"
    [ -f "$SITEMAP_FILE" ] && rm -f "$SITEMAP_FILE" && ok "已删除 sitemap.xml"

    echo ""
    ok "卸载完成。"
    warn "请记得在 1panel 面板「计划任务」中手动删除对应任务。"
}

# ============================================================
# 菜单功能:4 卸载脚本
# ============================================================
do_uninstall_script() {
    echo ""
    warn "将删除本脚本自身:$SCRIPT_PATH"
    if is_installed; then
        warn "检测到网站地图仍已安装,建议先执行「3 卸载地图」。"
        read -rp "是否同时卸载地图?[y/N] " also_uninstall
        [[ "$also_uninstall" =~ ^[Yy]$ ]] && do_uninstall_map
    fi

    read -rp "确认删除脚本?[y/N] " confirm
    [[ "$confirm" =~ ^[Yy]$ ]] || { info "已取消。"; return; }

    rm -f "$SCRIPT_PATH"
    ok "脚本已删除:$SCRIPT_PATH"
    exit 0
}

# ============================================================
# 主菜单
# ============================================================
show_menu() {
    echo ""
    echo -e "${CYAN}=============================${RESET}"
    echo -e "${CYAN}       xBlog 网站地图        ${RESET}"
    echo -e "${CYAN}=============================${RESET}"
    echo "  1. 安装地图"
    echo "  2. 更新地图"
    echo "  3. 卸载地图"
    echo "  4. 卸载脚本"
    echo "  5. 使用说明"
    echo -e "${CYAN}=============================${RESET}"
    echo -n "请选择操作 [1-5],按 q 退出:" 
}

# ============================================================
# 菜单功能:5 使用说明
# ============================================================
do_help() {
    echo ""
    echo -e "${CYAN}=============================${RESET}"
    echo -e "${CYAN}          使用说明           ${RESET}"
    echo -e "${CYAN}=============================${RESET}"
    echo ""
    echo -e "${YELLOW}【脚本配置项】${RESET}(位于 map.sh 顶部)"
    echo "  SITE_DIR              网站根目录路径(宿主机)"
    echo "  SITE_DIR_CONTAINER    网站根目录路径(容器内)"
    echo "  SITE_URL              博客域名,末尾不加斜杠"
    echo "  SITEMAP_SECRET        手动触发更新的密钥"
    echo "  PHP_BIN               PHP 执行命令"
    echo ""
    echo -e "${YELLOW}【菜单功能】${RESET}"
    echo "  1. 安装地图  写入 generate_sitemap.php、robots.txt,"
    echo "               并立即生成 sitemap.xml"
    echo "  2. 更新地图  立即手动刷新一次 sitemap.xml"
    echo "  3. 卸载地图  删除相关文件(需手动删除 1panel 计划任务)"
    echo "  4. 卸载脚本  删除本脚本,可选同时卸载地图"
    echo ""
    echo -e "${YELLOW}【生成的文件】${RESET}(均位于网站根目录)"
    echo "  generate_sitemap.php  地图生成脚本"
    echo "  robots.txt            搜索引擎爬取规则"
    echo "  sitemap.xml           网站地图(自动生成)"
    echo ""
    echo -e "${YELLOW}【1panel 计划任务】${RESET}"
    echo "  在 1panel 面板新建计划任务,类型选「Shell 脚本」,"
    echo "  内容填写:"
    echo ""
    echo -e "    ${GREEN}bash $(realpath "$0") update${RESET}"
    echo ""
    echo -e "${YELLOW}【手动触发更新】${RESET}(浏览器访问)"
    echo "  ${SITE_URL}/generate_sitemap.php?key=${SITEMAP_SECRET}"
    echo ""
    echo -e "${YELLOW}【Sitemap 地址】${RESET}"
    echo "  ${SITE_URL}/sitemap.xml"
    echo ""
}

# ============================================================
# 入口
# ============================================================

# 支持直接传参,方便 1panel 计划任务非交互式调用
# 用法:bash /root/map.sh update
if [ "$1" = "update" ]; then
    do_update
    exit $?
fi

while true; do
    show_menu
    read -r choice
    case "$choice" in
        1) do_install ;;
        2) do_update ;;
        3) do_uninstall_map ;;
        4) do_uninstall_script ;;
        5) do_help ;;
        q|Q) echo ""; info "已退出。"; exit 0 ;;
        *) warn "无效输入,请输入 1-5 或 q。" ;;
    esac
    echo ""
    read -rp "按 Enter 返回菜单..." _
done

2. 保存文件

Ctrl + X → 确认保存按 Y → 确认文件名按 Enter

3. 赋予执行权限

chmod +x /root/map.sh

4. 运行脚本

bash /root/map.sh

💡 脚本使用说明

配置项

配置文件位于 map.sh 顶部,请根据实际环境修改

配置项 说明 示例值
SITE_DIR 网站根目录路径(宿主机) /opt/1panel/www/sites/xblog/index
SITE_DIR_CONTAINER 网站根目录路径(容器内) /www/sites/xblog/index
SITE_URL 博客域名(末尾不加斜杠) https://xblog.itxgo.com
SITEMAP_SECRET 手动触发更新的密钥 xblog_sitemap_2026
PHP_BIN PHP 执行命令(容器环境) docker exec php php

菜单功能

选项 功能 说明
1 安装地图 自动写入 generate_sitemap.phprobots.txt,并立即生成 sitemap.xml
2 更新地图 立即手动刷新一次 sitemap.xml
3 卸载地图 删除相关文件(需手动删除 1Panel 计划任务)
4 卸载脚本 删除本脚本 map.sh,可选同时卸载地图
5 使用说明 显示当前这份帮助文档

生成的文件

所有文件均位于网站根目录下:

文件 用途
generate_sitemap.php 网站地图生成脚本(核心)
robots.txt 搜索引擎爬取规则
sitemap.xml 网站地图(自动生成)

1Panel 计划任务配置

步骤说明

  1. 登录 1Panel 管理面板
  2. 进入「计划任务」功能
  3. 点击「新建计划任务」
  4. 任务类型选择「Shell 脚本
  5. 执行周期按需选择(推荐:每天执行一次)
  6. 脚本内容填写:
bash /root/map.sh update

🌐 手动触发更新

浏览器访问

https://xblog.itxgo.com/generate_sitemap.php?key=xblog_sitemap_2026

⚠️ 安全提示

  • 请将密钥 xblog_sitemap_2026 替换为您实际设置的 SITEMAP_SECRET
  • 建议使用复杂密钥,防止未授权访问

返回结果示例

成功时:

✅ sitemap.xml 已生成:/www/sites/xblog/index/sitemap.xml
📄 共收录 42 篇文章

失败时:

❌ 生成失败,请查看 PHP 错误日志。

🔗 Sitemap 地址

https://xblog.itxgo.com/sitemap.xml

📌 可将此地址提交至搜索引擎(Google Search Console、百度资源平台等)

❓ 常见问题

Q1:生成 sitemap.xml 失败怎么办?

检查以下几点:

  • 数据库文件 blog.db 是否存在且可读
  • 网站根目录是否有写入权限
  • PHP 错误日志中是否有详细报错信息

Q2:如何验证 sitemap.xml 是否正确?

# 检查 XML 格式是否合法
curl -s https://xblog.itxgo.com/sitemap.xml | xmllint --format -

Q3:容器环境下 PHP 命令无法执行?

确认 PHP_BIN 配置项是否正确,例如:

# Docker 环境示例
PHP_BIN="docker exec php php"

# 宿主机环境示例
PHP_BIN="php"

📞 技术支持

如有问题,请检查:

  1. 脚本执行权限:chmod +x /root/map.sh
  2. PHP 错误日志:通常在 /var/log/ 或容器内日志目录
  3. 文件权限:确保网站目录对执行用户可写

出处:http://xblog.itxgo.com/article.php?id=45
版权:本文采用 CC BY-NC-SA 4.0 协议,完整转载请注明来源。