ここで sed は GNU sed を指す. BSD sed (Mac で標準に入っている sed がこれだ) のことは知らない. 同じものが動くといいですね.
s
コマンド以外を覚えたければ読んでおくべきチュートリアル実装を読むことにする. GNU sed の execute.c
(空文字列)
(空文字列)
line
に残った文字列を出力する
-n
オプションで抑制できるコマンド | 機能 | default_output を抑制 | 例 | 補足 |
---|---|---|---|---|
a |
続く文字列を line の後ろに付け足す |
ahoge |
a に続く文字列は次の改行文字または末尾までを読み取る. 付け足すときは改行文字を挟む. |
|
b |
jump_index までジャンプする |
bx |
jump_index は : で作ったラベルを指定する |
|
c |
line と続く文字列で置き換えて, プログラムループを抜ける |
choge |
c に続く文字列は a 同様に読む. 実行直後に新しい cycle が始まる |
|
d |
即座にプログラムループを抜ける | ✔️ | d |
line を削除して次の cycle を始めるような挙動 |
D |
line から一行目(最初に見つかる改行文字まで)を削除してプログラムループを最初からやり直す |
D |
コメントでは「次の行を読まずに新しい cycle を始める」とある | |
e |
line の内容で popen する |
echo 'echo @' | sed 's/@/HELLO WORLD/; e' |
eval の e だろうか? | |
g |
hold の内容で line を上書きする |
g |
||
G |
hold の内容を line の末尾に付け足す |
G |
改行文字を挟む | |
h |
line の内容で hold を上書きする |
h |
||
H |
line の内容で hold の末尾に付け足す |
H |
||
i |
続く文字列を line の前に付け足す |
ihoge |
改行文字を挟む | |
n |
次の行を入力から読んで, line を上書き |
|||
N |
次の行を読んで line の末尾に付け足す |
改行文字を挟む | ||
p |
現在の line を出力 |
|||
P |
現在の line の内, 一行目を出力 |
|||
q |
即座に全ての cycle を終える | |||
Q |
即座に全ての cycle を終える | ✔️ | ||
r |
テキストファイルを開いて中身を出力 | rTEXT.txt |
||
R |
テキストファイルを開いて一行だけ読んで出力 | RTEXT.txt |
どこまで読んだか記憶して続きの一行を読む | |
s |
line に対する文字列置換を行う |
s/[0-9]*/NUMS/g |
||
t |
直前の置換に成功したら jump_index までジャンプ |
:loop s/x//; tloop |
||
T |
直前の置換に失敗したら jump_index までジャンプ |
|||
w |
ファイル名を指定して, line をこれに出力 |
wOUT.txt |
||
W |
ファイル名を指定して, line の一行目をこれに出力 |
WOUT.txt |
||
x |
line と hold を交換 |
|||
y |
translate による置換をする | y/123/abc/ |
一対一の変換対応を与える | |
z |
line を空にする |
|||
= |
行番号を出力する | |||
F |
ファイル名を出力する |
sed の例文を集める. ほとんどは自分で思いついたものではないので, 他人が書いたコードを勝手に解説してることになる.
tac
コマンドは行単位で逆順に出力する
seq 5
1
2
3
4
5
seq 5 | tac
5
4
3
2
1
これを行う sed スクリプトは [sed1line.txt] で紹介されている. このスクリプトの理解はパターンスペースとホールドスペースを理解するのに役立ちそう.
簡略化すると次のようなスクリプトで tac
は再現できる.
G
h
sed は実行の際に内部にパターンスペースとホールドスペースというそれぞれ文字列型のレジスタを持つ. 入力から読まれた一行はパターンスペースに保存されてスクリプトを実行する. ホールドスペースは初めはただ空文字列が入っている. G
は ホールドスペースの文字列をパターンスペースの末尾に改行を入れてから追加する. ( g
なら追加ではなく上書きになる.) 逆に h
はパターンスペースの文字列でホールドスペースを上書きする. ( H
なら上書きじゃなくて改行を入れてから追加をする.) これを繰り返すと,
Command Pattern Hold
--------------- --------- -------
(next cycle) 1 ""
G 1\n ""
h 1\n 1\n
(next cycle) 2 1\n
G 2\n1\n 1\n
h 2\n1\n 2\n1\n
となってホールドスペースに tac の結果が保存されていることが分かる.
結果をきれいに出力するための処理として, 一番最初のホールドスペースは G
してもしょうがないのでさせないのと, 出力は一番最後だけすればいい (デフォルトではサイクルの最後にパターンスペースにあるものを出力する) ので d
で出力せずに次のサイクルに移すような処理を付け足す.
1!G
h
$!d
入力が英大文字のときに含まれる W*A
を AC*
に置換したい. ただしここでパターンで W
が \(n\) 個続いたとき, これを A
のあとに C
が \(n\) 個続いたような文字列にしたい. 例えば WWA
は ACC
にしたい.
地道に s/WA/AC/g
を何度も繰り返せば良い. これは
:a
s/WA/AC/g
ta
と出来る.
上のようにループを使って一手ずつやると大変遅い. 一度に変換できるならしたほうが良い.
x20 さんの方法 wupc2019/submissions/4545993 を真似る.
s/\(W*\)A/A\L\1/g
とすると, \L
はそれより後ろを lowercase で出力してくれる ( manual ) ので, WWWA
が Awww
になってくれる. 入力に他に w
という文字がないことを仮定すれば (この問題ではそうなっている), 最後に
y/w/C
とすれば ACCC
を得る.
gsed (GNU sed) の拡張を用いるとシェルスクリプトが実行できる. これは s
コマンドの e
フラグとして実装されている.
すなわち, シェルスクリプトを出力させるような s
コマンドを書いて,
echo 2 3 | sed 's/\(.*\) \(.*\)/echo $(( \1 + \2 ))/'
echo $(( 2 + 3 ))
これに e
フラグを付けると実行する.
echo 2 3 | sed 's/\(.*\) \(.*\)/echo $(( \1 + \2 ))/e'
5
中身は bash なので(?), for 文でもなんでも書ける.