Back List


制御文字と図形文字


まずは本題ではないのだが、この項では制御文字と図形文字について、7ビットのASCIIを例にあらかじめ整理しておく。ASCIIでは、7ビットが制御文字(C0、C1グループ)と図形文字(GLグループ)とにあてがわれている。8ビットになるとこれに拡張図形文字(GRグループ)が加わる。「図形文字」とは見えるもの/表示するもの、すなわちアルファベットや数字や記号のことで、「制御文字」とはそうではないもののことをここでは言う。

-1.制御文字
制御文字は、多くのシステムで、読みとった時点で特別な処理に使われる(システムが動く)可能性のある文字として認識されている。有名なものにはラインフィード、キャリッジリターン、エンドオブテキストなどがある。

-2.エスケープ:図形文字
経由するシステムに与える影響や逆にシステムから受ける影響を考慮すれば、送り手と受け手が安心して扱えるのは、図形文字グループと、制御文字ではラインフィードくらいである。しかしその受け渡しのあとにも(その上位の層でも)、受け手に向けて図形文字以外の処理や独自に定義した命令を送りたい場合がある。このための特別な約束に現在多く使われているのが図形文字バックスラッシュ\である。図形文字バックスラッシュ\を図形文字とは解釈せず、次に続く図形文字1文字も図形文字とは解釈せず、この2文字の組み合わせによって別の処理を表す、という約束だ。この考え方は「エスケープ」と呼ばれる。「エスケープ」とは、「次に続く図形文字とセットで図形文字から外す」というくらいの意味で、送り手と受け手双方のこの約束によって複数の機能が透過的に実現することになる。送り手と受け手のプログラムが双方に対応していなければ、バックスラッシュがエスケープを意味するのかどうか、そのあとの一文字が表す機能が何であるのかなどは当然異なってくる。

注)
制御文字の1つである1Bh(ESC)を用い、他の制御文字の機能を超えた特別な処理が定義されることがある。こちらもエスケープと呼ばれ(本家だしそもそもESCだ)古い型の装置やソフトウエアに多い。混乱しないよう気をつけなければならない。

-3.メタキャラクタ:図形文字
後出の正規表現に用いるメタキャラクタは、エスケープと同じように受け手(例えばgrepなどのプログラム)が独自に解釈する図形文字で、やはり図形文字であることを放棄して用いられる。ファイルの中から指定の文字列を探索する目的で、そのマッチングの範囲を表現する。メタキャラクタには、比較的使われない図形文字が複数(多すぎて亜種もあるのが難点)選ばれている。エスケープ(バックスラッシュ)との組み合わせになっていないのは、メタキャラクタは連続して使う性質のものだから(読みにくくてしようがないから)かもしれない。正規表現でエスケープ(バックスラッシュ)は、定義されてしまっているメタキャラクタをもとの図形文字に戻す場合に用いられる。



<C0 Group (制御文字)>
ASC
00h NUL NULL
01h SOH Start Of Heading
02h STX Start of TeXt
03h ETX End of TeXt
04h EOT End Of Transmission
05h ENQ ENQuiry
06h ACK ACKnowledge
07h BEL BELL
08h BS Back Space
09h HT Horizontal Tabulation
0Ah LF Line Feed
0Bh VT Vertical Tabulation
0Ch FF Form Feed
0Dh CR Carriage Return
0Eh SO Shift Out
0Fh SI Shift In
10h DLE Data Link Escape
11h DC1 Device Control 1
12h DC2 Device Control 2
13h DC3 Device Control 3
14h DC4 Device Control 4
15h NAK Negative AcKnowledge
16h SYN SYNchronous idle
17h ETB End of Transmission Block
18h CAN CANcel
19h EM End of Medium
1Ah SUB SUBstitute
1Bh ESC ESCape
1Ch FS File Separator
1Dh GS Group Separator
1Eh RS Record Separator
1Fh US Unit Separator

