12k
All articles

面向终端用户的 WP-CLI 指南

面向WordPress迁移、备份、登录受限、插件和核心更新、远程SSH任务及序列化数据安全替换的WP-CLI命令指南。

OpenReplay Team
OpenReplay Team
面向终端用户的 WP-CLI 指南

WP-CLI 是 WordPress 安装的命令行界面。它能省去大量点击操作,但真正值得学习的原因在于:有一类工作在后台面板里根本没有对应的界面——比如在更换域名时重写序列化的 option 数据、针对线上站点执行任意 PHP 代码、或者在一个命令提示符里更新十个安装实例。

大多数人是在紧急情况下第一次接触它的:某次插件更新把后台搞崩了,dashboard 打不开,FTP 加 phpMyAdmin 突然成了唯一的回路。这条路确实可行,但速度慢,而且需要一点胆量。

本文按任务组织,而非按命名空间组织。每一节都是一个在 dashboard 中痛苦或无法完成的任务,随后给出完成它的命令,以及让操作更安全的参数。

核心要点

  • wp search-replace 会先反序列化 PHP 数据,执行替换,然后重新序列化。正因如此,它能够重写那些用原生 SQL REPLACE() 会损坏的小工具设置和插件 option。
  • 每次替换都先用 --dry-run 跑一遍,然后再执行去掉该参数的同一条命令。
  • 用 --skip-columns=guid 排除 guid 列,因为订阅阅读器依靠文章的 guid 来判断是否已经展示过该条内容。
  • --ssh=[<scheme>:][<user>@]<host>[:<port>][<path>] 会把命令代理到远程安装实例上执行,且远程机器需要自备一份能通过 wp 调用的 WP-CLI。
  • 这些命令都不会提示确认,也都无法撤销,所以第一步永远是 wp db export。

这些命令会立即执行

没有确认对话框,没有预览界面,也没有撤销。你一按回车,wp search-replace 就会写入每一个匹配的行。wp plugin deactivate --all 在生产站点上停用一切插件,和在笔记本上一样毫不犹豫。你唯一的回滚手段就是事先导出的数据库,所以务必先导出一份。

如何在不破坏序列化数据的前提下更换域名?

wp search-replace 是更换域名的正确工具:它能正确读取序列化的 PHP 数据,并且不会动主键——这两点普通的 SQL 查找替换都做不到。原因在于存储格式:PHP 的 serialize() 会先记录字符串的字节长度,再记录字符串本身。

a:1:{s:3:"url";s:27:"https://staging.example.com";}

一条盲目的 SQL UPDATE ... REPLACE() 会把 URL 改写成 https://example.com,却让那个 27 保持原样。声明的长度与实际内容不再匹配,PHP 无法再反序列化这个值,于是存放在其中的小工具或插件 option 就悄无声息地变成了空值。而 WP-CLI 会反序列化该结构,在其内部执行替换,再用正确的长度重新序列化。

分三步逐级推进:

# 1. report what would change; writes nothing
wp search-replace 'https://staging.example.com' 'https://example.com' \
  --skip-columns=guid --dry-run

# 2. optional: write the result to a SQL file instead of the database
wp search-replace 'https://staging.example.com' 'https://example.com' \
  --skip-columns=guid --export=migration.sql

# 3. apply it
wp search-replace 'https://staging.example.com' 'https://example.com' \
  --skip-columns=guid

--dry-run 会完整执行整个任务并打印报告,然后丢弃所有改动。--export 把结果写入一个 SQL 文件,线上数据库保持不变,这样你可以先阅读差异,或者把它应用到别处。要跳过 guid 列,因为 WordPress 把文章的 guid 视为终身不变:一旦改动,订阅阅读器可能会把你的全部历史内容当作新文章重新推送一遍。

对于结构别扭的嵌套数据,可以加上 --precise。默认情况下该命令使用快速的 SQL 查询,并对包含序列化数据的列自动切换到 PHP 处理;--precise 则强制所有列都走 PHP,速度更慢,但面对复杂的序列化结构更可靠。正则模式同样明显更慢,所以只在字面字符串无法胜任时才动用它。

