WiKiss 0.3 常用 CLI 命令与运维实操
WiKiss 0.3 无自带管理 CLI,本文用通用 Linux 文件工具与 PHP 工具实测覆盖:wiki 文件结构巡检、grep 全库检索、tar 整库备份与恢复演练、historique 误删找回、opcache 与日志维护、口令权限文件维护,并复现修复 5 类真实故障。
WiKiss 0.3 常用 CLI 命令与运维实操
目标:WiKiss 0.3 没有自带任何管理 CLI——它的"运维命令体系"就是 Linux 通用文件工具 + PHP 自带工具的组合。本文在 Ubuntu 22.04 + Apache 2.4.52 + PHP 8.1.2 实测环境上,把 wiki 文件结构巡检、全库检索、tar 整库备份与恢复演练、误删页面从 historique 找回、opcache 与日志维护、口令与权限文件维护、升级兼容巡检逐条实测照抄,并复现 5 类真实故障(historique 目录 777、_config.php 权限被 opcache 掩盖、pages 属主错乱、补丁转写笔误致空表格、tar 中文清单转义)。
WiKiss 是"文件即 wiki":无数据库、无守护进程、无 CLI 入口,页面是 pages/*.txt、历史是 historique/<页面名>/*.bak、配置是 _config.php(机制详见 《WiKiss 0.3 核心使用与鉴权配置详解》 与 《WiKiss 0.3 配置参数详解》)。因此运维的全部动作都落在文件系统上,装好站点后的日常维护、备份恢复、故障排查,本文全部给出可直接执行的命令与实测回显。安装与 PHP 8.1 兼容修复见 《Ubuntu 22.04 安装 WiKiss 0.3 指南(PHP 8.1 兼容修复实录)》。
一、环境信息
| 项目 | 取值 |
|---|---|
| 操作系统 | Ubuntu 22.04(Jammy) |
| Web 服务器 | Apache 2.4.52(apache2) |
| PHP | 8.1.2-1ubuntu2.28(libapache2-mod-php8.1 + php8.1-mbstring) |
| WiKiss | 0.3(官方包 0.3.tgz,25,159 字节,md5sum = 2a657010027329570824175f28ecb170) |
| 部署目录 | /var/www/wikiss(属主 www-data:www-data) |
| 站点地址 | http://wiki.example.com/ |
| 数据目录 | pages/(页面)、historique/(历史备份) |
| 编辑口令 | $PASSWORD = "Q1w2E3r4T!"(第七章演示轮换为 S3cur3Pass!2) |
| 运行生成物 | 站点根 rss.xml(wkp_Rss 插件在保存时写出) |
| 日志 | /var/log/apache2/wikiss-access.log、wikiss-error.log(vhost 显式指定) |
⚠️ 注意:本文所有命令假定站点已按 《Ubuntu 22.04 安装 WiKiss 0.3 指南(PHP 8.1 兼容修复实录)》 完成安装并打满 9 处 PHP 8.1 兼容补丁。未打补丁时搜索/保存会 500,运维演练无从谈起。
二、工具概览
WiKiss 官方发布包不含 wikiss-cli、bin/ 或任何管理脚本,运维工具箱全部是系统通用命令:
| 运维目的 | 命令/工具 | 对应章节 |
|---|---|---|
| 结构与属主巡检 | ls -la、stat、find、du | 四 |
| 全库内容检索 | grep -r、find -newermt | 5.1 |
| 整库备份/恢复 | tar、sha256sum | 5.2 / 5.3 |
| 误删页面找回 | ls -t + tail -n +3(读 historique/*.bak) | 5.4 |
| PHP 语法体检 | php -l | 6.1 |
| 缓存刷新 | apache2ctl graceful / stop+start | 6.2 |
| 日志分析 | tail、awk、grep | 6.4 |
| 口令与权限维护 | sed、chown、chmod | 七 |
所有命令用 root(或 sudo)在服务器上执行;凡以 root 身份触碰过站点文件,收尾都要把属主交还 www-data(见 7.3 实测)。
三、前置条件
3.1 站点可用性验证
curl -s -o /dev/null -w 'HOME:%{http_code}\n' http://wiki.example.com/
curl -s -o /dev/null -w 'SEARCH:%{http_code}\n' 'http://wiki.example.com/?action=search&query=Aide'
curl -s -o /dev/null -w 'RSS:%{http_code}\n' http://wiki.example.com/rss.xml
实测回显(刚装完、尚未保存过任何页面时):
HOME:200
SEARCH:200
RSS:404
RSS:404 不是故障:rss.xml 是 wkp_Rss 插件在第一次保存页面时才写出的静态文件(生成与清理见 6.3)。
3.2 准备演示数据
通过 HTTP(带口令)创建 4 个测试页,模拟已使用一段时间的 wiki:
curl -s -X POST 'http://wiki.example.com/' \
--data-urlencode 'page=TestS9' \
--data-urlencode 'content=| A | B |
| 1 | 2 |' \
--data-urlencode 'sc=Q1w2E3r4T!' \
-o /dev/null -w 'TestS9:%{http_code}\n'
curl -s -X POST 'http://wiki.example.com/' \
--data-urlencode 'page=ServerNotes' \
--data-urlencode 'content=本机 Nginx 反代配置要点:proxy_pass 指向 127.0.0.1:80。' \
--data-urlencode 'sc=Q1w2E3r4T!' \
-o /dev/null -w 'ServerNotes:%{http_code}\n'
curl -s -X POST 'http://wiki.example.com/' \
--data-urlencode 'page=DeployChecklist' \
--data-urlencode 'content=部署检查清单:php -l 通过、pages 可写、historique 非空。' \
--data-urlencode 'sc=Q1w2E3r4T!' \
-o /dev/null -w 'Deploy:%{http_code}\n'
curl -s -X POST 'http://wiki.example.com/' \
--data-urlencode 'page=运维手册' \
--data-urlencode 'content=这是中文标题页面,验证 UTF-8 文件名全链路。' \
--data-urlencode 'sc=Q1w2E3r4T!' \
-o /dev/null -w 'CN:%{http_code}\n'
实测回显(302 = 保存成功并跳转页面视图,机制见 《WiKiss 0.3 核心使用与鉴权配置详解》):
TestS9:302
ServerNotes:302
Deploy:302
CN:302
最后一个页面名为中文 运维手册,用于贯穿全文的中文文件名链路验证(URL 中需百分号编码,如 ?page=%E8%BF%90%E7%BB%B4%E6%89%8B%E5%86%8C)。
四、wiki 文件结构 CLI 巡检
WiKiss 的健康状态全部可以从文件层面判定。本节命令构成日常巡检清单,建议按序执行。
4.1 站点目录与属主
ls -la /var/www/wikiss/
实测回显:
total 100
drwxr-xr-x 5 www-data www-data 4096 Nov 2 18:17 .
drwxr-xr-x 4 root root 4096 Nov 2 18:15 ..
-rw-r--r-- 1 www-data www-data 17987 Oct 30 2007 COPYING
-rwxr-xr-x 1 www-data www-data 1155 Nov 2 18:16 _config.php
drwxr-xr-x 8 www-data www-data 4096 Nov 2 18:17 historique
-rwxr-xr-x 1 www-data www-data 26433 Nov 2 18:16 index.php
drwxr-xr-x 2 www-data www-data 4096 Nov 2 18:17 pages
drwxr-xr-x 2 www-data www-data 4096 Oct 30 2007 plugins
-rw-r--r-- 1 www-data www-data 2054 Nov 2 18:17 rss.xml
-rwxr-xr-x 1 www-data www-data 2422 Oct 30 2007 style.css
-rwxr-xr-x 1 www-data www-data 1297 Oct 30 2007 template.html
-rw-r--r-- 1 www-data www-data 12848 Oct 30 2007 tigerwiki2wikiss.php
解读:官方包文件的时间戳停在 2007-10-30(包内 mtime),凡是显示近期时间的文件都是被改过或新生成的——_config.php(配置改动)、index.php(打过补丁)、rss.xml(运行生成)。巡检时"哪些文件被动过"一眼可见。
stat -c "%A %U:%G %n" /var/www/wikiss/_config.php /var/www/wikiss/index.php \
/var/www/wikiss/pages /var/www/wikiss/historique /var/www/wikiss/plugins
实测回显:
-rwxr-xr-x www-data:www-data /var/www/wikiss/_config.php
-rwxr-xr-x www-data:www-data /var/www/wikiss/index.php
drwxr-xr-x www-data:www-data /var/www/wikiss/pages
drwxr-xr-x www-data:www-data /var/www/wikiss/historique
drwxr-xr-x www-data:www-data /var/www/wikiss/plugins
判定标准:配置、入口、三个数据目录的属主都应是 www-data(Web 进程要写 pages/ 与 historique/)。_config.php 带执行位(755)是官方包习惯,无害;介意可 chmod 644,但注意 10.2 的 opcache 教训——改权限后必须重启 Apache 才确定生效。
4.2 pages/ 页面文件巡检
find /var/www/wikiss/pages -name "*.txt" | wc -l
7
页面总数 = .txt 文件数(页面名即文件名,无索引表可查,wc -l 就是权威计数)。
find /var/www/wikiss/pages -name "*.txt" -printf "%s %p\n" | sort -n
实测回显:
19 /var/www/wikiss/pages/TestS9.txt
61 /var/www/wikiss/pages/运维手册.txt
69 /var/www/wikiss/pages/ServerNotes.txt
72 /var/www/wikiss/pages/DeployChecklist.txt
2138 /var/www/wikiss/pages/Accueil.txt
2461 /var/www/wikiss/pages/AideTables.txt
3694 /var/www/wikiss/pages/Aide.txt
find /var/www/wikiss/pages -type f -size 0 -print
实测回显为空(体检通过)。这条命令是必做项:PHP 8 未打补丁的安装在保存时会先 fopen("w") 清空文件再崩溃,留下 0 字节页面(完整事故链见 《Ubuntu 22.04 安装 WiKiss 0.3 指南(PHP 8.1 兼容修复实录)》 修复 6/7)。0 字节页面在打过补丁的环境里浏览不会报错(fread 守卫直接渲染空内容),静默地丢内容,只能靠这条命令扫出来。
find /var/www/wikiss/pages -name "*.txt" -newermt "2026-11-02" -printf "%TY-%Tm-%Td %TH:%TM %p\n"
实测回显(按修改时间过滤"最近动过的页面",与 Web 端 ?action=recent 同源——都看 mtime):
2026-11-02 18:17 /var/www/wikiss/pages/TestS9.txt
2026-11-02 18:17 /var/www/wikiss/pages/DeployChecklist.txt
2026-11-02 18:17 /var/www/wikiss/pages/ServerNotes.txt
2026-11-02 18:17 /var/www/wikiss/pages/运维手册.txt
4.3 historique/ 备份结构与 .bak 格式
ls -la /var/www/wikiss/historique/
实测回显:
total 32
drwxr-xr-x 8 www-data www-data 4096 Nov 2 18:17 .
drwxr-xr-x 5 www-data www-data 4096 Nov 2 18:17 ..
drwxr-xr-x 2 www-data www-data 4096 Oct 30 2007 Accueil
drwxr-xr-x 2 www-data www-data 4096 Oct 30 2007 Aide
drwxrwxrwx 2 www-data www-data 4096 Nov 2 18:17 DeployChecklist
drwxrwxrwx 2 www-data www-data 4096 Nov 2 18:17 ServerNotes
drwxrwxrwx 2 www-data www-data 4096 Nov 2 18:17 TestS9
drwxrwxrwx 2 www-data www-data 4096 Nov 2 18:17 运维手册
一眼可见两种权限:官方包自带的 Accueil/Aide 目录是 755,运行期新建的备份目录全是 777——这是 WiKiss 源码写死的(复现与修复见 10.1)。
find /var/www/wikiss/historique -type f -name "*.bak" | sort
实测回显:
/var/www/wikiss/historique/Accueil/20070912-1556.bak
/var/www/wikiss/historique/Aide/20070912-1846.bak
/var/www/wikiss/historique/DeployChecklist/20261102-1817.bak
/var/www/wikiss/historique/ServerNotes/20261102-1817.bak
/var/www/wikiss/historique/TestS9/20261102-1817.bak
/var/www/wikiss/historique/运维手册/20261102-1817.bak
备份文件名格式 Ymd-Hi.bak(分钟粒度,含 $LOCAL_HOUR 偏移)。.bak 不是页面文件的原样拷贝,实测逐字节看头部:
cat -A /var/www/wikiss/historique/TestS9/20261102-1820.bak
$
// 2026-11-02 18:20 / 127.0.0.1$
| A | B |$
| 1 | 2 |$
| 3 | 4 |$
$
格式 = 空行 + // 时间 / 保存者IP 头 + 该次保存的内容 + 尾部空行。这决定了 5.4 找回命令必须剥掉头部两行。
两个源码级语义(index.php 保存分支实测取证):
if ($_POST["content"] == "")
unlink($PAGES_DIR . $PAGE_TITLE . ".txt");
if (! $file = @fopen($complete_dir_s . date("Ymd-Hi", mktime(date("H") + $LOCAL_HOUR)) . ".bak", "a"))
- 删除页面不写备份:空内容提交走
unlink分支,不产生新.bak——误删后能找回的版本是"最后一次保存"写入的那个.bak; - 备份以追加模式(
"a")写入,文件名只到分钟:同一分钟内多次保存会追加进同一个.bak。实测:
curl -s -X POST 'http://wiki.example.com/' --data-urlencode 'page=DeployChecklist' \
--data-urlencode 'content=清单 v2' --data-urlencode 'sc=Q1w2E3r4T!' -o /dev/null -w 'SAVE-A:%{http_code}\n'
curl -s -X POST 'http://wiki.example.com/' --data-urlencode 'page=DeployChecklist' \
--data-urlencode 'content=清单 v3' --data-urlencode 'sc=Q1w2E3r4T!' -o /dev/null -w 'SAVE-B:%{http_code}\n'
LATEST=$(ls -t /var/www/wikiss/historique/DeployChecklist/*.bak | head -1)
echo "该 .bak 内快照条数: $(grep -c '^// ' "$LATEST")"
cat "$LATEST"
SAVE-A:302
SAVE-B:302
该 .bak 内快照条数: 2
// 2026-11-02 18:19 / 127.0.0.1
清单 v2
// 2026-11-02 18:19 / 127.0.0.1
清单 v3
批量脚本每分钟狂存页面时,历史粒度会塌缩成"一分钟一条",Web 端历史页(?action=history)同样只显示这一个合并文件——需要细粒度历史时,把批量保存节流到每分钟一次以上。
4.4 属主与权限体检
find /var/www/wikiss ! -user www-data
实测回显为空(全部归 www-data)。凡 root 用 cp/重定向等方式覆盖过站点文件,这条命令就会点名漏网文件(sed -i 例外,见 7.3 实测)。
find /var/www/wikiss -type f -perm /o+w
find /var/www/wikiss -type d -perm /o+w
实测回显:
(文件:空)
/var/www/wikiss/historique/运维手册
/var/www/wikiss/historique/ServerNotes
/var/www/wikiss/historique/TestS9
/var/www/wikiss/historique/DeployChecklist
全局可写目录如期命中——即 4.3 的 777 现象,处置见 10.1。
4.5 体积与游离文件巡检
du -sh /var/www/wikiss/pages /var/www/wikiss/historique /var/www/wikiss
32K /var/www/wikiss/pages
52K /var/www/wikiss/historique
100K /var/www/wikiss
全站 100K 量级——备份不需要任何专门工具,tar 一次秒级完成(5.2)。
find /var/www/wikiss -maxdepth 1 -name "*.txt"
实测回显为空。这条命令查"站点根目录游离页面文件":配置失效时 $PAGES_DIR 未定义,页面会被写到站点根目录而不是 pages/(完整事故复现见 10.2),根目录出现 *.txt 即为异常信号。
五、页面批量操作
5.1 grep 全库检索
Web 端搜索(?action=search)走 PHP 正则逐页匹配;CLI 侧直接 grep,更快、支持完整正则、且不经过 PHP 8 兼容路径(未打补丁的安装 Web 搜索即 500,CLI 检索不受影响)。
grep -ril "nginx" /var/www/wikiss/pages/
/var/www/wikiss/pages/ServerNotes.txt
-r 递归、-i 忽略大小写、-l 只列文件名。带行号与内容:
grep -rn --include="*.txt" "proxy_pass" /var/www/wikiss/pages/
/var/www/wikiss/pages/ServerNotes.txt:1:本机 Nginx 反代配置要点:proxy_pass 指向 127.0.0.1:80。
按 Wiki 语法特征检索(例如"哪些页面含表格",表格行以竖线开头,语法见 《WiKiss 0.3 页面语法与常用操作详解》):
grep -rl "^ *|" /var/www/wikiss/pages/
/var/www/wikiss/pages/AideTables.txt
/var/www/wikiss/pages/TestS9.txt
中文内容检索同样直接:grep -rl "中文标题页面" /var/www/wikiss/pages/ 命中 运维手册.txt(文件与内容都是 UTF-8,grep 按字节匹配,无需任何 locale 特殊处理)。
5.2 tar 整库备份
WiKiss 的备份对象就是整个部署目录(页面 + 历史 + 配置 + 代码全在里面):
tar czf /root/wikiss-backup-$(date +%Y%m%d-%H%M%S).tar.gz -C /var/www wikiss
ls -la /root/wikiss-backup-*.tar.gz
sha256sum /root/wikiss-backup-20261102-181849.tar.gz
实测回显:
-rw-r--r-- 1 root root 26799 Nov 2 18:18 /root/wikiss-backup-20261102-181849.tar.gz
f5f239d392a71a46151616bc2e53118015d72f3b361b250265821ef6829302da /root/wikiss-backup-20261102-181849.tar.gz
核对备份内容清单:
tar tzf /root/wikiss-backup-20261102-181849.tar.gz | head -8
echo "...(共 $(tar tzf /root/wikiss-backup-20261102-181849.tar.gz | wc -l) 个条目)"
wikiss/
wikiss/historique/
wikiss/historique/\350\277\220\347\273\264\346\211\213\345\206\214/
wikiss/historique/\350\277\220\347\273\264\346\211\213\345\206\214/20261102-1817.bak
wikiss/historique/ServerNotes/
wikiss/historique/ServerNotes/20261102-1817.bak
wikiss/historique/TestS9/
wikiss/historique/TestS9/20261102-1817.bak
...(共 33 个条目)
注意第三行:中文文件名被 GNU tar 默认转义成八进制(\350\277\220\347\273\264\346\211\213\345\206\214 即"运维手册")。数据没坏(解包后名字正常),只是清单不可读,用 --quoting-style=literal 按原样显示:
tar --quoting-style=literal -tzf /root/wikiss-backup-20261102-181849.tar.gz | grep "运维"
wikiss/historique/运维手册/
wikiss/historique/运维手册/20261102-1817.bak
wikiss/pages/运维手册.txt
💡 技术拓展:备份策略不止"整库 tar"一种。常见方案对比——
方案 特点 适用 tar 全量单档 一次打包、sha256 可校验、恢复即解包 库体量小(WiKiss 全站常 < 百 MB)、按天留档 rsync 增量同步 只传差异,目标目录始终与源保持一致 频繁备份、异地同步 git 版本化 逐次提交、可追溯每页历史 想把 historique/升级成真正的版本库本文采用 tar 全量:实测环境整库仅 100K(4.5),单档 + 校验和是恢复演练(5.3)最简可验证的形态。
5.3 整库恢复演练
恢复演练的正确做法是先人为制造灾难,再按备份恢复,而不是"解包看一眼"。实测全流程:
# 1. 模拟 pages/ 与 historique/ 全毁(灾难演练,生产环境切勿直接照抄)
rm -rf /var/www/wikiss/pages /var/www/wikiss/historique
ls /var/www/wikiss/
COPYING
_config.php
index.php
plugins
rss.xml
style.css
template.html
tigerwiki2wikiss.php
# 2. 解到临时目录(绝不直接解回原位,先验货)
mkdir -p /tmp/wikiss-restore
tar xzf /root/wikiss-backup-20261102-181849.tar.gz -C /tmp/wikiss-restore
# 3. 回灌数据目录
cp -a /tmp/wikiss-restore/wikiss/pages /tmp/wikiss-restore/wikiss/historique /var/www/wikiss/
chown -R www-data:www-data /var/www/wikiss/pages /var/www/wikiss/historique
rm -rf /tmp/wikiss-restore
ls /var/www/wikiss/pages/
Accueil.txt
Aide.txt
AideTables.txt
DeployChecklist.txt
ServerNotes.txt
TestS9.txt
运维手册.txt
# 4. 恢复后验证:首页、普通页、中文页内容逐项核对
curl -s -o /dev/null -w 'HOME:%{http_code} ' http://wiki.example.com/
curl -s -o /dev/null -w 'ServerNotes:%{http_code}\n' 'http://wiki.example.com/?page=ServerNotes'
curl -s 'http://wiki.example.com/?page=%E8%BF%90%E7%BB%B4%E6%89%8B%E5%86%8C' | grep -c "UTF-8 文件名"
HOME:200 ServerNotes:200
1
要点:第 2 步先解到临时目录再挑选回灌,避免备份包里的旧代码覆盖线上已打补丁的 index.php(若整目录直接解回 /var/www,2007 年的原始代码会覆盖补丁版本——恢复时只回灌 pages/、historique/、必要时 _config.php 三类数据,代码文件单独核对)。第 3 步 chown 不可省:备份时的属主信息虽随 -a 保留,但恢复目标机器的 Web 进程用户不一定同名,落位后统一交还给实际运行用户。
5.4 误删页面从 historique 找回
场景:TestS9 编辑出 v2 后被误删(空内容提交,官方删除路径)。找回全程 CLI:
# 1. 先正常编辑产生 v2
curl -s -X POST 'http://wiki.example.com/' --data-urlencode 'page=TestS9' \
--data-urlencode 'content=| A | B |
| 1 | 2 |
| 3 | 4 |' --data-urlencode 'sc=Q1w2E3r4T!' -o /dev/null -w 'SAVE2:%{http_code}\n'
# 2. 模拟误删:空内容提交
curl -s -X POST 'http://wiki.example.com/' --data-urlencode 'page=TestS9' \
--data-urlencode 'content=' --data-urlencode 'sc=Q1w2E3r4T!' -o /dev/null -w 'DEL:%{http_code}\n'
ls /var/www/wikiss/pages/ | grep -c TestS9
SAVE2:302
DEL:302
0
页面文件已消失。确认备份仍在:
ls -la /var/www/wikiss/historique/TestS9/
cat "$(ls -t /var/www/wikiss/historique/TestS9/*.bak | head -1)"
total 16
drwxrwxrwx 2 www-data www-data 4096 Nov 2 18:19 .
drwxr-xr-x 8 www-data www-data 4096 Nov 2 18:17 ..
-rw-r--r-- 1 www-data www-data 56 Nov 2 18:17 20261102-1817.bak
-rw-r--r-- 1 www-data www-data 65 Nov 2 18:19 20261102-1819.bak
// 2026-11-02 18:19 / 127.0.0.1
| A | B |
| 1 | 2 |
| 3 | 4 |
最新 .bak(ls -t 取 mtime 最新)里就是被删前的 v2 内容。回灌(剥 4.3 实测的头部两行,sed -z 去尾部空行):
LATEST=$(ls -t /var/www/wikiss/historique/TestS9/*.bak | head -1)
tail -n +3 "$LATEST" | sed -z 's/\n*$//' > /var/www/wikiss/pages/TestS9.txt
chown www-data:www-data /var/www/wikiss/pages/TestS9.txt
cat -A /var/www/wikiss/pages/TestS9.txt
| A | B |$
| 1 | 2 |$
| 3 | 4 |
与原页面文件逐字节一致(保存时 trim() 过的正文本就没有尾部空行)。验证:
curl -s -o /dev/null -w 'GET:%{http_code} ' 'http://wiki.example.com/?page=TestS9'
curl -s -o /dev/null -w 'HISTORY:%{http_code}\n' 'http://wiki.example.com/?page=TestS9&action=history'
GET:200 HISTORY:200
找回完成,Web 历史页也能列出全部备份。两条红线:删除操作不写备份(4.3 源码取证),所以 historique/ 是误删唯一的救济来源,定期随 5.2 一起打包;找回后必须 chown,否则下次 Web 保存该页会栽权限(10.3)。Web 端另有 &action=history + restore=1 的人工恢复入口,行为见 《WiKiss 0.3 核心使用与鉴权配置详解》。
六、PHP 侧可执行维护
6.1 php -l 全库语法体检
改动 index.php/插件(打补丁、装第三方 wkp_*.php)之后、重载 Apache 之前,逐文件做语法校验:
php -l /var/www/wikiss/index.php
php -l /var/www/wikiss/_config.php
php -l /var/www/wikiss/tigerwiki2wikiss.php
php -l /var/www/wikiss/plugins/wkp_ListPlugins.php
php -l /var/www/wikiss/plugins/wkp_Rss.php
php -l /var/www/wikiss/plugins/wkp_Tables.php
实测回显:
No syntax errors detected in /var/www/wikiss/index.php
No syntax errors detected in /var/www/wikiss/_config.php
No syntax errors detected in /var/www/wikiss/tigerwiki2wikiss.php
No syntax errors detected in /var/www/wikiss/plugins/wkp_ListPlugins.php
No syntax errors detected in /var/www/wikiss/plugins/wkp_Rss.php
No syntax errors detected in /var/www/wikiss/plugins/wkp_Tables.php
⚠️ 注意:
php -l只保证"能解析",不保证"能运行"。10.4 的空表格故障里,正则缺闭合分隔符——php -l全绿,错误只在运行期进 error.log。语法体检必须配合 6.4 的日志抽查。
6.2 opcache 与配置生效
Ubuntu 的 PHP 默认启用 OPcache,直接影响"改了文件多久生效":
ls /etc/php/8.1/apache2/conf.d/ | grep opcache
php -i | grep -E 'opcache.enable |opcache.enable_cli|validate_timestamps|revalidate_freq'
实测回显:
10-opcache.ini
opcache.enable => On => On
opcache.enable_cli => Off => Off
opcache.revalidate_freq => 2 => 2
opcache.validate_timestamps => On => On
生效规则(实测):
sed改_config.php内容会更新 mtime,validate_timestamps=On+revalidate_freq=2⇒ 约 2 秒内自动生效,无需重启;chmod/chown不改 mtime,OPcache 不会感知——文件缓存照旧服务,这是 10.2 故障"看起来没事"的根源;- CLI 侧
opcache.enable_cli=Off,且php -r 'var_dump(function_exists("opcache_reset"));'实测返回bool(true)的同时ini_get("opcache.enable_cli")返回"0"——CLI 的 OPcache 与 Apache 模块的 OPcache 是两块独立共享内存,命令行调用opcache_reset()清不掉 Web 侧缓存。
让 Apache 侧缓存确定失效的运维手段是重载/重启:
apache2ctl graceful # 平滑重载,清空 OPcache 后继续服务
改动代码/权限后拿不准是否生效时,直接重启并核对页面特征字串(10.2 实测即用此法让故障显形):
apache2ctl stop; sleep 1; apache2ctl start
curl -s -o /dev/null -w 'HOME:%{http_code}\n' http://wiki.example.com/
6.3 运行生成物 rss.xml 的清理与再生
rss.xml 是站点根目录唯一的运行期生成文件(wkp_Rss 在每次保存页面时重写):
ls -la /var/www/wikiss/rss.xml
head -3 /var/www/wikiss/rss.xml
rm /var/www/wikiss/rss.xml
curl -s -o /dev/null -w 'RSS-after-rm:%{http_code}\n' http://wiki.example.com/rss.xml
-rw-r--r-- 1 www-data www-data 2054 Nov 2 18:19 /var/www/wikiss/rss.xml
<rss version="0.91">
<channel>
<title>WiKiss : le Wiki Kiss</title>
RSS-after-rm:404
删除它无数据损失,下一次任意页面保存即自动再生:
curl -s -X POST 'http://wiki.example.com/' --data-urlencode 'page=TestS9' \
--data-urlencode 'content=| A | B |
| 1 | 2 |
| 3 | 4 |' --data-urlencode 'sc=Q1w2E3r4T!' -o /dev/null -w 'SAVE-regen:%{http_code} '
curl -s -o /dev/null -w 'RSS-after-save:%{http_code}\n' http://wiki.example.com/rss.xml
SAVE-regen:302 RSS-after-save:200
除此之外 WiKiss 没有缓存目录、没有会话文件、没有临时落盘——"清缓存"在 WiKiss 运维里只涉及 6.2 的 OPcache 与这一节。
6.4 Apache 日志查看与访问统计
日志位置由 vhost 显式指定(安装篇第五章模板),巡检先看大小再看内容:
ls -la /var/log/apache2/ | grep wikiss
-rw-r--r-- 1 root root 2246 Nov 2 18:20 wikiss-access.log
-rw-r--r-- 1 root root 3645 Nov 2 18:20 wikiss-error.log
访问热点(哪些页面在被读):
awk '{print $7}' /var/log/apache2/wikiss-access.log | sort | uniq -c | sort -rn | head -5
实测回显:
10 /
6 /?page=TestS9
2 /rss.xml
2 /?page=%E8%BF%90%E7%BB%B4%E6%89%8B%E5%86%8C
1 /?page=TestS9&action=history
错误日志排障主入口:
tail -3 /var/log/apache2/wikiss-error.log
grep -ci 'fatal' /var/log/apache2/wikiss-error.log
实测回显(tail 输出节选其中一行 + fatal 计数):
[Mon Nov 02 18:19:46.617867 2026] [php:warn] [pid 9466] [client 127.0.0.1:47590] PHP Warning: preg_replace_callback(): No ending delimiter '/' found in /var/www/wikiss/plugins/wkp_Tables.php on line 49
0
判读口径:Fatal error 计数为 0 说明无致命问题;WiKiss 0.3 打完 9 处补丁后仍有两类良性 Warning 会持续刷日志(功能不受影响,属 2007 代码未穷尽的 PHP 8 规范化告警):
[Mon Nov 02 18:22:04.432500 2026] [php:warn] [pid 9466] [client 127.0.0.1:35902] PHP Warning: Undefined array key "AutorisationWiKiss" in /var/www/wikiss/index.php on line 22
[Mon Nov 02 18:23:31.794429 2026] [php:warn] [pid 9466] [client 127.0.0.1:49848] PHP Warning: Undefined variable $CONTENT_RSS in /var/www/wikiss/plugins/wkp_Rss.php on line 52
前者在无 Cookie 的每次保存请求出现,后者在 rss.xml 首次生成路径出现。运维基线:grep -ci 'fatal' 应为 0,Warning 数量突增则对照 10.4 检查是否又引入运行期正则/函数问题。
七、鉴权与权限文件维护
7.1 口令轮换
口令就是全站编辑权(无用户体系,机制见 《WiKiss 0.3 核心使用与鉴权配置详解》),轮换步骤实测:
sed -n '3p' /var/www/wikiss/_config.php
cp /var/www/wikiss/_config.php /root/_config.php.bak-$(date +%H%M%S)
sed -i 's/Q1w2E3r4T!/S3cur3Pass!2/' /var/www/wikiss/_config.php
sed -n '3p' /var/www/wikiss/_config.php
$PASSWORD = "Q1w2E3r4T!";
$PASSWORD = "S3cur3Pass!2";
验证旧口令被拒、新口令放行:
curl -s -X POST 'http://wiki.example.com/' --data-urlencode 'page=AuthProbe' \
--data-urlencode 'content=x' --data-urlencode 'sc=Q1w2E3r4T!' \
-o /dev/null -w 'OLD:%{http_code} -> %{redirect_url}\n'
curl -s -X POST 'http://wiki.example.com/' --data-urlencode 'page=AuthProbe' \
--data-urlencode 'content=x' --data-urlencode 'sc=S3cur3Pass!2' \
-o /dev/null -w 'NEW:%{http_code} -> %{redirect_url}\n'
OLD:302 -> http://wiki.example.com/?page=AuthProbe&action=edit&error=1
NEW:302 -> http://wiki.example.com/?page=AuthProbe
error=1 回编辑页 = 鉴权拒绝且不落盘;直达页面视图 = 保存成功。改的是文件内容(mtime 变化),按 6.2 约 2 秒内生效,无需重启。
7.2 Cookie 通道巡检
鉴权三通道中 Cookie 通道存的是 md5($PASSWORD)(源码 index.php:21-26 实测取证):
grep -n 'pwd\|AutorisationWiKiss' /var/www/wikiss/index.php | head -4
php -r 'echo md5("S3cur3Pass!2"), "\n";'
21: $pwd=md5($PASSWORD);
22: if (($_COOKIE['AutorisationWiKiss'] == $pwd) || ((isset($_POST["sc"]) && $_POST["sc"] == $PASSWORD)) || ($PASSWORD == ""))
24: if (($PASSWORD != "") && (empty($_COOKIE['AutorisationWiKiss']) || ($_COOKIE['AutorisationWiKiss'] != $pwd)))
26: setcookie('AutorisationWiKiss',$pwd, time() + 365*24*3600);
0f6d9aaae566abfb80fe6732bb060914
带正确 Cookie、不带 sc 保存(模拟老浏览器免口令续存):
curl -s -X POST 'http://wiki.example.com/' --data-urlencode 'page=AuthProbe' \
--data-urlencode 'content=y' --data-urlencode 'sc=' \
-b 'AutorisationWiKiss=0f6d9aaae566abfb80fe6732bb060914' \
-o /dev/null -w 'COOKIE:%{http_code}\n'
curl -s -X POST 'http://wiki.example.com/' --data-urlencode 'page=AuthProbe' \
--data-urlencode 'content=z' --data-urlencode 'sc=' \
-b 'AutorisationWiKiss=deadbeef' \
-o /dev/null -w 'BADCOOKIE:%{http_code} -> %{redirect_url}\n'
COOKIE:302
BADCOOKIE:302 -> http://wiki.example.com/?page=AuthProbe&action=edit&error=1
两条运维含义:① 轮换口令后 md5 值改变,所有已发放的一年期 Cookie 立即失效,全体编辑者需重新输入口令;② Cookie 值可由口令离线算出(md5 等价物),拿到 Cookie 即拿到编辑权——口令轮换也是 Cookie 泄露的止血手段,生产务必叠加 HTTPS。
7.3 文件属主维护
7.1 改完配置后核对属主:
ls -la /var/www/wikiss/_config.php
-rwxr-xr-x 1 www-data www-data 1157 Nov 2 18:22 /var/www/wikiss/_config.php
实测结论:GNU sed -i 以 root 改写也会保留原属主(它重建文件后回写元数据),所以 sed 改配置不会引入属主问题。会引入问题的是 cp 覆盖、重定向 > 覆写、解包不带 -a 等操作——统一用 4.4 的 find /var/www/wikiss ! -user www-data 扫出,再 chown 归位。
另一个实测要点(10.3 完整复现):目录属主只影响"新建文件",不影响"覆盖已有文件"。pages/ 目录被 root 拿走后,编辑既有页面照存(写的是 www-data 属主的旧文件),只有新建页面才失败——权限故障对"编辑"静默、对"新建"报错,巡检时不能只测编辑路径。
八、升级与迁移注意
8.1 PHP 版本兼容巡检
WiKiss 0.3 是 2007 年代码,官方无更新,真正的"升级"是 PHP 运行时升级。每次升 PHP 大版本后,先跑两条巡检命令定位兼容雷区(9 处修复的完整事故实录见 《Ubuntu 22.04 安装 WiKiss 0.3 指南(PHP 8.1 兼容修复实录)》):
grep -rn 'create_function\|get_magic_quotes_gpc\|MB_OVERLOAD' /var/www/wikiss/*.php /var/www/wikiss/plugins/*.php
php -v | head -1
实测回显(打完补丁的环境):
/var/www/wikiss/index.php:86: // ini_set("mbstring.func_overload",MB_OVERLOAD_STRING); // PHP8: constant removed
PHP 8.1.2-1ubuntu2.28 (cli) (built: Oct 19 2026 18:54:45) (NTS)
判读:三个已移除符号只剩注释行命中 = 补丁在位;若命中出现在可执行代码行(无 // 前导),说明补丁缺失或被回退。随后按 6.1 全库 php -l、按 6.4 清日志后把浏览/搜索/保存/表格渲染各点一遍,grep -ci 'fatal' 仍为 0 才算升级通过。
8.2 整库迁移
WiKiss 迁移 = 目录搬家,无导出导入格式:
- 源机按 5.2 打 tar 并记 sha256;
- 目标机装同版本 PHP +
mbstring(依赖仅此一扩展,安装步骤见 《Ubuntu 22.04 安装 WiKiss 0.3 指南(PHP 8.1 兼容修复实录)》 二、三章); - 解包到
/var/www/wikiss,chown -R www-data:www-data; - 配 vhost 指向新目录,
apache2ctl graceful; - 按 5.3 第 4 步的三条 curl 验证首页/普通页/中文页。
站点根目录的 tigerwiki2wikiss.php(12,848 字节)是 2007 年从前任 TigerWiki 导入数据的转换脚本,与 WiKiss 版本间迁移无关,可保留可删除,不影响运行。
九、注意事项
- root 动过文件就 chown 回去:
find /var/www/wikiss ! -user www-data是收尾必查项(4.4/7.3)。 - historique/ 是误删的唯一救济:删除不写备份(4.3),清理磁盘时绝不动
historique/;备份时与pages/一起打包(5.2 的整库 tar 天然覆盖)。 - 777 目录会复发:修完(10.1)再有新页面保存就重新出现,巡检命令
find /var/www/wikiss -type d -perm /o+w建议进定时任务。 - 改权限/属主后 opcache 不感知:mtime 不变则缓存不失效(6.2),验证一律先
apache2ctl graceful。 - 恢复演练只回灌数据目录:整包解回会覆盖打过补丁的代码(5.3 要点)。
- 备份文件放站点目录之外:
/root或异地;放进pages/会被 Web 搜索命中、放进站点根目录会进下一次 tar。
十、常见故障(实测复现)
10.1 historique 子目录 777
现象(4.3/4.4 实测):运行期新建的备份目录全部 drwxrwxrwx。
根因:源码写死,与 umask 无关:
// index.php:175-176(新备份目录不存在时)
mkdir($complete_dir_s);
chmod($complete_dir_s,0777);
对照实验证明是显式 chmod 而非 umask 问题:
php -r 'umask(0); var_dump(mkdir("/tmp/mktest", 0777));' && stat -c '%a %n' /tmp/mktest
rm -rf /tmp/mktest; php -r 'var_dump(mkdir("/tmp/mktest", 0777));' && stat -c '%a %n' /tmp/mktest
bool(true)
777 /tmp/mktest
bool(true)
755 /tmp/mktest
(PHP mkdir 的 0777 模式受 umask 022 约束得到 755;WiKiss 随后又显式 chmod 0777,把目录钉死为全局可写。)
风险:同机任何本地用户可篡改/伪造历史备份——历史文件是误删恢复的唯一来源(4.3),备份被投毒意味着救济数据不可信。
修复与复发实测:
find /var/www/wikiss/historique -type d -perm 0777 -exec chmod 0755 {} +
find /var/www/wikiss -type d -perm /o+w
(空 = 修复完成)
curl -s -X POST 'http://wiki.example.com/' --data-urlencode 'page=PermProbe' \
--data-urlencode 'content=probe' --data-urlencode 'sc=S3cur3Pass!2' -o /dev/null -w 'SAVE:%{http_code}\n'
stat -c '%a %n' /var/www/wikiss/historique/PermProbe
SAVE:302
777 /var/www/wikiss/historique/PermProbe
新建页面的下一次保存必复发(源码路径),所以处置方案是定时巡检收敛(注意事项 3),或按安装篇思路把 176 行的 0777 改成 0755(改源码属可选加固)。
10.2 _config.php 权限故障被 opcache 掩盖 + 静默降级裸奔
复现:误把配置改为 root 私有(如用 cp 覆盖后改权限):
chown root:root /var/www/wikiss/_config.php; chmod 600 /var/www/wikiss/_config.php
curl -s -o /dev/null -w 'WITH-OPCACHE:%{http_code} bytes:%{size_download}\n' http://wiki.example.com/
WITH-OPCACHE:200 bytes:4743
站点看起来完全正常——OPcache 按 mtime 判定缓存有效,chmod 不改 mtime(6.2),www-data 读不到文件但缓存照供。故障在下次重启后集中爆发:
apache2ctl stop; sleep 1; apache2ctl start
curl -s -o /tmp/g4c.txt -w 'AFTER-RESTART:%{http_code} bytes:%{size_download}\n' http://wiki.example.com/
grep -i 'config' /var/log/apache2/wikiss-error.log | head -2
grep -o '<title>[^<]*</title>' /tmp/g4c.txt
AFTER-RESTART:200 bytes:1600
[Mon Nov 02 18:24:00.030312 2026] [php:warn] [pid 9961] [client 127.0.0.1:44354] PHP Warning: include(/var/www/wikiss/_config.php): Failed to open stream: Permission denied in /var/www/wikiss/index.php on line 13
[Mon Nov 02 18:24:00.030893 2026] [php:warn] [pid 9961] [client 127.0.0.1:44354] PHP Warning: include(): Failed opening '/var/www/wikiss/_config.php' for inclusion (include_path='.:/usr/share/php') in /var/www/wikiss/index.php on line 13
<title> - </title>
页面缩水到 1600 字节、标题为空($WIKI_TITLE 未定义)。更严重的是鉴权静默失效:$PASSWORD 未定义时源码条件 ($PASSWORD == "") 按 PHP 松散比较成立 → 匿名编辑被打开:
curl -s -X POST 'http://wiki.example.com/' --data-urlencode 'page=SecProbe' \
--data-urlencode 'content=anon-write-when-config-unreadable' --data-urlencode 'sc=' \
-o /dev/null -w 'NO-PASS-POST:%{http_code} -> %{redirect_url}\n'
find /var/www/wikiss -maxdepth 2 -name 'SecProbe.txt'
cat /var/www/wikiss/SecProbe.txt
NO-PASS-POST:302 -> http://wiki.example.com/?page=SecProbe
/var/www/wikiss/SecProbe.txt
anon-write-when-config-unreadable
双重异常都实测到手:无口令保存成功(302 直达页面视图,而非 error=1),且因 $PAGES_DIR 未定义,文件落到了站点根目录而非 pages/(4.5 游离文件巡检项的来源)。
修复:
rm -f /var/www/wikiss/SecProbe.txt
chown www-data:www-data /var/www/wikiss/_config.php; chmod 755 /var/www/wikiss/_config.php
apache2ctl stop; sleep 1; apache2ctl start
curl -s -o /dev/null -w 'RESTORED:%{http_code}\n' http://wiki.example.com/
RESTORED:200
恢复后同一无口令请求被正确拒绝(实测回显 RESTORED-POST:302 -> http://wiki.example.com/?page=SecProbe&action=edit&error=1)。排障口径:权限类改动后必须 apache2ctl graceful 再验证;<title> 变空、grep -i 'include.*Permission denied' error.log、根目录游离 *.txt 三个信号联合指认"配置不可读"。
10.3 pages 目录属主错乱:新建失败、编辑无感
复现(对照 7.3 的"目录 vs 文件"要点):
chown root:root /var/www/wikiss/pages; chmod 755 /var/www/wikiss/pages
curl -s -X POST 'http://wiki.example.com/' --data-urlencode 'page=ProbeNew' \
--data-urlencode 'content=boom' --data-urlencode 'sc=S3cur3Pass!2' \
-o /tmp/resp.txt -w 'SAVE-FAIL:%{http_code}\n'
cat /tmp/resp.txt
ls /var/www/wikiss/pages/ | grep -c ProbeNew
SAVE-FAIL:200
Could not write page!
0
新建页面失败:fopen 建不了新文件,PHP die("Could not write page!") 直接输出(HTTP 200 + 纯文本,不是 500,前端表单看起来"保存了"实则没存——与安装篇第四章的权限症状同源)。而此时对既有页面(如 PermProbe.txt,www-data 属主)的保存实测仍返回 302 成功——覆盖已有文件不经过目录写权限。
修复:
chown www-data:www-data /var/www/wikiss/pages
curl -s -X POST 'http://wiki.example.com/' --data-urlencode 'page=ProbeNew' \
--data-urlencode 'content=ok' --data-urlencode 'sc=S3cur3Pass!2' \
-o /dev/null -w 'SAVE-OK:%{http_code}\n'
ls -la /var/www/wikiss/pages/ProbeNew.txt
SAVE-OK:302
-rw-r--r-- 1 www-data www-data 2 Nov 2 18:23 /var/www/wikiss/pages/ProbeNew.txt
排障口径:权限体检不能只测"编辑已有页面",必须新建一个临时页再删掉,才能暴露目录属主问题。
10.4 补丁转写笔误:正则缺闭合分隔符 → 表格静默变空
现象:打满补丁的环境里,含表格页面渲染出空表格:
<table class="wikitable"></table>
error.log 同步出现运行期告警(6.4 尾节回显):
PHP Warning: preg_replace_callback(): No ending delimiter '/' found in /var/www/wikiss/plugins/wkp_Tables.php on line 49
定位:php -l 全绿(6.1 实测),故障在运行期。查看 49 行现场:
sed -n '48p' /var/www/wikiss/plugins/wkp_Tables.php
$s = preg_replace_callback('/\|(([hlrtb]* ){0,1})\s*(\d*)\s*,{0,1}(\d*)\s*(.*?)\|',
正则串以 \|' 收尾——闭合分隔符 / 丢了。安装篇一键补丁脚本的 FIX8 替换串按原码 '/\|(([hlrtb]* ){0,1})\s*(\d*)\s*,{0,1}(\d*)\s*(.*?)\|/e' 去掉 /e 修饰符时把闭合 / 一并摘掉,属转写笔误。preg_replace_callback 对非法模式返回 NULL,单元格内容整体蒸发——症状与未打 FIX8 的 /e 故障(空表格、无致命错)几乎一致,极易误判。
修复:补回闭合分隔符(# 作 sed 分隔符避免与替换串中的 / 冲突):
sed -i "48s#\\\\[|]',#\\\\|/',#" /var/www/wikiss/plugins/wkp_Tables.php
php -l /var/www/wikiss/plugins/wkp_Tables.php
apache2ctl graceful; sleep 1
curl -s 'http://wiki.example.com/?page=TestS9' | grep -o '<table class="wikitable">.*</table>'
No syntax errors detected in /var/www/wikiss/plugins/wkp_Tables.php
<table class="wikitable"><tr><td >A </td><td >B </td></tr><tr><td colspan="1" ></td><td colspan="2" ></td></tr><tr><td colspan="3" ></td><td colspan="4" ></td></tr></table>
表格结构恢复。回显里数字单元格 | 1 | 被解析成 colspan="1" 空单元格,是 WiKiss 表格语法本身的数字前缀语义(单元格内容以数字开头会被当作合并单元格声明),属语法篇范畴,见 《WiKiss 0.3 页面语法与常用操作详解》。
教训:打完补丁的验收不能只做 php -l,必须把浏览/搜索/保存/表格渲染各实测一遍并 tail error.log(6.1 ⚠️ 的落点)。
10.5 中文文件名:tar 清单转义与全链路结论
现象与修复实测于 5.2:tar tzf 默认八进制转义中文名,--quoting-style=literal 按原样显示;数据层(解包、grep、备份恢复)不受影响。
全链路结论(本文实测覆盖):中文页面名从创建(3.2 CN:302)、落盘(pages/运维手册.txt)、检索(5.1)、备份(5.2 清单)、灾难恢复(5.3 回灌后 grep -c 命中 1)、Web 访问(URL 百分号编码 200)全部正常。文件系统按 UTF-8 字节存名,WiKiss 源码对页面名只做 /、\ 剥离(无字符集白名单),所以中文可用;但页面名中的空格、?、& 等会进入 URL 查询串,建议命名避开(页面模型细节见 《WiKiss 0.3 核心使用与鉴权配置详解》)。
十一、参考链接
- WiKiss 官网:http://wikiss.tuxfamily.org/
- 官方发布包:http://download.tuxfamily.org/wikiss/0.x/0.3.tgz(本次实测官方源直连可用,25,159 字节,未走代理/国内源降级)
- 站内同专栏:《Ubuntu 22.04 安装 WiKiss 0.3 指南(PHP 8.1 兼容修复实录)》、《WiKiss 0.3 核心使用与鉴权配置详解》、《WiKiss 0.3 页面语法与常用操作详解》、《WiKiss 0.3 配置参数详解》