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 呼び出しで処理 |
Note
sed 呼び出し回数の改善
ナイーブに実装すると,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の範囲指定がポイントで,本文中に同じ文字列があっても置換されません.
注意点
Appendix: .bakの削除コマンド
find -delete
find . -type f -name '*.bak' -delete-exec rm
find . -type f -name '*.bak' -exec rm {} +