<GL Group (図形文字)>
ASC EUC UTF-8
20h 20 20
21h 21 21 ! エクスクラメーション
22h 22 22 " ダブルクオォート
23h 23 23 # シャープ
24h 24 24 $ ダラー
25h 25 25 % パーセント
26h 26 26 & アンパサンド
27h 27 27 ' シングルクオォート
28h 28 28 ( パレンサシス
29h 29 29 ) パレンサシス
2Ah 2A 2A * アスタリスク
2Bh 2B 2B +
2Ch 2C 2C , カンマ
2Dh 2D 2D -
2Eh 2E 2E . ピリオド
2Fh 2F 2F / スラッシュ
30h 30 30 0
31h 31 31 1
32h 32 32 2
33h 33 33 3
34h 34 34 4
35h 35 35 5
36h 36 36 6
37h 37 37 7
38h 38 38 8
39h 39 39 9
3Ah 3A 3A : コロン
3Bh 3B 3B ; セミコロン
3Ch 3C 3C < レスザン
3Dh 3D 3D = イコール
3Eh 3E 3E > グレータザン
3Fh 3F 3F ? クエスチョン
40h 40 40 @ アットマーク
41h 41 41 A
42h 42 42 B
43h 43 43 C
44h 44 44 D
45h 45 45 E
46h 46 46 F
47h 47 47 G
48h 48 48 H
49h 49 49 I
4Ah 4A 4A J
4Bh 4B 4B K
4Ch 4C 4C L
4Dh 4D 4D M
4Eh 4E 4E N
4Fh 4F 4F O
50h 50 50 P
51h 51 51 Q
52h 52 52 R
53h 53 53 S
54h 54 54 T
55h 55 55 U
56h 56 56 V
57h 57 57 W
58h 58 58 X
59h 59 59 Y
5Ah 5A 5A Z
5Bh 5B 5B [ ブラケット
5Ch 5C C2A5 \ バックスラッシュ
5Dh 5D 5D ] ブラケット
5Eh 5E 5E ^ ハット
5Fh 5F 5F _ アンダースコア
60h 60 60 ` バッククオォート
61h 61 61 a
62h 62 62 b
63h 63 63 c
64h 64 64 d
65h 65 65 e
66h 66 66 f
67h 67 67 g
68h 68 68 h
69h 69 69 i
6Ah 6A 6A j
6Bh 6B 6B k
6Ch 6C 6C l
6Dh 6D 6D m
6Eh 6E 6E n
6Fh 6F 6F o
70h 70 70 p
71h 71 71 q
72h 72 72 r
73h 73 73 s
74h 74 74 t
75h 75 75 u
76h 76 76 v
77h 77 77 w
78h 78 78 x
79h 79 79 y
7Ah 7A 7A z
7Bh 7B 7B { ブレイス
7Ch 7C 7C | パイプ
7Dh 7D 7D } ブレイス
7Eh 7E E280B ~ チルダ

<C1 Group (制御文字)>
ASC
80h
81h
82h BPH Break Permitted Here
83h NBH No Break Here
84h
85h NEL NExt Line
86h SSA Start of Selected Area
87h ESA End of Selected Area
88h HTS Horizontal Tabulation Set
89h HTJ Horizontal Tabulation with Justification
8Ah VTS Vertical Tabulation Set
8Bh PLD Partial Line Down
8Ch PLU Partial Line Up
8Dh RI Reverse line feed
8Eh SS2 Single Shift 2
8Fh SS3 Single Shift 3
90h DCS Device Control String
91h PU1 Private Use 1
92h PU2 Private Use 2
93h STS Set Transmit State
94h CCH Cancel CHaracter
95h MW Message Waiting
96h SPA Start of Protected Area
97h EPA End of Protected Area
98h SOS Start Of String
99h
9Ah SCI Single Character Introducer
9Bh CSI Control Sequence Introducer
9Ch ST String Terminator
9Dh OSC Operating System Command
9Eh PM Privacy Message
9Fh APC Application Program Command


このように
7Ah
は、z (小文字のゼット:目に見える図形)が割り当てらている。アルファベットの大文字小文字と数字それにひととおりの記号を表すには128個という数は充分で、余ったコードが制御(命令)に使われているのである。例えば
0Ah
は、タイプライターで紙を一行送れという制御(命令)だ。もともとこの約束は、目に見えるアルファベットや数字に混ぜて制御(命令)コードを受け渡すという使い方を意図したものなのだが、コンソールの入出力や読み書きするテキストの処理だけでなく、コマンドとしてシステムとのインターフェイスにも古くから使われてきた。

こうしたルールとCRTやキーボードによって、人の手になるプログラミング作業の能率は大きく向上することになった。しかしすぐに、作業効率と作業量とのバランスは崩れることになる。オブジェクト化や再利用などでプログラミング作業の能率が上がってアプリケーションが広がり、プログラムのコードが大きくなってくると、テープに穴を開けていた時代には許されなかった反対向きの作業が目立って必要になってきたのである。それは、大きなプログラムの中の小さな(だが数の多い)修正や、「細部を作りながらコンセプトを造り上げる」といった、検索や編集に大きな負担がかかる現代風のオペレーションだ。
これを助けるため、同じ128個の枠の中で、別の間接的な表現機能である「正規表現」が多く用いられるようになる。「正規表現」は、あるひとつの「見える文字」で他の「見える文字」の組み合わせを代表できるようにする約束である。制御コードを使わないので「見える文字」を扱うどのような入出力も通過することができる。考えられる無数の文字列をひとつの文字列で表すことができる「正規表現」は、特に同じ(または類似の)文字列を複数のファイルの中で繰り返し正確に使わなければならないプログラミングには、無くてはならない大切な道具となっていった。


Back List














































Back List


正規表現


この節では、grepを例に正規表現(Regular Expression)という考え方を見てゆく。
grepの1つめの引数は検索パターン、2つめはファイル名である。
例えば以下は、/etc/filenameという名のファイルの中で、abcdefという文字列を探す。
grep -inI 'abcdef' /etc/filename (-inI: ケースインセンス、行番号付、バイナリファイル無視)
正規表現は、この検索パターンの中で以下のように使う。この例では . と * が正規表現(のメタキャラクタ)である。
grep -inI 'a.cdef' /etc/filename
grep -inI 'abcde*f' /etc/filename

正規表現とは、文字の組み合わせをダイナミックに定義できるコンパクトな方法であり、テキストの検索や修正などあらゆる場面で、プログラムの引数に対してユーザーが用いる表現方法だ。実際には正規表現は比較的ゆるやかな約束または仕様に過ぎず、実際にはそれぞれのプログラムがこれを独自に処理することが多い。正規表現はシェルやOSの基本機能ではないが、UNIXのコマンド(プログラム)には正規表現をバインドするものも多くある。インターネットで活躍するPerlという言語でも正規表現は頻繁に使われている。上述のとおりプログラムによってその仕様がいくぶん異なる可能性は覚悟しておかなければならないが、それでも、BRE(基本正規表現)、ERE(拡張正規表現)というおおまかなくくりで、主な汎用プログラムがどちらを使っているかを分けることはできる。

プログラム    BRE/ERE/ERE+
grepBRE
sedBRE
viBRE
moreBRE
edBRE
exBRE
egrepERE
grep -EERE
AWKERE(+AWK)

正規表現では、文字の組み合わせを指定するのにメタキャラクタを定義してそれを用いる。EREではメタキャラクタを増やすことによって、BREで複雑だったメタキャラクタへのエスケープが整理され、+(1文字以上)や ?(0か1文字)など新しい機能も装備された。




初めに、本題で混乱しないよう、3点ほど脱線し確認しておく。

1. grepで2つ目の引数は対象とするファイル名である。UNIXのシェルでは、パス名展開 (path name expansion) とその一部であるファイル名展開 (file name expansion) にはワイルドカードを用いることができ、シェルは実際に存在するディレクトリやファイル名にこれを展開してコマンドに渡す。したがってgrepの検索パターンとファイル名では * の意味が違うのである。
$grep -in ab*f /etc/f*name
(ab*f はファイル/パス名ではないのでこのままgrepに渡され、正規表現なので例えばabcdefにはマッチしない。)
(/etc/f*name はファイル/パス名なのでワイルドカードとして展開され、したがってファイル名filenameは検索対象になる。ただし、'/etc/f*name'とコートすればシェルの展開から守ることはできるようになる。)

2. シェルは、シェルのメタキャラクタ(正規表現のメタキャラクタではない) ; & ( ) | ^ < > ? * [ ] $ ` ' " \ ! { } space tab を解することで、引数などを区切ったり文字の演算や入出力制御を行ったりすることが可能だが、コーテーションは、それらシェルの動作から文字列を守る。シングルコーテーションは全てをカプサライズするが、ダブルコーテーションはこのうち「 $ ` \ 」を例外とする。
echo '$PATH'
$PATH
慣れないうちは、ファイル/パス名以外の引数は、面倒でもシングルコーテーションで囲っておく/シェルの機能を使いたい場合に限り注意してコーテーションを外す、という使い方が、意味の無い決まりごとのバリエーションに振り回されたりせず、安全かもしれない。

