Let’s batch-edit WordPress posts using the command line.
相模原市で IoT 設計を受託しているファームロジックスです。前回に続いて WordPress を取り上げます。
WordPress でウェブサイトを運用しているソフトウェア技術者の方にとって、ちょっとした記事の修正のために、いちいちウェブインターフェイスを開くのは面倒に感じませんか? 手元の PC のテキストエディタから、さっと記事を修正できたら便利ですよね。
SQL に通じている方でしたら、WordPress のデータベースに接続して、SQL で記事を検索したり書き換えたり、ということもできるのでしょうが、データベースの専門知識がないと、ちょっとハードルが高く感じます。また、SQL のコマンドを間違えてデータベースをごっそり壊してしまう危険性もあります。
実は今回、WordPress で運営しているサイトの大幅なアップデート作業をしたのですが、MariaDB と DBeaver + SQL を駆使しながらデータベースの移行作業をしながら、もう少しうまい方法はないだろうか、とずっと考えていました。もちろん直接データベースにアクセスするには利点もあり、WordPress のデータベースがどのような構造になっているか理解するには非常に参考になります。
しかし、ウェブインターフェイスと、SQL 接続の間で、もう少し安全な操作でありながら、コマンドラインで WordPress を操作する方法はないものでしょうか。
WP CLI を使おう
実は以前にも少しだけ試したことがあったのですが、WP CLI の存在をすっかり忘れてました。
WP CLI(コマンドラインインターフェイス)とは、WordPress のシステム管理や、固定ページや投稿記事の管理を、コマンドラインで処理することのできる便利なツールです。
しかしながら、WP CLI のマニュアルはやや(非常に?)分かりづらく、実際には実装されているのにマニュアルに出てこない、あるいは明確な説明のない機能などが多くあります。余談ですが、ネットでいくら検索しても、私のやりたい操作方法が見つからず、件の ChatGPT 先生に聞いたら一発で解決してしまい、驚きました。ChatGPT 先生、そんな知識、どっから見つけてきたんですか!?
閑話休題。いくつか典型的な操作について、備忘録としてまとめさせて頂きました。
記事の検索
いま、WordPress の固定ページ(page)、投稿(post)から「ChatGPT」が含まれる記事を検索したいとしましょう。このためには、次のようなコマンドを実行します。
$ wp post list --s="ChatGPT"
当たり前のコマンドだと思うんですけど、こちらのマニュアルには説明されていないんですよね。
追記: こちらの、
WP_Query()
という関数の引数は、概ね指定できるようです。--s
も、そこで説明されています。
ちなみに、複数のキーワードで AND 検索をするには、間にスペースを入れると良いようです。なお、この検索は case insensitive のようなので、小文字で書いても大丈夫です。
$ wp post list --s="chatgpt python"
また、一度に表示される記事数が多すぎる場合は、表示数を減らせます。
$ wp post list --s="chatgpt python" --posts_per_page=10
ところで、ここで記事の投稿日を使って検索範囲を狭められると良いですよね。Google で検索したり、ChatGPT に聞いたりしてみたのですが、うまい方法は見つけられませんでした。唯一分かったのは、直接 SQL を渡して出力を得る方法です。
$ wp db query 'SELECT ID, post_date, post_title FROM `wp_posts` WHERE `post_date` < "2015-01-01";'
これだけだと結果を加工しづらいのですが、こちらのサイトに、こんな例が出てました。(少し修正してあります。)
$ wp post list --post__in=$( wp db query 'SELECT ID FROM `wp_posts` WHERE `post_date` < "2015-01-01";' --skip-column-names | paste -s -d ',' - )
ここまで来ると手軽さが失われてますが、何か困ったときには参考になりそうです。
追記: 上で追記した
WP_Query()
を使うことで、次のような検索ができることは分かりました。
$ wp post list --s="chatgpt python" --year=2022 --monthnum=12 --posts_per_page=10
日付範囲を指定するには
WP_Query()
で array を渡さなくてはいけないのですが、WP CLI で引数に array を渡すのは(現状では)難しいようです。
テキストエディタで記事を編集
次に、コマンドラインで記事を編集する方法です。いま、wp post list --s
などで記事番号が 123 番だと分かったとしましょう。
次のようにして、エディタで編集できます。
$ wp post edit 123
環境変数 EDITOR
を見ているようなので、Emacs 派の人は Emacs で編集できます。
追記:
wp post edit
コマンドでも、post_status が publish でない revision 記事の編集ができてしまうことが分かりました。番号を指定するときは、それが publish の記事かどうか十分に確認するようにしてください。
ところで、後で SSH を使ったリモート実行について説明しますが、ここで問題が生じます。リモート実行で wp
コマンドを使った場合でも、エディタはリモート先で実行される、という点です。
具体的に説明します。いま、myhost というローカルホストから、wphost という WordPress ホストにリモートアクセスして記事を編集するとしましょう。しかし、ローカルの myhost で wp
コマンドを実行しても、エディタはリモートの wphost 上で起動してしまうのです。まあそんなもんかな、という気はしますが、できたら、一度ローカルにファイルをコピーしてから編集し、リモートに書き戻す、くらいの機能があったら嬉しいですよね。
実際、そんなことを GitHub の Issues で投稿している人があります。この記事です。
このアプローチは面白いです。ただし、wp post update
を使うときは注意が必要です。記事番号を間違えて指定してしまうと、関係ない投稿を壊してしまうことがあります。(はい。私は実験中に、やってしまいました。)
特に、記事番号が実際に公開されている記事ではなく、ある記事のあるリビジョンだったりしても、エラーになることはなく、無関係の記事のリビジョンが書き換えられてしまうことになります。そうなると、SQL の知識、あるいは DBeaver などのツールがないとお手上げになります。
一方、wp post edit
の場合は、そういうことはなく、既存の記事の編集後には、新たなリビジョンが割り当てられ、編集前の記事もちゃんと残ります。
ちょっと危険そうな search-replace について
今まで読んできてくださった方は、
「バッチ編集しよう」とか言っておいて、バッチ編集の話がないじゃないか。
思われたかも知れません。今までに説明した内容を組み合わせれば(特に、wp post get
と wp post update
)を使えば、バッチ処理っぽいことはできると思います。
ここではもう一つ、search-replace
というアブナい wp
コマンドを簡単に紹介しておきます。説明は、こちらにあります。例がいくつか載っているので御参考頂きたいのですが、稼働中のデータベースに対して、いきなり実行するのはやめましょう。必ず、データベースのバックアップを取って、さらに --dry-run
でテストしてから実行してください。
このコマンドは、WordPress のデータベースに対してグローバルな置換をしようとします。テーブル名やカラム名を厳密に指定しないと、とんでもないことが起きるかも知れません。
私個人としては、ここまでするなら、SQL と Python などを活用して、置換条件を厳密にチェックしながら処理をするプログラムを書いたほうが良いような気がします。試しに search-replace
で --export
をしてみたのですが、いきなりテーブル全体を DROP
して INSERT
するような SQL が出てきて、かなりビビりました。
最後に SSH を使ったリモート実行について
先ほど触れたように、WP CLI にはリモート実行の機能があります。つまり、いちいちリモートの WordPress サイトにログインしなくても、遠隔から WP CLI を実行できるのです。
そのためにまず、次のコマンドが実行できることを確認しておきます。
$ ssh <user>@<host> wp --info
これがエラーになる場合には、おそらく、SSH でリモート先の wp
コマンドが PATH に入っていないのが原因です。そのために ~/.bashrc
で PATH を追記するのが定石ですが、Debian などだと、.bashrc
の末尾に export PATH=
などと書いてもうまくいきません。詳しくはここに議論がありますが、.bashrc
の末尾でなく先頭に export PATH=
を書くのが簡単な対処でしょう。
次にローカル側です。当然ながら、ローカル側にも WP CLI のインストールが必要です。つまり、ローカル側にも PHP をインストールする必要があります。
実際に実行するときは、--ssh
というオプションを使います。こんな感じです。ローカルホストは wphost、ユーザー名は yokoyama としましょう。
$ wp --ssh=yokoyama@wphost --info
--ssh
の後ろは =
です。スペースではダメですよ。(私はずっと、はまってた。)
しかし毎回指定するのは面倒ですので、
~/.wp-cli/config.yml
というファイルを作って、中に ssh: yokoyama@wphost
と書いておきましょう。(今度は、イコールでなくコロンです。)
今日はここまで
とりあえず今日はここまでとさせてください。また、WP CLI の便利な使い方が見つかったら、追記させて頂きます。