如何为多个站点更新插件和核心?

一条命令就能更新所有有可用更新的项目,无需翻 dashboard 的分页,也不用逐个插件勾选复选框:

wp plugin update --all
wp core update
wp core update-db

wp core update-db 执行 WordPress 的数据库更新例程,也就是核心更新后 dashboard 在升级页面上替你完成的那一步。在 wp core update 之后执行它,升级才算真正完成,而不是半途而废。

结合下文介绍的别名机制,同一行命令就变成了 wp @all plugin update --all,会依次作用于你维护的每一个安装实例。

事前导出,事后导入

wp db export 会调用 mysqldump,并从 wp-config.php 中读取数据库主机、名称、用户和密码,所以你永远不用手动输入连接信息。请显式指定文件名;省略的话它会写成 {dbname}-{Y-m-d}-{random-hash}.sql。

wp db export backup-$(date +%Y%m%d-%H%M%S).sql

恢复则是镜像操作:

wp db import backup-20250413-141055.sql

wp db import 既接受文件名,也接受管道输入,因此你可以通过 ssh 把一份导出直接从一台主机传到另一台。如果你需要的不只是在冒险操作前做一次单独的转储,而是更长期的策略,OpenReplay 的 WordPress 备份系列文章涵盖了定时计划与异地存储。

被锁在站点外时如何重新进入?

三条命令几乎覆盖了所有被锁定的场景,顺序就是你在压力之下会执行的顺序:创建一个全新的管理员、重置某个已有用户的密码,或者干脆把插件从等式中剔除:

wp user create ops ops@example.com --role=administrator
wp user reset-password admin --show-password --skip-email
wp plugin deactivate --all

wp user reset-password 会生成一个新密码;--show-password 把它打印到终端,--skip-email 则阻止通知邮件发往一个你可能无法控制的邮箱。wp plugin deactivate 接受 --all 来停用一切插件,还可以用 --exclude=<name> 以逗号分隔的列表保留若干插件处于启用状态。

当搞坏后台的正是某个插件中的致命错误时,WP-CLI 可能会因为和站点相同的原因而无法完成引导(bootstrap)。--skip-plugins 全局参数可以在该命令执行期间阻止全部插件(或指定列表中的插件)加载:

wp plugin deactivate broken-plugin --skip-plugins

跳过加载并不会改变存储的状态;以这种方式被跳过的插件仍然会被报告为已启用。它只是为你换来一个可用的引导过程,好让停用操作能够执行。此外,当致命代码位于 mu-plugin 中时它也无济于事,因为无论如何 WP-CLI 都会加载 mu-plugins。这正是大多数维护者第一次真正需要 WP-CLI 的时刻,而且它比打开 FTP 客户端、逐个重命名插件目录要快得多。后台恢复之后,这项工作的诊断部分可以参考 OpenReplay 关于 WordPress 白屏死机的文章。

用 wp eval 执行一次性 PHP 代码

wp eval 针对一个已完全加载的 WordPress 安装执行任意 PHP 代码。dashboard 中没有任何对应功能,而这恰恰是重点:插件注册的任何函数、任何 option、任何查询,都能变成一行命令。

wp eval 'echo get_option( "siteurl" );'
wp eval 'echo count( get_users( [ "role" => "administrator" ] ) );'

再长一点的代码就应该放进文件里。wp eval-file 接受一个 PHP 文件路径,并把额外的位置参数以 $args 的形式传给脚本;如果你传入 --skip-wordpress,它会完全跳过 WordPress 的引导过程。你的代码运行在一个方法内部,因此每个用到的全局变量都需要各自的 global 声明。

wp eval 没有 dry run。脚本写了什么,就是真的写了什么。这是导出备份最有力的理由。

如何针对远程主机运行 WP-CLI?

这才是这个工具从”便利”升级为”必需”的地方。WP-CLI 的 --ssh 全局参数 的形式是 --ssh=[<scheme>:][<user>@]<host>[:<port>][<path>],其工作方式是把你的命令交给 ssh 二进制程序,由它再转交给远端的 WP-CLI。

