Linux の 101 試験対策
テキスト・ストリームとフィルター
コマンドラインで GNU テキスト・ユーティリティーを使ってテキストを操作する
コンテンツシリーズ
このコンテンツは全#シリーズのパート#です: Linux の 101 試験対策
このコンテンツはシリーズの一部分です:Linux の 101 試験対策
このシリーズの続きに乞うご期待。
概要
この記事ではフィルターについて取り上げ、皆さんがフィルターを使ってテキストを操作するための複合パイプラインを作成できるようにします。テキストの表示、ソート、単語数と行数のカウント、文字の変換などのタスクを行う方法を学ぶとともに、ストリーム・エディター sed の使い方も学んでください。
この記事では、以下のトピックを取り上げます。
- テキスト・ファイルや出力ストリームをテキスト・ユーティリティー・フィルターに送り込んで出力を変更する方法
- GNU テキスト・ユーティリティー・パッケージに含まれる標準 UNIX コマンドの使用方法
- sed エディターを使用して、テキスト・ファイルに対する複雑な変更スクリプトを作成する方法
この記事は、Junior Level Administration (LPIC-1) 101 試験における主題 103の 103.2 の試験対策となります。この目標の重要度は 3 です。この記事の内容は、2009年4月時点での 101 試験の目標に対応します。明確な要件については、必ず目標を参照して確認してください。
テキストのフィルタリング
テキストのフィルタリングとは、テキストの入力ストリームを取り、そのテキストに対して何らかの変換を行ってから出力ストリームに送信するプロセスのことです。入力元または出力先がファイルである場合もありますが、Linux および UNIX® 環境でのフィルタリングは大抵の場合、コマンドのパイプラインを構成することによって行われます。コマンドのパイプラインでは、1 つのコマンドの出力を次のコマンドの入力として使用するために、出力がパイプされるか、またはリダイレクトされます。パイプとリダイレクトについてはストリーム、パイプ、リダイレクトに関する記事 (連載のロードマップを参照) で詳細に説明しますが、差し当たりここでは、パイプと基本的な出力のリダイレクトとして | 演算子と > 演算子を使用する方法について説明します。
ストリーム
ストリームとは、ライブラリー関数を使用して読み取りまたは書き込みすることができるバイトのシーケンスにすぎません。ライブラリー関数は、ベースにあるデバイスの詳細をアプリケーションから隠します。そのためプログラムはストリームを使用することによって、デバイスに依存することなく端末、ファイル、またはネットワーク・ソケットのいずれに対しても読み取り/書き込みを行うことができます。最近のプログラミング環境とシェルが使用する標準入出力ストリームは以下の 3 つです。
- stdin。コマンドに入力を提供する標準入力ストリームです。
- stdout。コマンドからの出力を表示する標準出力ストリームです。
- stderr。コマンドからのエラー出力を表示する標準エラー・ストリームです。
| によるパイプ
入力はコマンドに指定したパラメーターから渡すことが可能で、出力はユーザーの端末に表示することが可能です。多くのテキスト処理コマンド (フィルター) は、標準入力ストリームからでも、ファイルからでも入力を取ることができます。例えばコマンド command1 の出力をフィルター command2 への入力として使用するには、この 2 つのコマンドをパイプ演算子 (|) を使って結合します。リスト 1 に、短い単語のリストをソートするために echo
コマンドの出力をパイプする方法を示します。
リスト 1. echo コマンドの出力と sort コマンドの入力とのパイプ
[ian@echidna ~]$ echo -e "apple\npear\nbanana"|sort apple banana pear
上記のいずれのコマンドにしても、オプションや引数を指定することができます。また、| を使ってパイプラインの 2 番目のコマンドからの出力を 3 番目のコマンドにリダイレクトすることや、さらにその出力を次のコマンドの入力にするといったことを繰り返すことができます。それぞれに限られた機能を持つコマンドをいくつも接続して長いパイプラインを構成するという方法は、Linux および UNIX ではタスクを実現するための一般的な方法です。コマンドへの引数としてファイル名の代わりにハイフン (-) が使用されていることもよくあります。これは、入力はファイルからでなく、stdin から渡されるという意味です。
> による出力のリダイレクト
複数のコマンドからなるパイプラインを作成し、端末上に出力を表示するという機能は便利ですが、出力をファイルに保存したいという場合もよくあります。その場合には、出力リダイレクト演算子 (>) を使用します。
このセクションでは以降、小さなサンプル・ファイルを用いて説明を進めるので、lpi103-2 というディレクトリーを作成し、cd を実行してそのディレクトリーに移動してください。続いて > を使って echo
コマンドの出力を text1 という名前のファイルにリダイレクトします。この一連の操作をリスト 2 に記載します。出力はファイルにリダイレクトされているため、端末には表示されないことに注意してください。
リスト 2. コマンド出力のファイルへのリダイレクト
[ian@echidna ~]$ mkdir lpi103-2 [ian@echidna ~]$ cd lpi103-2 [ian@echidna lpi103-2]$ echo -e "1 apple\n2 pear\n3 banana" > text1
これで、パイプライン処理とリダイレクトを行うための基本的なツールを使えるようになったので、ここからはよく使われる UNIX および Linux のテキスト処理コマンドとフィルターに目を向けます。これらのセクションでは基本的な機能を抜粋して説明します。コマンドの詳細については該当する man ページを参照してください。
cat、od、split
test1 ファイルを作成したところで、このファイルの内容を確認してみてください。ファイルの内容を stdout に表示するには、cat
(concatenate) (連結) の略) コマンドを使用します。リスト 3 で、上記で作成したファイルの内容を確認します。
リスト 3. cat によるファイルの内容の表示
[ian@echidna lpi103-2]$ cat text1 1 apple 2 pear 3 banana
ファイル名が指定されていない場合 (またはファイル名として - を指定した場合)、cat
コマンドは stdin から入力を取ります。今度はこのコマンドと出力リダイレクトを併せて使用して、別のテキスト・ファイルを作成します (リスト 4 を参照)。
リスト 4. cat によるテキスト・ファイルの作成
[ian@echidna lpi103-2]$ cat >text2 9 plum 3 banana 10 apple
リスト 4 の cat
は、ファイルの終わりに達するまで stdin からの読み取りを続けます。ファイルの終わりを通知するには、Ctrl-d (Ctrl を押しながら d を押すこと) の組み合わせを使用します。これは、bash シェルを終了するときと同じキーの組み合わせです。フルーツの名前を整列させるには、Tab キーを使用してください。
前述のとおり、cat は concatenate (連結) の略です。つまり cat
を使えば、複数のファイルを連結して表示することができます。リスト 5 には、これまでに作成した 2 つのファイルの両方が表示されています。
リスト 5. cat による 2 つのファイルの連結
[ian@echidna lpi103-2]$ cat text* 1 apple 2 pear 3 banana 9 plum 3 banana 10 apple
2 つのサンプル・テキスト・ファイルを cat
を使って表示すると、内容が表示される位置が違うことに気付くはずです。この違いをもたらす原因について学ぶには、ファイル内にある制御文字を調べる必要があります。制御文字は、それ自体が何らかの表現で表示されるのではなく、出力されるテキストの表示に作用します。したがって、これらの特殊文字を見つけて解釈するには、特殊文字が表現される形式でファイルをダンプしなければなりません。GNU テキスト・ユーティリティーには、そのためのコマンド、od
(Octal Dump (8 進ダンプ)) があります。
od
には、ファイル・オフセットの基数を制御する -A
オプションや、ファイルの内容を表示する際の書式を制御する -t
オプションなど、複数のオプションがあります。オフセットの基数には、o (8 進数、これがデフォルトです)、d (10 進数)、x (16 進数)、または n (オフセット非表示) を指定することができます。出力は、8 進数、16 進数、10 進数、浮動小数点、バックスラッシュ・エスケープを使用した ASCII、または名前の付けられた文字 (改行には nl、水平タブには ht など) として表示することができます。リスト 6 に、サンプル・ファイル text2 をダンプする場合に使用できる書式をいくつか記載します。
リスト 6. od によるファイルのダンプ
[ian@echidna lpi103-2]$ od text2 0000000 004471 066160 066565 031412 061011 067141 067141 005141 0000020 030061 060411 070160 062554 000012 0000031 [ian@echidna lpi103-2]$ od -A d -t c text2 0000000 9 \t p l u m \n 3 \t b a n a n a \n 0000016 1 0 \t a p p l e \n 0000025 [ian@echidna lpi103-2]$ od -A n -t a text2 9 ht p l u m nl 3 ht b a n a n a nl 1 0 ht a p p l e nl
注:
cat
の-A
オプションは、タブと行の終わりを確認する手段にもなります。詳細については、man ページを参照してください。- text2 ファイルでタブの代わりにスペースが使われている場合は、この後の「expand、unexpand、tr」のセクションで、ファイル内でのタブとスペースを切り替える方法を読んでください。
- メインフレームの知識がある場合は、別のユーティリティー・セットに含まれている hexdump ユーティリティーを使うこともできます。ここではこのユーティリティーについて説明しないので、man ページを参照してください。
私たちが使用しているサンプル・ファイルは非常に小さなものですが、ファイルのサイズが大きいために、いくつかの部分に分割しなければならないという場合もあります。例えば、DVD を作成してもらうためにサイズの大きなファイルを CD サイズのチャンクに分割し、それを CD に書き込んで送り届けたい場合があるかもしれません。このタスクを引き受けるのは、split
コマンドです。cat
コマンドを使ってファイルを簡単に作り直せるように、split
コマンドでも簡単にファイルを分割することができます。デフォルトでは、split
コマンドによって処理されたファイルの名前には、「x」というプレフィックスと「aa」、「ab」、「ac」… 「ba」、「bb」などのサフィックスが付加されます。これらのデフォルト設定はオプションで変更することができます。また、出力ファイルのサイズを制御できることに加え、出力ファイルに完全な行を書き込むか、あるいはバイト数だけを書き込むかを制御することもできます。
リスト 7 は、2 つのサンプル・テキスト・ファイルを分割し、出力ファイルにデフォルトとは異なるプレフィックスを付ける例です。text1 は最大で 2 行が含まれるファイルに分割し、text2 は最大 18 バイトが含まれるファイルに分割します。次に分割された部分を、cat
を使用して個別に表示するとともに、グロビング (globbing) を使用して完全なファイルを表示します。グロビングについては、基本的なファイルおよびディレクトリー管理に関する記事 (連載のロードマップを参照) で説明します。
リスト 7. split と cat による分割と再結合
[ian@echidna lpi103-2]$ split -l 2 text1 [ian@echidna lpi103-2]$ split -b 17 text2 y [ian@echidna lpi103-2]$ cat yaa 9 plum 3 banana 1[ian@echidna lpi103-2]$ cat yab 0 apple [ian@echidna lpi103-2]$ cat y* x* 9 plum 3 banana 10 apple 1 apple 2 pear 3 banana
分割後の yaa という名前のファイルは改行文字で終わっていないため、cat
を使ってファイルを表示した後にプロンプトがオフセットされたことに注意してください。
wc、head、tail
cat
はファイル全体を表示します。ここでのサンプル・ファイルのように小さなファイルであればファイル全体を表示しても問題ありませんが、ファイルのサイズが大きい場合を考えてみてください。その場合、まずは wc
(Word Count) コマンドを使って、ファイルの大きさを確認するとよいでしょう。wc
コマンドはファイルに含まれる行数、単語数、およびバイト数を表示します。バイト数を調べるには、ls -l
を使用するという方法もあります。リスト 8 に、2 つのサンプル・テキスト・ファイルの詳細な一覧表示に続き、wc
による出力を記載します。
リスト 8. テキスト・ファイルでの wc の使用
[ian@echidna lpi103-2]$ ls -l text* -rw-rw-r--. 1 ian ian 24 2009-08-11 14:02 text1 -rw-rw-r--. 1 ian ian 25 2009-08-11 14:27 text2 [ian@echidna lpi103-2]$ wc text* 3 6 24 text1 3 6 25 text2 6 12 49 total
オプションで、wc
の出力を制御することや、行の最大長などの情報も表示することができます。詳細については、man ページを参照してください。
ファイルの最初の部分 (先頭) または最後の部分 (末尾) を表示するには、それぞれ head
コマンドと tail
コマンドを使用することができます。この 2 つのコマンドは、フィルターとして使用することも、ファイル名を引数として取ることもできます。デフォルトでは、ファイルまたはストリームの最初、または最後の 10 行を表示します。リスト 9 ではシステム起動時のメッセージを表示する dmesg
コマンドを wc
、tail
、および head
と組み合わせて使用することで、791 個のメッセージがあることを検出した後、そのうちの最後の 10 個メッセージを表示し、さらに最後から 15 番目以降の 6 個のメッセージを表示しています。この出力で一部の行を省略する場合は、… で示してあります。
リスト 9. wc、head、および tail によるブート・メッセージの表示
[ian@echidna lpi103-2]$ dmesg|wc 791 5554 40186 [ian@echidna lpi103-2]$ dmesg | tail input: HID 04b3:310b as /devices/pci0000:00/0000:00:1a.0/usb3/3-2/3-2.4/3-2.4:1.0/input/i nput12 generic-usb 0003:04B3:310B.0009: input,hidraw1: USB HID v1.00 Mouse [HID 04b3:310b] on us b-0000:00:1a.0-2.4/input0 usb 3-2.4: USB disconnect, address 11 usb 3-2.4: new low speed USB device using uhci_hcd and address 12 usb 3-2.4: New USB device found, idVendor=04b3, idProduct=310b usb 3-2.4: New USB device strings: Mfr=0, Product=0, SerialNumber=0 usb 3-2.4: configuration #1 chosen from 1 choice input: HID 04b3:310b as /devices/pci0000:00/0000:00:1a.0/usb3/3-2/3-2.4/3-2.4:1.0/input/i nput13 generic-usb 0003:04B3:310B.000A: input,hidraw1: USB HID v1.00 Mouse [HID 04b3:310b] on us b-0000:00:1a.0-2.4/input0 usb 3-2.4: USB disconnect, address 12 [ian@echidna lpi103-2]$ dmesg | tail -n15 | head -n 6 usb 3-2.4: USB disconnect, address 10 usb 3-2.4: new low speed USB device using uhci_hcd and address 11 usb 3-2.4: New USB device found, idVendor=04b3, idProduct=310b usb 3-2.4: New USB device strings: Mfr=0, Product=0, SerialNumber=0 usb 3-2.4: configuration #1 chosen from 1 choice input: HID 04b3:310b as /devices/pci0000:00/0000:00:1a.0/usb3/3-2/3-2.4/3-2.4:1.0/input/i nput12
tail
の一般的な使用法としては、-f
オプション (通常は行数を 1 に設定) を使ってファイルを追跡するという使い方もあります。この方法は、バックグラウンド・プロセスがファイルに出力を生成している場合、ファイルの内容を調べてプロセスの実行状況を確認するために使用することができます。このモードでは、tail
はキャンセル (Ctrl-c を使用) されるまで実行され、ファイルに行が書き込まれと、その行を表示します。
expand、unexpand、tr
サンプル・ファイルの text1 と text2 を作成したときに、text2 はタブ文字を使って作成しましたが、タブをスペースに変換したり、あるいはその逆の変換を行ったりする必要が出てくる場合もあります。この双方のタスクに対応するのが、expand
および unexpand
コマンドです。どちらのコマンドでも、-t
オプションを使ってタブ・ストップ数を設定することができます。-t に続く単一の値によって、タブが繰り返されるときの間隔が設定されるのです。リスト 10 に、text2 のタブのそれぞれを 1 つのスペースに展開する方法、そして text2 のテキストのアラインメントを解除する expand
と unexpand
のちょっと変わったシーケンスを示します。
リスト 10. expand と unexpand の使用
[ian@echidna lpi103-2]$ expand -t 1 text2 9 plum 3 banana 10 apple [ian@echidna lpi103-2]$ expand -t8 text2|unexpand -a -t2|expand -t3 9 plum 3 banana 10 apple
残念ながら、unexpand
を使って text1 のスペースをタブに置換することはできません。unexpand
ではタブに変換するにはスペースが 2 つ以上必要なためです。ただし、tr
コマンドを使用して 1 つの文字セット (set1) 内の文字を別の文字セット (set2) 内の対応する文字に変換することはできます。リスト 11 に、tr
を使用してスペースをタブに変換する方法を示します。tr
は単なるフィルターであるため、このコマンドの入力は cat
コマンドを使って生成します。この例では、- を使用して cat
に対して標準入力を指定し、tr
の出力と text2 ファイルを連結できるようにする方法もわかります。
リスト 11. tr の使用
[ian@echidna lpi103-2]$ cat text1 |tr ' ' '\t'|cat - text2 1 apple 2 pear 3 banana 9 plum 3 banana 10 apple
上記の 2 つの例の内容がよくわからない場合は、od
を使ってパイプラインの各ステージを順に終了してみてください。以下はその一例です。cat text1 |tr ' ' '\t' | od -tc
pr、nl、fmt
pr
コマンドは、印刷用にファイルの書式を設定するために使用します。デフォルトのヘッダーにはファイル名とファイルの作成日時、そしてページ番号と 2 行の空白のフッターが組み込まれます。複数のファイルや標準入力ストリームから出力が作成される場合には、ファイル名と作成日の代わりに現在の日時が使用されます。複数のファイルを並べて出力し、オプションを使って書式設定のさまざまな側面を制御することができます。詳細については、同じく man ページを参照してください。
行に番号を付ける nl
コマンドは、ファイルを印刷するときに便利です。また、cat
コマンドの -n
オプションで行に番号を付けることもできます。リスト 12 に、サンプル・テキスト・ファイルの印刷方法に続き、text2 に番号を付けて text1 と並べて印刷する方法を示します。
リスト 12. 印刷用の行番号と書式の設定
[ian@echidna lpi103-2]$ pr text1 | head 2009-08-11 14:02 text1 Page 1 1 apple 2 pear 3 banana [ian@echidna lpi103-2]$ nl text2 | pr -m - text1 | head 2009-08-11 15:36 Page 1 1 9 plum 1 apple 2 3 banana 2 pear 3 10 apple 3 banana
テキストの書式を設定するには、fmt
コマンドも役立ちます。このコマンドは、テキストが余白の中に収まるように書式を設定します。複数の短い行を結合することも、長い行を分割することもできます。リスト 13 では、!#:*
履歴機能のバリエーションを使ってセンテンスの入力を 4 回保存し、1 行の長いテキストが含まれる text3 を作成します。また、1 行あたり 1 つの単語が含まれる text4 も作成します。その後、cat
を使用して、行の終わりを示す「$」文字も含め、書式が設定されていない状態でファイルの内容を表示します。最後に fmt
を使用して各行が最大 60 文字になるように書式を設定します。その他のオプションについては、同じく man ページで詳細を調べてください。
リスト 13. 行の最大長に従った書式設定
[ian@echidna lpi103-2]$ echo "This is a sentence. " !#:* !#:1->text3 echo "This is a sentence. " "This is a sentence. " "This is a sentence. ">text3 [ian@echidna lpi103-2]$ echo -e "This\nis\nanother\nsentence.">text4 [ian@echidna lpi103-2]$ cat -et text3 text4 This is a sentence. This is a sentence. This is a sentence. $ This$ is$ another$ sentence.$ [ian@echidna lpi103-2]$ fmt -w 60 text3 text4 This is a sentence. This is a sentence. This is a sentence. This is another sentence.
sort と uniq
sort
コマンドは、システムのロケールに対応する照合順序 (LC_COLLATE) で入力をソートします。sort
コマンドではソート済みのファイルをマージしたり、ファイルがソートされているかどうかをチェックしたりすることもできます。
リスト 14 は、text1 の空白をタブに変換してから、sort
コマンドを使って 2 つのサンプル・テキスト・ファイルをソートする例です。ソート順は文字順となっていることから、意外な結果になる場合もあります。しかし幸い、sort
コマンドは数値によっても、文字の値によってもソートすることができます。さらに、選択したソートの基準をレコード全体に指定することも、フィールドごとに指定することもできます。別のフィールド区切り文字を指定しない限り、フィールドは空白またはタブで区切られます。リスト 14 の 2 番目の例では、最初のフィールドを数値でソートし、2 番目のフィールドを照合順序 (アルファベット順) でソートします。また、この例では -u
オプションを使用して重複する行を排除し、それぞれに固有の行のみとなるようにしています。
リスト 14. 文字と数値によるソート
[ian@echidna lpi103-2]$ cat text1 | tr ' ' '\t' | sort - text2 10 apple 1 apple 2 pear 3 banana 3 banana 9 plum [ian@echidna lpi103-2]$ cat text1|tr ' ' '\t'|sort -u -k1n -k2 - text2 1 apple 2 pear 3 banana 9 plum 10 apple
フルーツ「apple」が含まれる行がまだ 2 つ残っていることに注目してください。これは、この例では k1n と k2 という 2 つのソート・キーの両方で一意性がテストされるためです。上記のパイプラインのステップを変更または追加して、2 番目の「apple」のオカレンスを排除するにはどうすればよいでしょうか。
重複する行の排除を制御するには、uniq
という別のコマンドを使用することができます。uniq
コマンドは通常、ソート済みのファイルに対して実行されますが、ファイルがソートされているかどうかに関わらず、連続する同一の行をファイルから削除します。uniq
コマンドは一部のフィールドを無視することもできます。リスト 15 では、2 番目のフィールド (フルーツの名前) を使って 2 つのテキスト・ファイルをソートした上で、2 番目のフィールド以降が同一になっている行を排除します (つまり、uniq
でテストする際に最初のフィールドをスキップするということです)。
リスト 15. uniq の使用
[ian@echidna lpi103-2]$ cat text1|tr ' ' '\t'|sort -k2 - text2|uniq -f1 10 apple 3 banana 2 pear 9 plum
この例では照合順序でソートされることから、uniq
は「1 apple」の行ではなく「10 apple」の行を残します。キー・フィールド 1 に数値順のソートを追加して、結果がどのように変わるか確かめてみてください。
cut、paste、join
テキスト・データのフィールドを操作するコマンドをあと 3 つ説明します。これらのコマンドは、表形式のデータを操作する際に特に役立ちます。まず始めに、cut
コマンドから見ていきましょう。このコマンドは、テキスト・ファイルからフィールドを抽出します。デフォルトのフィールド区切り文字はタブ文字です。リスト 16 では、cut
コマンドを使用して text2 の 2 つの列を切り離し、出力区切り文字としてスペースを使用しています。この方法は、各行のタブをスペースに変換する風変わりな方法です。
リスト 16. cut の使用
[ian@echidna lpi103-2]$ cut -f1-2 --output-delimiter=' ' text2 9 plum 3 banana 10 apple
pr
コマンドに -m
オプションを指定してファイルをマージするのと同じように、paste
コマンドは複数のファイルからの行を横に並べて貼り付けます。リスト 17 に、2 つのサンプル・テキスト・ファイルを貼り付けた結果を示します。
リスト 17. ファイルの貼り付け
[ian@echidna lpi103-2]$ paste text1 text2 1 apple 9 plum 2 pear 3 banana 3 banana 10 apple
上記は単純な貼り付け操作の例ですが、paste
では 1 つ以上のファイルのデータを何通りもの方法で貼り付けることができます。詳細については、man ページを参照してください。
フィールドを操作する最後のコマンドとして説明するのは、一致するフィールドをベースに複数のファイルを結合する join
です。このコマンドの場合、対象ファイルの結合されるフィールドはソートされていなければなりません。text2 は数値順にソートされていないので、このファイルをソートしてから結合すると、一致する結合フィールド (この例の場合、値が 3 のフィールド) を持つ 2 つの行が結合されることになります。
リスト 18. 結合フィールドによるファイルの結合
[ian@echidna lpi103-2]$ sort -n text2|join -j 1 text1 - 3 banana banana join: file 2 is not in sorted order
上記で何が誤っているかと言うと、「sort と uniq」のセクションで説明した文字および数値によるソートを思い出してください。結合は、ロケールの照合順序に従って、一致する文字に対して行われます。したがって、フィールドの長さがすべて同じでない限り、数値フィールドでは機能しません。
上記では、-j 1
オプションを指定して各ファイルのフィールド 1 で結合するようにしています。結合対象として使用するフィールドは、ファイルごとに指定できます。つまり、例えば一方のファイルではフィールド 3 で、他方のファイルではフィールド 10 で結合することも可能です。
ここで新しく text5 ファイルを作成し、2 番目のフィールド (フルーツの名前) で text1 をソートしてから、スペースをタブに置き換えてください。次に text2 を 2 番目のフィールドでソートしてから、それぞれのファイルの 2 番目のフィールドを結合フィールドとして使用して text 5 と結合すると、2 つの項目 (apple と banana) が一致するはずです。リスト 19 に、この場合の結合を示します。
リスト 19. 結合フィールドによるファイルの結合
[ian@echidna lpi103-2]$ sort -k2 text1|tr ' ' '\t'>text5 [ian@echidna lpi103-2]$ sort -k2 text2 | join -1 2 -2 2 text5 - apple 1 10 banana 3 3
sed
sed (stream editor) は、数多くの本、および本の章で取り上げられているだけでなく、いくつかの developerWorks の記事でも取り上げられているストリーム・エディターです (「参考文献」を参照)。sed は極めて強力であり、このエディターで実現可能な内容を制限するのはユーザーの想像力以外にはありません。この短いセクションの目的は sed について完全に、あるいは広範に説明することではなく、皆さんに sed への興味を持ってもらうことです。
これまで見てきたテキスト・コマンドの多くと同じく、sed はフィルターとして機能することも、ファイルからその入力を取ることもできます。出力は標準出力ストリームに送られます。sed は入力から渡された行をパターン・スペース (pattern space) にロードし、sed の編集コマンドをパターン・スペースの内容に適用してからそのパターン・スペースを標準出力に書き込みます。sed ではパターン・スペースの中で複数の行を結合することも、パターン・スペースの内容をファイルに書き込むことも、選択された出力だけを書き込むことも、書き込みを行わないことも可能です。
sed は、パターン・スペースでのテキストの検索や、選択的なテキスト置換、そして一連の編集コマンドの操作対象とするテキスト行の制御に、正規表現の構文を使用します。正規表現については、正規表現を使用したテキスト・ファイルの検索についての記事で詳しく説明します (連載のロードマップを参照)。テキストはホールド・バッファー (hold buffer) に一時的に保存されます。ホールド・バッファーをパターン・スペースの代わりにすることも、ホールド・バッファーをパターン・スペースに追加したり、パターン・スペースとの間でホールド・バッファーを交換したりすることも可能です。sed には限られた数のコマンドしかありませんが、これらのコマンドを正規表現の構文およびホールド・バッファーと組み合わせることで、さまざまな驚くべき機能が実現されます。一連の sed コマンドは通常、sed スクリプトと呼ばれます。
リスト 20 に、3 つの単純な sed スクリプトを記載します。最初の sed スクリプトでは、s
(substitute (置換)) コマンドを使用して各行で小文字の「a」を大文字に置換します。この例では最初の「a」しか置換しませんが、2 番目の例には「g」(global (グローバル) の略) フラグを追加し、sed がすべてのオカレンスを変更するようにします。3 番目のスクリプトではさらに行を削除する d
(delete (削除)) コマンドを追加し、アドレスを 2 に設定して 2 行だけが削除されるようにしています。各コマンドをセミコロン (;) で区切り、2 番目のスクリプトで使用したグローバル置換を使用して「a」を「A」に置換します。
リスト 20. sed スクリプトの開始
[ian@echidna lpi103-2]$ sed 's/a/A/' text1 1 Apple 2 peAr 3 bAnana [ian@echidna lpi103-2]$ sed 's/a/A/g' text1 1 Apple 2 peAr 3 bAnAnA [ian@echidna lpi103-2]$ sed '2d;$s/a/A/g' text1 1 apple 3 bAnAnA
sed は個別の行を操作するだけでなく、複数行の範囲を操作することもできます。範囲の開始と終わりはカンマ (,) で区切った行番号または正規表現で指定します。範囲の終わりには、ファイルの終わりを示すドル記号 ($) を指定することも可能です。アドレスまたはアドレスの範囲を指定する場合、複数のコマンドを中括弧 ({ と }) で囲んでグループ化し、そのコマンドのグループを範囲で選択した行にだけ適用することができます。リスト 21 に、ファイルの最後の 2 行にのみグローバル置換を適用する 2 つの方法を示します。このリストには、複数のコマンドをスクリプトに追加する -e
オプションの使い方も示されています。
リスト 21. sed アドレス
[ian@echidna lpi103-2]$ sed -e '2,${' -e 's/a/A/g' -e '}' text1 1 apple 2 peAr 3 bAnAnA [ian@echidna lpi103-2]$ sed -e '/pear/,/bana/{' -e 's/a/A/g' -e '}' text1 1 apple 2 peAr 3 bAnAnA
sed スクリプトはファイルに格納することもできます。実際、頻繁に使用するスクリプトはファイルに格納したくなるでしょう。前に tr
コマンドを使用して text1 の空白をタブに変更したことを思い出してください。今度はこの変更を、ファイルに格納された sed で行います。echo
コマンドを使用してファイルを作成すると、その結果はリスト 22 のようになります。
リスト 22. sed のワン・ライナー
[ian@echidna lpi103-2]$ echo -e "s/ /\t/g">sedtab [ian@echidna lpi103-2]$ cat sedtab s/ / /g [ian@echidna lpi103-2]$ sed -f sedtab text1 1 apple 2 pear 3 banana
リスト 22 のような便利な sed のワン・ライナーは豊富にあります。そのうちの一部については、「参考文献」のリンクを参照してください。
最後に紹介する sed の例は、=
コマンドを使って行番号を出力した後、結果の出力をもう一度 sed によってフィルタリングすることで、行番号を付ける nl
コマンドの効果を模倣するというものです。リスト 23 では、=
コマンドを使って行番号を出力し、次に N
コマンドで 2 番目の入力行をパターン・スペースに読み込んだ後、最後にパターン・スペースの 2 つの行の間にある改行文字 (\n) を削除します。
リスト 23. sed による行の番号付け
[ian@echidna lpi103-2]$ sed '=' text2 1 9 plum 2 3 banana 3 10 apple [ian@echidna lpi103-2]$ sed '=' text2|sed 'N;s/\n//' 19 plum 23 banana 310 apple
上記は意図したとおりの結果になっていません。ここで目的としていることは、行番号を一列に並べ、ファイルからの行の前に多少のスペースを入れることです。そこで、リスト 24 では数行のコマンドを入力しています (2 番目のプロンプト > に注意してください)。この例を検討した上で、下記の説明を読んでください。
リスト 24. sed による行の番号付け (2 回目)
[ian@echidna lpi103-2]$ cat text1 text2 text1 text2>text6 [ian@echidna lpi103-2]$ ht=$(echo -en "\t") [ian@echidna lpi103-2]$ sed '=' text6|sed "N > s/^/ / > s/^.*\(......\)\n/\1$ht/" 1 1 apple 2 2 pear 3 3 banana 4 9 plum 5 3 banana 6 10 apple 7 1 apple 8 2 pear 9 3 banana 10 9 plum 11 3 banana 12 10 apple
上記で行ったステップは以下のとおりです。
- まず
cat
を使用して、text1 および text2 ファイルのコピーから 12 行のファイルを作成します。桁数が異なっていなければ、列内で番号の書式を設定しても面白くありません。 - bash シェルは Tab キーを使用してコマンドを完了するので、本物のタブが必要なときに使用できる専用のタブ文字があると便利です。そこで
echo
コマンドを使用して、専用のタブ文字をシェル変数「ht」に保存します。 - 前に行ったように、行番号の後にデータ行が続くストリームを作成し、このストリームを sed の 2 つの目のコピーでフィルタリングします。
- 2 番目の行をパターン・スペースに読み込みます。
- パターン・スペースの先頭 (^ で指定) に行番号を追加し、6 つの空白を追加します。
- 次に、改行までの行のすべての部分を改行前の最後の 6 文字で置き換えてタブ文字を追加します。これで出力行の最初の 6 列で、行番号が一列に並ぶことになります。「s」コマンドの左側の部分では「\(」と「\)」を使って、右側の部分で使用する文字にマークを付けていることに注意してください。右側の部分では、このようにマークが付けられた最初のセット (この例では唯一のセット) を \1 として参照します。この例でのコマンドは、二重引用符 (") で囲まれているため、置換は $ht に対して行われることにも注意が必要です。
バージョン 4 の sed には、info
形式のドキュメントと併せて多数の優れたサンプルが含まれています。これらのサンプルは、これよりも前のバージョン 3.02 には含まれていません。GNU sed では sed --version
でバージョンを表示することができます。
ダウンロード可能なリソース
関連トピック
- developerWorks の LPIC-1 ロードマップを利用して、2009年4月時点での目標に基づく LPIC-1 認定の試験勉強として役立つ developerWorks の記事を見つけてください。
- LPIC Program サイトで、Linux Professional Institute の 3 つの Linux システム管理資格認定レベルについて、詳しい目標、タスクのリスト、そして出題例を調べてください。特に、2009年4月時点での LPI 試験 101 および LPI 試験 102 の目標は要チェックです。最新の目標については、必ず LPIC Program サイトを参照してください。
- developerWorks の連載「LPI exam prep」をすべて読んで、Linux の基礎を学び、2009年4月以前の LPI 試験目標に基づくシステム管理者認定試験に備えてください。
- 「Basic tasks for new Linux developers」(developerWorks、2005年3月) を読んで、ターミナル・ウィンドウやシェル・プロンプトの開き方をはじめ、さまざまなタスクを行う方法を学んでください。
- The Linux Documentation Project には、HOWTO 文書をはじめ、各種の有益な文書が豊富に揃っています。
- 連載「実例でわかるsed」の第 1 回、第 2 回、第 3 回は、sed のスキルを伸ばすのに最適な手段です。
- developerWorks Linux ゾーンに豊富に揃った Linux 開発者向けの資料を調べてください。記事とチュートリアルの人気ランキングも要チェックです。
- developerWorks に掲載されているすべての「Linux のヒント」シリーズの記事と Linux チュートリアルを参照してください。