3. 正規表現で特殊な処理を行うメタキャラクタは、ASCIIでは普通の文字である。これに特別な意味が割り当てられて正規表現は機能する。これら予約されたメタキャラクタを図形文字として扱いたい(この場合検索する)場合は、バックスラッシュを用いてメタキャラクタから図形文字に向かってエスケープする。
grep -in 'a\.cdef' /etc/filename
(a.cdefそのものを検索する。abcdefなどにはマッチしない。)
さらに脱線だが、エスケープはファイル名の指定でも使われる。
grep -in 'a\.cdef' /etc/f\*name
(f*nameというファイルを対象とする。filenameというファイルは対象としない。)
この違いを確認しておくと混乱の予防に役立つだろう。1つめの引数である検索パターンの中のエスケープは正規表現からのエスケープだが(grepが処理します)、2つめの引数であるコートされていないファイル名で同様に使われているバックスラッシュは、シェル機能(この例ではワイルドカード)からのエスケープである。





では戻って、以下、grepやsedの標準にもなっている基本正規表現について、実際のパターンを例に使い方を見てみよう。

1.ブラケット外部のメタキャラクタ(基本的なもの)

.(ピリオド)
全ての一文字にマッチする。
例:a.c
abc  adc  a4c

*(アスタリスク)
直前の文字の零個以上の連続にマッチする。
例:a*
NULL  a  aa  aaa ...
例:aa*
a  aa  aaa ...

