テキストを置換したい(sd)
$ sd '検索パターン' '置換パターン' ファイル名
$ コマンド | sd '検索パターン' '置換パターン'
sdは、ファイルの中身をテキスト置換できるコマンドです。
sedのRust代替コマンドで、引数が直感的に指定できるようになっています。
インストールしたい(sd)
$ brew install sd
$ sd --version
sd 1.0.0
sdはHomebrewでインストールできます。
すべて置換したい
$ echo "aaa" | sd 'a' 'b'
bbb
sdはデフォルトで、1行の中でマッチしたすべての箇所を置換します。
sedと違ってgフラグは不要です。
部分置換したい(sd --max-replacements)
$ echo "aaa" | sd --max-replacements 1 'a' 'b'
baa
$ echo "aaa" | sd -n 1 'a' 'b'
--max-replacements(-n)オプションで、置換する回数の上限を指定できます。
-n 1にすると、最初にマッチした箇所だけを置換します。
置換内容を事前確認したい(sd --preview)
$ sd --preview '検索パターン' '置換パターン' ファイル名
$ sd -p '検索パターン' '置換パターン' ファイル名
--preview(-p)オプションで、置換結果を標準出力に表示できます。
sdは、sedと異なり、デフォルトで末尾に渡したファイル名を上書きします。
--previewオプションで、変更内容を確認しておくと安全です。
$ cat ファイル名 | sd '検索パターン' '置換パターン' > 出力ファイル名
より安全に置換したい場合は、上記のようにパイプで標準出力に出力して、別のファイルに書き込む方法もあります。
文字列をそのまま置換したい(sd --fixed-strings)
$ echo "a.b.c" | sd '.' 'X'
XXXXX
$ echo "a.b.c" | sd --fixed-strings '.' 'X'
aXbXc
$ echo "a.b.c" | sd -F '.' 'X'
aXbXc
--fixed-strings(-F)オプションで、検索パターンと置換パターンをリテラル文字列として扱えます。
sdの検索パターンはデフォルトで正規表現として扱われます。
.のような特殊文字は「任意の1文字」にマッチしてしまうため、--fixed-stringsで見たままの文字列であることを指示する必要があります。
大文字小文字を区別せず置換したい(sd --flags i)
$ echo "Hello World" | sd -f i 'hello' 'HI'
HI World
--flags(-f)オプションで、正規表現の挙動を変更できます。
iは大文字小文字を区別しない(case-insensitive)フラグです。
複数のフラグを-f mcのようにまとめて指定することもできます。
フラグ |
意味 |
|---|---|
|
大文字小文字を区別する(デフォルト) |
|
大文字小文字を区別しない |
|
複数行モードで |
|
複数行モードを無効にする(デフォルト) |
|
|
|
単語全体のみにマッチさせる |
複数行を置換したい(sd --across)
$ printf 'foo\nbar\n' | sd 'foo\nbar' 'X'
foo
bar
$ printf 'foo\nbar\n' | sd --across 'foo\nbar' 'X'
X
--across(-A)オプションで、改行をまたいだパターンにもマッチできます。
sdはデフォルトで入力を1行ずつ処理するため、foo\nbarのように改行をまたぐパターンは通常マッチしません。
--acrossで、入力全体を1つのまとまりとして扱うことで対応できます。
連続するログ行のまとめ直しや、改行を含むタグの置換など複数行にまたがるブロック を扱うときに使います。
注釈
入力全体を一度にメモリへ読み込むため、大きなファイルの処理では注意が必要です。
後方参照を使いたい($1)
$ echo "2024-01-15" | sd '(\d+)-(\d+)-(\d+)' '$3/$2/$1'
15/01/2024
()で囲んだ部分は、マッチした文字列を$1・$2・$3のように置換パターン側で再利用できます。
sedの\1と違い、バックスラッシュなしの$1という書き方です。
ログをLTSV形式に置換したい
1$ head -n 100 ssl_access_log | \
2 sd '^^(\\S+) (\\S+) (\\S+) \\[([\\w:/]+\\s[+\\-]\\d{4})\\] "(\\S+)\\s?(\\S+)?\\s?(\\S+)?" (\\d{3}|-) (\\d+|-)\\s?"?([^"]*)"?\\s?"?([^"]*)?"?' \
3 'remote_host: $1, remote_logname: $2, remote_user: $3, request_time: $4, request_method: $5, request_url: $6, request_http_version: $7, status: $8, byte_sent: $9, referer: $10, user_agent: $11'
Apacheのcombined形式のログをLTSV(Labeled Tab-Separated Values)形式に変換しています。 10万行くらいの処理なら一瞬でした。 100万行を越えたら一瞬・・・とは言えなかったですが、自作のPythonスクリプトを回すより遥かに速かったです。 ・・・もう、これを使ったシェルスクリプトを書いた方がよさそう・・・。
1# あとでためす
2$ gzip -d ファイル名.gz | sd '検索パターン' 'LTSV形式' > 出力ファイル名