wp --ssh=dev_user@example.com:2222~/webapps/production plugin list
组成部分此处的值省略时的默认值
scheme(省略)ssh
userdev_user当前系统用户
hostexample.com必填
port222222
path~/webapps/productionssh 用户的家目录

path 前面不加任何分隔符。直接写在端口之后,或者在省略端口时直接写在主机之后,并以 / 或 ~ 开头。除了 ssh,手册的配置参考中还记录了 vagrant、docker、docker-compose 和 docker-compose-run。最后那个会用 docker-compose run 启动一个全新的容器,而不是使用已经运行中的容器。

有一个前提条件是绝对的:远程服务器需要自备 WP-CLI,而且必须能通过 wp 调用到。一个在你手动登录时能正常工作的 wp,在通过 --ssh 调用时仍然可能返回 command-not-found,因为执行远程命令的 shell 构建的 $PATH 并不相同。大多数发行版会在 ~/.bashrc 靠前的位置放一段守卫代码,在 shell 非交互式时提前退出,于是它下面的任何 PATH 设置都不会执行;在这种情况下,zsh 读取的是 ~/.zshenv 而不是 ~/.zshrc。解决办法是在远程一侧显式设置 $PATH。

那串字符输入两遍就已经够了。可以在项目的 wp-cli.yml 或全局的 ~/.wp-cli/config.yml 中注册别名:

@prod:
  ssh: deploy@example.com~/webapps/production
@stage:
  ssh: deploy@staging.example.com~/webapps/staging
@all:
  - @prod
  - @stage
wp @prod plugin update --all
wp @all core check-update

一个别名组能让一次调用作用于多个安装实例——这就是维护十个客户站点与登录十个 dashboard 之间的差别。对于不在当前目录下的本地安装,--path 全局参数用于告知 WP-CLI WordPress 文件的位置:

wp --path=/var/www/example.com/htdocs plugin update --all

下一步去哪里

从本文中最值得带走的一个观念是:WP-CLI 理解 WordPress 的数据结构,而 mysql 和 phpMyAdmin 不理解——这正是域名更换只应交给 wp search-replace、而不应交给任何其他工具的原因。挑出你下一次已排期的迁移任务,写好那条 --dry-run 命令,读一遍报告,然后在去掉该参数之前先执行 wp db export。上面所有操作,一旦按下回车便不可逆转。

常见问题

wp search-replace 会更新多站点网络中的每一个站点吗?

不会。它作用于 WordPress 自身注册的表,因此在多站点环境下你只会处理到当前站点的表,除非你加上 --network。若要覆盖数据库中的每一张表(无论其前缀为何,也无论 WordPress 是否知晓它),请使用 --all-tables,它的优先级高于 --network 和 --all-tables-with-prefix。在网络环境中,还要加上 --url,以便 WP-CLI 引导进入正确的站点。

为什么 WP-CLI 拒绝以 root 身份运行?

当 WP-CLI 检测到 root 用户时,会以一个 YIKES 错误中止。安装目录内的一切(包括并非你编写的插件和主题)都会继承 root 对服务器的权限范围,因此一段恶意代码就可能拿下整台机器。--allow-root 参数会跳过这项检查,以 root 身份运行的容器往往需要它,但项目本身并不建议这样做。请改用拥有 WordPress 文件所有权的那个系统用户来运行。

错误信息 'This does not seem to be a WordPress installation' 是什么意思?

WP-CLI 在它查找的位置没有找到 WordPress 核心文件,因此从未完成引导。请在包含 wp-admin、wp-content 和 wp-includes 的目录下执行命令,或者用 --path 全局参数指向该安装目录。传值时请使用等号形式,即 --path=/var/www/html,因为用空格分隔的写法会让该参数没有取到值,于是同样的错误会再次出现。

WP-CLI 能在 Windows 上运行吗?

WP-CLI 是为类 UNIX 环境构建的,例如 Linux、macOS、FreeBSD 或 Cygwin,而在 Windows 本身上仅获得部分支持,因此在 Windows 机器上,WSL 或 Cygwin 才是可靠的途径。它还需要 WordPress 4.9 或更高版本,低于当前 WordPress 发布版本的环境可能无法完整运行。

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.