^(ハット)
行の最初にマッチする。

$(ダラ)
行の最後にマッチする。

[](ブラケット)
囲まれた文字のうちのどれか一個にマッチする。ブラケット内部では、ブラケット外部で使ったメタキャラクタは意味を持たない。単なる文字列として扱われる。ブラケット内部の^と-はここだけで通用するメタキャラクタである。「3.ブラケット内部のメタキャラクタ」を参照のこと。
例:[ack]
a  c  k



2.ブラケット外部のメタキャラクタ(エスケープして使うブレースとパーレン)
(以下、項目と機能のみ)

\{m\}(エスケープ+ブレース:繰り返し量を指定する)
\{m,\}
\{m,n\}
\(.....\)(エスケープ+パーレン:繰り返しなどに再利用するために包括指定する)



3.ブラケット内部のメタキャラクタ
ブラケット内部では、外部とはまた異なるメタキャラクタ(すなわち機能)が使われる。
[^](ブラケット内の先頭のハット)
[-](ブラケット内の先頭のハット)
^が先頭にあれば、それに続く(範囲の)文字以外の文字全てを並べたことと同じである。
-はその範囲の文字全てを並べたことと同じでである。

例:[^abc]
a、b、c以外の文字にマッチする。

例:[a-k]
aからkまでの文字にマッチする。

例:[^0-9]
0から9までの文字以外にマッチする。

例:[1-3a-c]
1、2、3、a、b、cにマッチする。

例:[abc^]
a、b、c、^にマッチする。
^が[]の中にあってもそれが先頭以外なら単に「^」という文字として扱われる。





正規表現とは関係ないが、参考までに、grepで制御コードを検索する例を紹介する。grepには16進数を指定する機能が無い。そのため、シェルの機能をもってgrepに文字コードを渡す、という方法が簡単でよく使われている。ダラとそれに続くシングルコーテーションがそれである。
$'\xff' 16進数FF
$'\x0a' 16進数0A (= $'\n'でも可)
$'\x0d' 16進数0D (= $'\r'でも可)
例えば、改行などにCRを含んだ行を検索する場合の例。
grep $'\x0d' filename

また、これも正規表現とは関係ないが、fileコマンドは、エンコード情報などに加え改行についても教えてくれる。
file filename
filename: HTML document, UTF-8 Unicode text, with CRLF line terminators


Back List














































Back List


sed


sedはテキストを編集するためのプログラムである。HTMLや各種プログラムのメンテナンスでは百を超えるファイルを扱うことがあるが、複数のファイルで繰り返し使われる文字列をほとんど全て書き換えなければならないというケースはめずらしくない。それらを一括して処理してくれるのがsedである。開発環境が特別なものに制限されてさえいなければ、sedは誰にでも簡単に扱うことができるプリミティブなツールのひとつと言えるだろう。sedはgrepを飛び越えて使われることも多く、処理はgrepと同じく行単位だが、実際には複数のメモリを使うことによって、文字、文字列、行、複数行、昇順逆順など、さまざまな処理が可能になっている。例えば文字列の編集(sコマンド)では、行の抽出のための検索と文字列の編集のための検索が別々に行われる。以下、よく使われる一部の機能を抜粋して整理する。(GNU sed 4.2.1-10)





--- 処理の流れ

sedでは、下記①から④までの処理が1行毎に繰り返し行われる。このループは、②で該当行が行検索パターンとマッチしたかどうかにかかわらず止まらない。したがってマッチした行が複数あればそれら全てについての処理が行われる。以下、「パターンスペース」とは、作業用のバッファとして使われる1行~複数行分のメモリのことである。

①パターンスペースを削除する(NULLにする)。
②ファイルから次の1行を読み込み(-iオプションでは同時にファイルからこの1行を\nを含めて削除)、\nを除いてパターンスペースに追加し、行検索パターンと比較する。
 →読み込んだ1行が複数行検索の最初の行、または途中の行であれば、パターンスペースに\nを加えてから②をくり返す   (したがって複数行の最後の行の\nは検索されず、それ以外の行の\nは検索されることになる)  →その1行が複数行検索の終わりの行か、または1行検索パターンにマッチしていれば③へ  →その他の場合(行検索にかからなかった場合)はパターンスペースの最後に\nを戻して出力し、④へ
