find + sed でqmd frontmatterを一括置換

環境構築
shellscript
Author

Ryo Nakagami

Published

2026-05-01

Modified

2026-05-07

Goal

  • Quartoブログの全記事の frontmatter にある subtitle を一括で書き換えたい
  • 本文中に同じ文字列があっても巻き込まないように,frontmatterブロック内に限定して置換したい

コマンド

## base command
find . -type f -name 'index.qmd' \
  -exec sed -i '1,/^---$/ s|subtitle: "claude-code 101"|subtitle: "claude-code skills 101"|' {} +

## with backup
find . -type f -name 'index.qmd' -exec sed -i.bak '1,/^---$/ s|subtitle: "claude-code 101"|subtitle: "claude-code skills 101"|' {} +

置換前

---
title: "xxxxx"
subtitle: "claude-code 101"
---

本文に subtitle: "claude-code 101" と書いてあっても置換されない.

置換後

---
title: "xxxxx"
subtitle: "claude-code skills 101"
---

本文に subtitle: "claude-code 101" と書いてあっても置換されない.

コマンド解説

find 部分

部分 意味
. カレントディレクトリ以下を再帰的に探索
-type f 通常ファイルのみ(ディレクトリやシンボリックリンクは除外)
-name 'index.qmd' ファイル名が index.qmd のもの
-exec ... {} + 見つかったファイルをまとめて引数として渡し, sed 呼び出しで処理
Notesed 呼び出し回数の改善

ナイーブに実装すると,find が見つけたファイルのパスに置き換わるプレースホルダー {} を用いて

find . -name '*.qmd' -exec sed -i 's|A|B|' {} \;

とやりたくなりますが,これでは,見つかったファイル1つごとに sed プロセスが起動してしまいます.そのため,find マッチが100ファイル存在する場合, 内部的には100 回プロセスが立ち上がります.

これでは非効率なので

sed -i 's|A|B|' file1.qmd file2.qmd file3.qmd ... file100.qmd

に相当する工夫として

find . -name '*.qmd' -exec sed -i 's|A|B|' {} +

公式ドキュメントによる説明

-exec command {} +

        This  variant  of  the -exec action runs the specified command on the selected
        files, but the command line is built by appending each selected file  name  at
        the end; the total number of invocations of the command will be much less than
        the  number  of matched files.  The command line is built in much the same way
        that xargs builds its command lines.  Only one instance  of  `{}'  is  allowed
        within the command, and it must appear at the end, immediately before the `+';
        it  needs  to be escaped (with a `\') or quoted to protect it from interpreta‐
        tion by the shell.  The command is executed in the starting directory.  If any
        invocation with the `+' form returns a non-zero value  as  exit  status,  then
        find  returns  a  non-zero exit status.  If find encounters an error, this can
        sometimes cause an immediate exit, so some pending commands may not be run  at
        all.   For  this  reason -exec my-command ... {} + -quit may not result in my-
        command actually being run.  This variant of -exec always returns true.

sed 部分

部分 意味
-i in-place 編集(ファイルを直接書き換え)
1,/^---$/ アドレス範囲: 1行目から最初に --- だけの行が出るまで(= YAML frontmatterブロック)
s\|旧\|新\| 置換コマンド.区切り文字に \| を使うと置換文字列に / が含まれてもエスケープ不要

frontmatterの範囲指定がポイントで,本文中に同じ文字列があっても置換されません.

注意点

Warning使用時の注意
  • バックアップなし: -i は破壊的編集.不安な場合は GNU sed なら -i.bak でバックアップ付き編集が可能
  • --- の検出: frontmatterの終端行が前後に空白を含むと /^---$/ にマッチしない.実態に合わせて /^---\s*$/ などに調整
  • マッチしないファイル: 対象文字列が無いファイルはそのまま(エラーにならず無変更)

Appendix: .bakの削除コマンド

find -delete

find . -type f -name '*.bak' -delete

-exec rm

find . -type f -name '*.bak' -exec rm {} +