③sedコマンドとその引数を読み、実行する。
 [行一括処理=a, c ,i, dコマンドの場合]  ・sedコマンドに従って次のとおり出力する。    a文字列:  パターンスペース + \n + 文字列 + \n    c文字列:  文字列+ \n    i文字列:  文字列+ \n + パターンスペース + \n    d:  NULL  [行内検索処理=sコマンドの場合]  ・引数の文字列に従ってパターンスペースを編集する。  ・パターンスペース + \n を出力する。
④ファイルの終わりでなければ①に戻る。





--- 行検索とsedコマンド

sedを使うということは、この手順②と③についてのsedスクリプト、
【行検索パターン】
【sedコマンド】
【sedコマンドの引数】
を、シェルのインラインに例えば次のように書くということである。
sed -e '【行検索パターン】【sedコマンド】【sedコマンドの引数】' filename (*3)
実際にはこのようなシェルでの記述になるだろう。

$ sed -e '/RegExpression/cSTRING' filename


・コーテーションは、sedスクリプトの記述をシェルの機能からカプサライズするかどうか、通常のルールにしたがうこと。(*4)(*5)
・スラッシュは、検索パターンの前後と、sコマンドの目的文字列の後に必要である。(*2)

【行検索パターン】(処理する行を抽出するための条件)(手順②で参照される)
  (行検索パターンが無ければ全ての行)   6  6行目   5,8  5行目から8行目までの行   /RegExpression/  この検索パターンを含む行   /RegExpression1/,/RegExpression2/  /検索パターン1/を含む行から/検索パターン2/を含む行までの行   3,/RegExpression2/  3行目から/検索パターン2/を含む行までの行   /RegExpression1/,8  /検索パターン1/を含む行から8行目までの行

【sedコマンド】(手順③で参照される)
通常、行の追加/挿入/置換/削除には a, i, c, dコマンド、文字の置き換えにはsコマンドが使われる。
  a  該当行の後に指定の1行を追加する。(*1)   i  該当行の前に指定の1行を挿入する。(*1)   c  該当行を指定の1行に置換する。(*1)   d  該当行を削除する。   s  該当行に対して、さらに文字列の検索と置換を行う。
パターンスペースとは別にホールドスペースという記憶領域を活用することもできる。行の処理が進んでいっても残しておけることが特徴。
  h  パターンスペースの内容全てをホールドスペースに上書きする。   H  同、追加する。   g  ホールドスペースの内容全てをパターンスペースに上書きする。   G  同、追加する。   x  パターンスペースとホールドスペースの内容を交換する。

【sedコマンドの引数】(手順③で参照される)
(目的文字列には新しく \n を複数含むことができる:行を増やす事が可能)
 ・STRING  a, c ,i コマンドの目的文字列  ・/RegEspression/STRING/  sコマンドの/行内検索パターン/目的文字列/ (*2)





--- オプション

・-eオプションが無い場合は、最初のシェル引数だけがsedスクリプトとして処理される。-eオプションは、その直後のシェル引数がsedスクリプトであることを宣言するために使う。これによって、それぞれ直前に-eを伴った複数のsedスクリプトを並べることができる。複数のスクリプトは検索ループの1行毎に左から順に全てが実行される。このようにsedスクリプトを連ねた場合の特別な動作は、最後ではないsedスクリプトの出力先がパターンスペースになるということだ。これが次のsedスクリプトの入力になる。(*3)

・-iオプションが無ければパターンスペースの出力先はコンソールである。-iオプションがある場合はファイルの全ての行が上書きで出力先になる(行検索に1行もマッチしない場合であってもファイルは新しくなるということだ)。操作の確認を行う場合は-iオプションを外しておくが、それだけでは全ての行がコンソールに出力されてしまうことになる。-i.bakなどオプション名に続けて別の拡張子を指定すれば、バックアップファイルを自動的に出力することができる。

・-nオプションは、出力をコンソールとして処理の確認を行う場合に使えるかもしれない。このオプションは出力時にパターンスペースをNULLにする。したがって、 a, c, i コマンドの場合は、追加/挿入/置換文字列のみが該当した回数だけ出力される。dコマンドの場合は該当するとNULLが出力されるのでコンソールには何も起こらない。つまり a, c, i, d コマンドの場合、どの行が該当したのかを知ることはできないのである。また、sコマンドではパターンスペースが出力になるので、このオプションを用いると通常は何も出力されなくなるが、pフラグを付けることによって該当した処理結果だけを出力することができる。しかしこのまま-nオプションを外してしまうと今度は該当行が二重に出力されることになり(-iオプションではファイルを上書き)、確認のためにpフラグを用いる事はあまり安全とは言えない。また、ここまででお気づきのとおり、-nオプションと-iオプションを同時に使ってはいけない。これらを同時に使うと、コンソールへの-nオプションによる表示の場合と同様に、ファイル上でも全ての/または殆どの行が削除されてしまうことになるからである。

したがって、実際には、-eオプションは必ず付けておき、-nオプションも、sコマンドのpフラグも使わないようにするのが無難と言えるだろう。使用例にある【簡単なルーチンの例】を参照のこと。





--- 使用例

【簡単なルーチンの例】
1. まずは、grepで検索パターンと該当行(変更前として)を確認する。

$ grep 'searchpattern' *.extention


2. 確認できたら、同じgrepの出力を今度はsedにパイプし(該当行に限定して)、同じ検索パターンを用い-iオプション無しで結果を確認する。
(全ての行を表示させても良いならここでgrepは不要。)

$ grep 'searchpattern' *.extention | sed -e '/searchpattern/s/beforepattern/aftertext/g'


3. これも確認できたら、コマンドラインからgrep部分を削除し、sedのみを(-i.bakオプションとファイル名を追加して)実行し、ファイルを上書きする。

$ sed -i.bak -e '/searchpattern/s/beforepattern/aftertext/g' *.extention




【cコマンド】(*1)
行の全てを置き換え

$ sed -e '/I am.......$/,/^You are/cSTRING1\nSTRING2\nSTRING3' *.* */*.*


文字列"I am a pen."などで終わる行から、"You are"で始まる行までを抽出し、それら全ての行を"STRING1\nSTRING2\nSTRING3"の3行に置き換える。
\nのバックスラッシュはシングルコーテーションでシェルからはカプサライズされている。コンソールには全行は表示せず、編集の行われた行だけを表示する。ファイルへの上書きは行わない。対象とするファイルは、カレントディレクトリとその下のディレクトリにある、拡張子のあるもの全て。

$ sed -e 'cSTRING' filename


全ての行で、各行を"STRING"の1行に置き換える。対象ファイルのファイル名はfilenameである。

$ sed -e '2cSTRING' filename


2行目を"STRING"の1行に置き換える。

sed -e '2,5cSTRING' filename


2行目から5行目の4行を"STRING"の1行に置き換える。

$ sed -e '/abc/cSTRING' filename


文字列"abc"を含む行を抽出し、その行を"STRING"の1行に置き換える。

$ sed -e '/abc/,/def/cSTRING1\nSTRING2' *.* */*.* */*/*.*


文字列"abc"を含む行から文字列"def"を含む行までを抽出し、
それら全ての行を"STRING1\nSTRING2"の2行に置き換える。
対象とするファイルは2レベル下のディレクトリまで。


【sコマンド】(*2)
行の中の一部を置き換え

$ sed -e 's/abc/def/' filename


全ての行で、最初の文字列"abc"を文字列"def"に置き換える。

$ sed -e '3s/abc/def/' filename


3行目のみで、最初の文字列"abc"を文字列"def"に置き換える。

$ sed -e '3,6s/abc/def/g' filename


3行目から6行目までの全行で、全ての文字列"abc"を文字列"def"に置き換える。
gはその行でマッチした全ての文字列を処理するためのフラグであって、gコマンドではない。

$ sed -e '/uvw/,/xyz/s/abc/def/g' filename


"uvw"を含む行から"xyz"を含む行までを抽出し、その全行で、全ての文字列"abc"を文字列"def"に置き換える。行を選択する行検索パターンとその中で置換を行うための文字列検索パターンとは別のものである。




(*1)
c, a, i など、行を一括して扱うsedコマンドを表す識別子1文字の直後に目的文字列(目的行)を続けるというフォーマット(インライン:cline12, aline34, iline56 ...)は、実は alternative syntax である。これらsedコマンドの元々のフォーマットは、c\, a\, i\ の直後に必ず改行を行ってから目的文字列を書く、という、シェルに書いたスクリプトだ。シェルが通常はバックスラッシュと改行をセットで削除してくれるということを考えると、一見これら2つの使い方の間に違和感はない。だが注意しなければならないのは、現在のsedの実装では、sed自身もこの位置のバックスラッシュを処理するということである。シェルから渡された後であっても(sedが読む段階でも)、c, a, i 直後のバックスラッシュは有ってもかまわない、すなわち有ったら削除されることになっているのだ。こうなると不都合が起こる。新しい行の先頭(c, a, i の直後)にエスケープシーケンスを入れようと思っても、そのバックスラッシュは蹴っとばされてしまうのである。
このことに対しては、常に変数やシングルコーテーションの中で(シェルが展開しないよう) c\, a\, i\ を使う、という手も無くはないが、これではさらにコーテーションを使いたいときの記述が複雑になりすぎる。「c, a, i で新しい行の先頭に改行やタブを入れたいならバックスラッシュを2連打」、これが最もシンプルで気楽な策になるだろう。その他のsedコマンド、たとえばsコマンドの目的文字列にこのような問題はない。




(*2)
sedコマンドは数種類の決められた1文字に限定されている。検索パターンはスラッシュで囲まれる。パラメータは引数の区切りによって終わりが定義される。ただし、考え方はよくわからないが、パラメータであってもsedコマンド s または y の目的文字列に限っては、後にフラグが続かなくても引数の区切りでは終わらない。必ずスラッシュで終えなければならない。




(*3)
sedで-eに続かない最後の引数は対象ファイル名であり、もちろん、grepで使う入力ファイル名などと同様、シェルがワイルドカードを展開する。

<grep>

$ grep -inI "i am a pen" filen*e

$ grep -rinI "i am a pen" [DirectoryPath]

$ grep -inI 'l.*x' ~/MyLinuxMemo/*



<find>

$ find [DirectoryPath] -name "abcd*.txt"






(*4)
コーテーションに関し、シェルにおける一般的な使い方を確認しておく。

シェルにとっての特殊文字を多く含む可能性がある文字列(プログラムやマークアップのコードなど)を扱う場合、それらをひとつひとつエスケープによって守るのは煩わしい。したがってコーテーションを使うのが一般的だが、ダブルコーテーションで囲みその中の「ダバダバ」4文字をケアするよりも、シングルコーテーションで囲みその中のシングルコーテーション (') 1文字をケアするほうが楽である。このとき特殊文字を実行する場合は、一旦そのシングルコーテーションを閉じ、外でそれを実行し、あらためてシングルコーテーションを再開する、といったひと手間が必要になる。たとえば途中で文字列変数 ($HOGE) を利用するというケースだ。また、シングルコーテーションの中ではエスケープが効かなくなる(特殊文字であるはずのバックスラッシュが通常文字になる)ということも忘れてはならない。

01:HOGE='inserted'
02:echo -e 'start:\t\t'$HOGE'\t\t:end'
03:start: inserted :end


また、シングルコーテーション自身を通常文字(ただのカタチ)として使いたい場合にも同じ手順を使うことになる。それは、シングルコーテーションの外で、もともと特殊文字であるはずの 【'】 をエスケープアウトし通常文字としてその位置に置くということである 【'\''】) 。

01:echo -e '\t\t1st Line:ADD START\n\t\t\t2nd Line:"Double Quoted"\n\t\t\t3rd Line:'\''Single Quoted'\''\n\t\t4th Line:ADD END'
02: 1st Line:ADD START
03: 2nd Line:"Double Quoted"
04: 3rd Line:'Singke Quoted'
05: 4th Line:ADD END


これに対しダブルコーテーションの中では、バックスラッシュ 【\】 はそのままエスケープシーケンスとして処理される。本来この約束だけでどんなエスケープ動作も表現できるはずなのだが、実際にはダブルコーテーション自身とバックスラッシュだけでなく、バックコーテーション【`】、ダラ【$】を合わせた4キャラクタ(ダバダバ)がシェルによって展開される。




(*5)
変数について。まずはシェル全般について確認し、次いでsedにおける用法をまとめる。

下記①の01:行目では、コーテーションや特殊文字をルールどおりに処理しつつ、変数が定義される。そしてコーテーションはここで剥がれる。シェルは02:行目でこの変数を行の中に展開するのだが、ここで注意しなければならないのは、変数の中の文字は既に一度処理が済んでいるということである。したがってコーテーションで囲まれていなくても再度処理されることはない。したがって02:行に展開されたであろう 【>】 はリダイレクトでなく通常文字であり、これは動作しない。

01:PP='>'
02:echo 'abc' $PP hoge
03:abc > hoge


しかし、半角スペースは特別だ。シェルが展開する変数の中にあっても、半角スペースは区切り文字としての役割を保つのだ。したがって下記②③は正常に動作する。逆に、(④のように)変数の中に本当の空白を入れたつもりでも、行の中に展開された後それは区切り文字としてシェルに認識されてしまうことになる。
注)変数になった後もシェルに対して特別な役割を保っていられるのは半角スペース(区切り文字)だけである。パイプ | 、リダイレクト > 、コーテーション " ' 、バックスラッシュ \ などの特殊文字は、この時点でシェルによって何の解析も処理もされないただの文字(通常文字)になってしまっている。これは、後述するように、半角スペースに対する手当さえ正しく行っておけば、変数の中にあるかもしれない特殊文字をシェルの動作から自動的に隔離できるということでもある。たしかに、正規表現など、シェルにとっても特殊文字である文字を多く含むだろう文字列を変数にした場合、展開によってその特殊な意味がいちいち復活していたのでは面倒なことになるだろう。

01:echo -e 'abc defg\n1234' > hoge
02:ARG='abc hoge'
03:grep $ARG
04:abc defg


01:echo -e 'abc defg\n1234' > hoge
02:ARG='grep abc hoge'
03:$ARG
04:abc defg


01:echo -e 'abc defg\n1234' > hoge
02:ARG1='bc de'
03:ARG2='hoge'
04:grep $ARG1 $ARG2
05:エラー(deというファイルが無い)


上の例④に対する対策は、通常文字である空白を含む変数は外側をダブルコーテーションで囲む、ということである(上に書いた理由によって内側=変数定義にコーテーションやエスケープを重ねて用いてもそれらは機能しない)。【$】を展開しなければならないのでシングルコーテーションは使えない。

04:grep "$ARG1" $ARG2
05:abc defg


さて、やっとsedである。sedも同様で(sedの場合はsedスクリプト全体がひとつの引数である)、通常文字である空白を含む変数を裸にしたままではうまくない。以下の例ではひとつの引数(sedスクリプト)に複数の変数を使っている。
01:FILENAME='hoge'
02:SEARCH='ab cd'
03:TARGET='ef gh'
04:sed -e /$SEARCH/i$TARGET $FILENAME


上の例の04:行は、次の行を直接タイプしたのと同じことである。半角スペースは変数を経由してもなお区切り文字として認識されるからである。これでは動作しない。
$ sed -e /ab cd/ief gh hoge


対策は先の例と同じで、それぞれの変数を外からコーテーションで囲むか、あるいはsedスクリプト全体を一括してコーテーションで囲めば良い。
04:sed -e /"$SEARCH"/i"$TARGET" $FILENAME
04:sed -e "/$SEARCH/i$TARGET" $FILENAME


その結果、04:行は、
$ sed -e /"ab cd"/i"ef gh" hoge
$ sed -e "/ab cd/ief gh" hoge

として処理され、いずれも正しく動作するようになる。コーテーションの範囲は、記述が複雑にならないようsedスクリプト全体とするのが楽だろう。

まとめると、sedの引数(sedスクリプト)に変数を用いその中に通常文字である空白が含まれる可能性がある場合は(そうでなくても)、sedスクリプト全体をダブルコーテーションで囲んでおくのがお気楽で良い。変数を使わないならシングルコーテーションだ(sedスクリプトをシェルから隔離する)。一方ファイル名はワイルドカードで展開することが多いのでコーテーションには要注意。




(*6)
特殊文字(シェルと正規表現のメタ文字を含む)について整理する。

【シェルのメタ文字】
| & ; > < ( ) $ ` ' \ " ~ # * [ ] ?

( \ はメタ文字が続かなくてもメタ文字:ただし消えるだけ)
-- エスケープアウト:上記すべて(通常文字が続けばそのバックスラッシュは消える)
-- エスケープインによって特殊な意味を持つ文字:なし


【シェルのダブルコーテーションの中にあってもメタ文字のままである文字】
" $ ` \

( \ はメタ文字が続く場合のみメタ文字:そうでない場合は通常文字として残る)
-- エスケープアウト:上記すべて
-- エスケープインによって特殊な意味を持つ文字:なし


【シェルのシングルコーテーションの中にあってもメタ文字のままである文字】
'

-- エスケープアウト:なし
-- エスケープインによって特殊な意味を持つ文字:なし


【基本正規表現のメタ文字】
. [ ] ^ $ \ *

-- エスケープアウト:上記すべて
-- エスケープインによって特殊な意味を持つ文字:
\( \) \{ \}



【sedの引数の中で特殊な意味を持つ文字(正規表現区間以外で)】
sedコマンドやフラグに割り当てられたアルファベット / \



【ASCIIの特殊文字(制御コード除く)】
(特殊文字 -単体で特殊な意味を持つ文字- は無い)
-- エスケープインによって特殊な意味を持つ文字:
\a \b \f \n \r \t \v \\ \? \' \" \0

(これらはそれぞれ制御コードとも対応付けられている)




(*7)
本文は改行コードが \n である場合を想定している。使っているシステムによってはこれが \r\n または \r であるかもしれないことには注意が必要だ。\r が検索されるのかどうかという問題である。例えばsedに似たいくつかのプログラムがWindowsでも使われているが、Linuxとの間でファイルの受け渡しを伴う業務では、行末の \r に関して何らかの処理が必要になるだろう。






(参考)
https://www.gnu.org/software/sed/manual/sed.html



Back List