授業資料/05 の変更点


#CONTENTS

//  第 5 回 -- フィルタ I

* Filter: フィルタプログラム [#f32c978e]

&br;&br;
&ref(./filter.png);
&br;&br;
フィルタとは,入力したテキストを加工して出力するプログラム一般のことだ.
&br;
unix には単純な機能を持つフィルタが多い.
これらをパイプでつなげると,複雑で大量のテキスト処理が簡単にできる.
// &br;&br;
// コンピュータ上の情報の多くがテキストという形で保存されているため,
// 複雑で大量テキスト加工が簡単なコマンドの組み合わせで実現できてとても便利だ.

&br;&br;
CENTER:&size(24){''フィルタを使うと複雑なことがとても簡単に!''};
&br;&br;

* Simple filters: 単機能なフィルタ [#sf47fc48]

まず,単純な機能のフィルタを紹介する.
//事実上一つの機能しか持たない
// &br;
// ごく簡単な処理はこれらのフィルタを組み合わせることで大体できるはずだ.
// これら単機能なフィルタは長い間使い込まれたプログラムが多く,
// その分鍛え抜かれているといえる.
// 実際,メモリや cpu への負担は小さく,頑健で素早く動作し,バグも少ない.
// 安心してガンガン使おう.


** cat [#r885a54b]

ファイルを連結するプログラムであるが,''「入力をほぼそのまま出力する」フィルタ''として使われることも多い.
&br;
&ref(/materials/warning.png); 
ファイル名を与えない,もしくは,ファイル名として "-" とすると標準入力を読込んでそのまま出力する.
そうしたフィルタとして使える.
// &br;&br;
// 数学で言えば「恒等作用素」である(だから重要だ!).
&br;&br;
// 応用としては,
オプションを使用して
「出力に行番号を与える」,
「空白行をまとめる」
「目に見えない文字を見えるようにする」
などの利用方法の他に,
''rsh'' (ネットワークの向こうのコンピュータを操作する命令)と組み合わせて
「ネットワークを経由してファイルをコピーする」
などがよくある使い道だ.
&br;
&br;
&ref(/materials/Gnome-Preferences.png); 実行例(1)

> ''ls␣-lg␣|␣cat␣-n␣|␣less''

とすると,出力結果に行番号を簡単につけられる.
&br;&br;

&ref(/materials/Gnome-Preferences.png); 実行例(2)

> ''cat␣file1␣file2␣>␣sum-file''

とすると,file1 の中身に file2 の中身を続けた結果を sum-file に出力できる(このようにファイルを連結するのが cat の本来の機能).
&br;&br;

&ref(/materials/Gnome-Preferences.png); 実行例(3)

> ''ls␣-lg␣|␣rsh␣hoge␣'cat␣>␣dummy.txt'''

とすると,ネットワーク先のマシン "hoge"
上の
"dummy.txt"
というファイルに出力結果を書込める(もちろん,''rsh'' を "hoge" というマシン上で実
行する設定や許可が必要である).


&br;&br;
** tee [#kb7f1e1a]

''出力結果を「標準出力」と「ファイル」の両方にコピー'' するフィルタだ.
&br;
より詳しくは
前回の授業の [[&ref(/materials/JNorth_arrow-right-sm.png); tee 解説部分>授業資料/04#tee]]
を見よう.
&br;
&br;
&ref(/materials/Gnome-Preferences.png); 実行例

> ''ls␣-lg␣|␣cat␣-n␣|␣tee␣dummy.txt␣|␣less''

とすると,出力結果に行番号をつけたうえで,ファイル
"dummy.txt"
に結果を書込みつつ,同時に画面で確認できる.
&br;&br;

** grep 一族 [#b4f98d57]

''与えた「パターン」を含む行を検索する'' フィルタである.
&br;
フィルタとしては,
&br;
CENTER:&ref(./grep-syntax.png,150%);

&br;
という使い方になる.
&br;
&ref(/materials/warning.png); 検索パターンは「基本正規表現」を使って表す.
正規表現についてはこのすぐ後
[[&ref(/materials/JNorth_arrow-right-sm.png); 正規表現>#regex]]
で学ぶ.
&br;&br;

&ref(/materials/warning.png); 
マニュアルを引くとオプションに "ファイル名" があるんだけど?
&br;
''grep'' は,起動時オプションにファイル名を与えるとそのファイルの中を検索する.
逆に,ファイル名を与えない, もしくは,
ファイル名として "-" をオプションで与えると,
標準入力を読込んでその中から検索する.
&br;
フィルタとしては後者の使い方になる.
&br;
&ref(/materials/warning.png); 多くのフィルタでオプションとしてのファイル
名の取扱いが同様である.

&br;&br;
&ref(/materials/Gnome-Preferences.png); 実行例
&br;
例えば,emacs を使っている状態でその pid を知るには,

> ''ps␣axu␣|␣grep␣emacs''

とすれば該当行だけが表示されるので簡単にわかる.
&br;
// この時,この検索行為そのものが検索結果に出るのがイヤだという場合は,
// > ''ps␣axu␣|␣grep␣'[0-9]␣emacs'''
// や
// > ''ps␣axu␣|␣awk␣'{print␣$2,$11}'␣|␣grep␣emacs''
さらに,pid だけを出力したければ,

> ''ps␣axu␣|␣grep␣emacs␣|␣awk␣'{print␣$2}'''

などとすればよい.
// ''[0-9]'' の意味は [[&ref(/materials/JNorth_arrow-right-sm.png); 正規表現>#regex]] で,
awk のこの使い方はすぐあとで学ぶ.
[[&ref(/materials/JNorth_arrow-right-sm.png); (おまけ) awk の機能のホンの一部分>#awkintro]].
&br;&br;
&ref(/materials/notes.png); 上の実行例を参考にして,
「狙ったプラグラムの pid を調べてそれを用いて kill する」
動作を 1行で行うにはどうしたらよいか考え,実際にやってみよう.
&br;
具体的には,
+ emacs を端末内で
> ''emacs␣-nw''
<
として起動.
+ emacs の中で ''c-z'' として(ctrl キーを押しながら z キーを押す) emacs を停止状態へ.
+ ここから,たった 1行でこの emacs を kill せよ!
もちろん,他のシェルや端末でこの emacs の pid を調べるのは反則だし,シェル組み込みの機能 ''kill %1'' を使うのも反則だ!
&br;
&ref(/materials/warning.png); 前回学んだ [[&ref(/materials/JNorth_arrow-right-sm.png); 便利! 先読み評価>授業資料/04#h8e8c8b1]] を思い出そう.

&br;&br;
&ref(/materials/NG.png); 注意: コンピュータ上で,何かを''目で探す''のは,
+ 間違えがち
+ 疲れる
+ 遅い

ため,&quot;eye ball search&quot; と呼ばれ,''避けるべき''
行為とされている.
このためにも,''grep'' をバンバン使おう.

&br;&br;

*** grep 一族の他の方々 [#v84aa22c]

grep 一族には他のプログラムもある.
慣れてきたら使い分けるとなにかと便利なので,頭の片隅にいれておこう.

| 氏名 | 性格 |h
|CENTER: ''egrep'' | パターンに「拡張正規表現」が使える(このすぐ後で学ぶ. [[&ref(/materials/JNorth_arrow-right-sm.png); 正規表現>#regex]]). |
|CENTER: ''fgrep'' | パターンを「単なる文字列」として扱う. つまり,正規表現「無し」バージョン.&br; &ref(/materials/warning.png); しかし,''fgrep'' は必ずしも高速ではない(fgrep の f は fast ではなくて fixed- の頭文字である).  |
|CENTER: ''zgrep'' | 圧縮したファイルを解凍してから,その中身を検索する. &br; ただし,この能力は環境に依存する. |


&br;&br;

** sort [#n5e10e44]

与えられた行を ''「順番に」並べ直す'' フィルタ.
&br;
''sort''
は比較的よく使うフィルタなので,そのオプションをマニュアルで調べ,覚えておくと良いだろう.

&br;&br;
&ref(/materials/Gnome-Preferences.png); 実行例 (1)

> ''ps&#9251;axu&#9251;|&#9251;sort&#9251;-k&#9251;11&#9251;|&#9251;less''

とすると(プロセス名が11番目に表示される場合)プロセスのリストを「プログラムの名前順に」,

> ''ps&#9251;axu&#9251;|&#9251;sort&#9251;-r&#9251;-k&#9251;11&#9251;|&#9251;less''

とやればプロセスのリストを「プログラムの名前の逆順に」みることができる.

&br;&br;
&ref(/materials/Gnome-Preferences.png); 実行例 (2)

> ''ls&#9251;-lg&#9251;|&#9251;sort&#9251;-nr&#9251;-k&#9251;4&#9251;|&#9251;less''

とすれば,ファイルを「サイズの大きな順に」並べて見られる.
HDD の容量が残り少なくて,無駄なファイルがないかな〜 と調べるときなどに有効だろう.

&br;&br;
&ref(/materials/notes.png); sort のマニュアルを読んで,どういうことが出来そうか把握しておこう.

&br;&br;
** uniq [#fcc7371a]

''隣り合った行が同じならば一行にまとめる'' フィルタである.

データに対して
''sort''
フィルタを通してから
''uniq''
フィルタを通すと,全体の重複を除去できるので,こうして使うことも多い.
&br;&br;
さらに,以下のオプションを使うと少し違うことが出来る.覚えておくとよいだろう.

- ''-c'' … 同じ行をまとめるだけでなく,「何回続いたか」も出力.
- ''-d'' … 「重複した行のみ」出力.
- ''-u'' … 「重複しなかった行のみ」出力.


&br;&br;
&ref(/materials/Gnome-Preferences.png); 実行例 (1)
&br;
/usr/bin と /usr/local/bin にあるファイルを重複なしで全部見るには,

> ''(ls&#9251;/usr/bin;&#9251;ls&#9251;/usr/local/bin)&#9251;|&#9251;sort&#9251;|&#9251;uniq&#9251;|&#9251;less''

とすればよい.
&br;
&ref(/materials/warning.png); ''sort'' and ''uniq'' のコンボはもうパターンなので覚えてしまおう.
&br;
&ref(/materials/warning.png); ''ls'' コマンドの挙動を alias で変更している人は,上の例の中の ''ls'' という部分を ''\ls'' と置き換えて実行しよう.
ちなみに,コマンドに ''\'' をつけて実行するのは,「alias があってもコマンドそのものを呼び出す」という意味である.
&br;
&br;
&ref(/materials/Gnome-Preferences.png); 実行例 (2)
&br;
逆に, 重複しているファイルをチェックしたければ

> ''(ls&#9251;/usr/bin;&#9251;ls&#9251;/usr/local/bin)&#9251;|&#9251;sort&#9251;|&#9251;uniq&#9251;-d''

とすればよい(このケースでは結果はおそらくゼロだろうけれども).

** tr [#lfb42ef6]

入力されたテキストの''「文字」の置換,削除,圧縮を行なう''フィルタ.
&br;
CENTER:&ref(./tr-syntax.png,150%);

&br;
として使う.
基本は「文字」単位での変換である.「文字列」ではないことに注意しよう.
// 古い文字集合の文字数と,新しい文字集合の文字数は同じにしておくのが基本.
// &br;
// オプションを変えれば,削除や連続する同じ文字を一文字にまとめることもできる.

&br;&br;
&ref(/materials/Gnome-Preferences.png); 実行例 (1)

> ''ls&#9251;-lg&#9251;|&#9251;tr&#9251;a-z&#9251;A-Z''

で,ls -lg の結果を,a → A, b → B, c → C … z → Z と変換して出力する.
つまり,全て大文字に置換する.

&br;&br;
&ref(/materials/Gnome-Preferences.png); 実行例 (2)
&br;
そこそこ長いテキストファイル "dummy.txt" に対して,

> ''cat&#9251;dummy.txt&#9251;|&#9251;tr&#9251;-cd&#9251;&apos;[:print:]&apos;''

とすると,印刷できない文字を全て消去して出力する.
この場合は例えば「改行」などが削られるはず.
&br;
&ref(/materials/warning.png); オプション -cd と ''文字クラス'' [:print:] についてはマニュアルを見ておこう.

&br;&br;
&ref(/materials/Gnome-Preferences.png); 実行例 (3)

> ''cat&#9251;dummy.txt&#9251;|&#9251;tr&#9251;-s&#9251;&apos;\n&apos;''

とすると,入力中の改行の繰り返しを一つにまとめる.
つまり,空行が消去される.

&br;&br;
&ref(/materials/Gnome-Preferences.png); 実行例 (4)
&br;
逆に処理しやすくするためにすべての項目をバラバラにすることもできる.例えば

> ''cal&#9251;|&#9251;tr&#9251;&apos;[:blank:]&apos;&#9251;&apos;\n&apos;''

とすると,''cal'' コマンドの出力がすべて行に分解される.
&br;
&ref(/materials/notes.png); 1年に ''○月31日'' は 7回あるはずであるが,試しにこれを使って確認してみよ.

&br;&br;
&ref(/materials/Gnome-Preferences.png); 実行例 (5) &ref(/materials/OK.png); 面白い例

例えば

> ''cat&#9251;dummy.txt&#9251;|&#9251;tr&#9251;'[:space:]'&#9251;'\n'&#9251;|&#9251;tr&#9251;-s&#9251;'\n'&#9251;|&#9251;sort&#9251;|&#9251;uniq&#9251;-c&#9251;|&#9251;sort&#9251;-nr&#9251;|&#9251;less''

とすると,入力ファイル dummy.txt 中の「単語」が出現回数と共に,出現回数の多い順に表示される.
&br;
&ref(/materials/notes.png); ''Zipf の法則'' について調べてみよ.
余裕があるならば,適当な英文を用意してこの法則が成り立つかどうか確認せよ.

&br;&br;
** head, tail [#i5df0a27]

入力の''最初/最後の 数行を出力''する.
&br;
動的に記録が追加されていくようなファイルの変化をチェックするには,''tail'' が結構役に立つ.

&br;&br;
** fold [#mce4e305]

入力された行を''決められた桁数で折り返し''て行分割して出力する.
デフォルトは 80桁.

&br;&br;
&ref(/materials/Gnome-Preferences.png); 実行例
&br;
例えば
&quot;dummy.txt&quot;
というファイルの中身をメールで送ろうと考えたとする.
そのメールが 1行あたり 25文字表示される携帯電話でどう見えるかを荒っぽくシミュレートしてみるには,

> ''cat&#9251;dummy.txt&#9251;|&#9251;fold&#9251;-w&#9251;25&#9251;|&#9251;less''

としてみればよい.

&br;&br;
** expand, unexpand [#n778196b]
''タブをスペースに変換''する(expand).
&br;
''スペースをタブに変換''する(unexpand).

&br;&br;
** pr, enscript [#j765175a]

入力を''プリンタに出力するのにヨサゲな形式に変換''する.

&br;&br;
&ref(/materials/Gnome-Preferences.png); 実行例(1)

> ''ps&#9251;axu&#9251;|&#9251;pr&#9251;|&#9251;less''

としてみると意味が分かるだろう. ただし, pr の能力はそんなに高くない.

&br;&br;
&ref(/materials/Gnome-Preferences.png); 実行例(2)
もし enscript コマンドがインストールされていれば,たとえば

> ''enscript&#9251;--pretty-print&#9251;--color&#9251;--line-numbers&#9251;--columns=2&#9251;--landscape&#9251;--fancy-header&#9251;-o&#9251;dummy.ps&#9251;dummy.txt''

とすると, dummy.txt を横二段組にして行番号をつけて,内容を色付けしたものが dummy.ps という Postscript 形式のファイルに出力される.
自分で書いたプログラムを印刷するときなどはとても便利なので覚えておこう.
また,enscript は Postscript だけでなく,html 形式などにすることもできる.


&br;&br;
** wc [#v0e98763]

入力データの''「大きさ」「単語数」「行数」を出力''する.
&br;
他のフィルタとの組み合わせて使うと意外に使い勝手が良い.

&br;&br;
&ref(/materials/Gnome-Preferences.png); 実行例

> ''ps&#9251;axu&#9251;|&#9251;wc''

とすると,行数・単語数・バイト数 が出力される.

&br;&br;
** nkf [#u42e9f00]

日本語入力の''漢字コードを変換''するフィルタ.
&br;
漢字コードについての簡単な解説は
[[&ref(/materials/JNorth_arrow-right-sm.png); 漢字コードについて:http://www.cas.cmc.osaka-u.ac.jp/~paoon/Lectures/2005-1Semester-InformationLiteracy/04.html#japanese-code]]
を参照せよ.
おおざっぱな使い方は
&br;
''nkf&#9251;-e'' … 入力を euc に変換して出力.
&br;
''nkf&#9251;-j'' … 入力を jis に変換して出力.
&br;
''nkf&#9251;-s'' … 入力を shift-jis に変換して出力.
&br;
''nkf&#9251;-w'' … 入力を UTF8 に変換して出力.
&br;

&br;&br;
&ref(/materials/Gnome-Preferences.png); 実行例
&br;
テキスト 'dummy.txt' が文字化けして読めない時,端末の表示漢字コードが EUC ならば

> ''nkf&#9251;-e&#9251;&lt;&#9251;dummy.txt&#9251;|&#9251;less''

とすれば読めたりする.

&br;&br;
&ref(/materials/warning.png); ただし…
&br;
テキストファイルの扱いは,漢字コードだけでなく,改行コードも OS によって異なる.
&br;
そのため,OS を越えてテキストファイルをやり取りする際には改行コードも変換する必要がある.
&br;
幸い,nkf には改行コードを変換する機能もついている.
詳しくはマニュアルを読むこと.

// &br;&br;
// ** compress, uncompress, zcat
// compress は,入力データを adaptive Lempel-Ziv 法を用いて圧縮して出力する.
// &br;
// uncompress は,入力データが圧縮されたデータならば元に戻して出力する.
// &br;
// (zcat は,圧縮されたファイルに変更を加えずに中身を閲覧するためのコマンド).
// &br;
// &br;
// 通常は compress によって圧縮されたファイルには
// .Z
// という拡張子を付加するのが慣例である.
// &br;
// &br;
// &ref(/materials/Gnome-Preferences.png); 実行例
// &br;
// ''ls -lg | compress > dummy.txt.Z''
// とすると,
// ls -lg の結果が圧縮されて dummy.txt.Z という名前のファイルになる.
// &br;
// 圧縮してない例
// ''ls -lg > dummy.txt''
// もやってみたあと,
// ''ls -lg dummy.txt*''
// などとしてファイルサイズがどれくらい小さくなっているか(圧縮されているか)
// 実際に見てみるとよい.

&br;&br;
** gzip, gunzip, zcat, bzip2, bunzip2, bzcat [#x9911601]

データを ''圧縮・解凍・閲覧'' する.
&br;
- gzip : 入力データを''圧縮''する. //Lempel-Ziv 77 法を用いて. compress よりも効率がよい.
- gunzip : 圧縮済みデータを ''解凍'' する(元に戻す).
- zcat : 圧縮済みデータの中身を ''閲覧'' する.
- b*: 上同様の機能を持つが,より高性能(圧縮形式が Burrows-Wheeler ブロックソート圧縮+ハフマン符号化で,gzip の Lempel-Ziv と違う).


&br;&br;
&ref(/materials/warning.png); gzip によって圧縮されたファイルには
.gz や .z
という拡張子を付加するのが慣例である.
bzip2 では
.bz2
という拡張子にすることが多い.

&br;&br;
&ref(/materials/Gnome-Preferences.png); 実行例(1)

> ''gzip&#9251;dummy.txt''

とすると, dummy.txt が圧縮されて dummy.txt.gz になる.
ただし,この方法だと dummy.txt は消去される.
&br;&br;

&ref(/materials/Gnome-Preferences.png); 実行例(2)

> ''gzip&#9251;-k&#9251;dummy.txt''

というように,オプション -k (=keep) をつけると, dummy.txt は消去されずに圧縮されて dummy.txt.gz という新たなファイルができる.


&br;&br;
&ref(/materials/notes.png); 適当に大きなテキストファイルなどを用意して,圧縮しない場合, gzip で圧縮した場合,(bzip2 が使えるなら) bzip2 で圧縮した場合,の結果を比較して,ファイルサイズがどれくらい小さくなっているか(圧縮されているか)実際に見てみよう.

&br;&br;
&ref(/materials/Gnome-Preferences.png); 実行例(3)

> ''ps&#9251;axu&#9251;|&#9251;gzip&#9251;>&#9251;dummy.gz''

とすると,ps axu の結果が圧縮されて dummy.gz という名前のファイルになる.
すぐに使わない結果などはこうして最初から圧縮しておくと保管しやすい.


&br;&br;
** tar [#s211dc12]

''複数のファイルをまとめて一つのデータに'' して出力する.
もちろん,後で元の複数のファイルに戻せる.

&br;&br;
&ref(/materials/Gnome-Preferences.png); 実行例(1)

例えば,そこにあるディレクトリ data をまるごと1つのファイル result.tar にしたい時は,

> ''tar&#9251;cvf&#9251;result.tar&#9251;data''

とすればよい.
ちなみに tar は圧縮はしないので,圧縮したければ上の gzip や bzip2 をこのまとめた結果に対して使えば良い.
&br;
また,こうしてできた result.tar というファイルを解凍したい場合は,

> ''tar&#9251;xvf&#9251;result.tar''

とすればよい.

&br;
&ref(/materials/warning.png); その他の通常の使い方はマニュアルを見よう

&br;&br;
tar をフィルタとして使う面白い応用として,
複数のファイルをディレクトリの構造ごと「移動」させる例がある.
&br;
&br;
&ref(/materials/Gnome-Preferences.png); 実行例(2)

> ''tar&#9251;cf&#9251;-&#9251;-C&#9251;ディレクトリA&#9251;.&#9251;|&#9251;tar&#9251;xpf&#9251;-&#9251;-C&#9251;ディレクトリB''

とすると,ディレクトリA の中身をそのままディレクトリ B へコピーできる.
パイプの間に
''rsh'' など
を挟めばネットワークを越えてディレクトリの構造をコピーできる.

&br;&br;
** One of functions of awk: (おまけ) awk の機能のホンの一部分 [#awkintro]
ここではとりあえず,入力行の ''項目を好きなように並べ直せる'' フィルタとして理解しておこう.
&br;
つまり,
スペースや ,(カンマ) で区切られたデータが連なる入力に対して,
区切られたデータを抜き出せる.
&br;
データは,左から $1, $2, ... という名前で扱える.
&br;
&br;
&ref(/materials/Gnome-Preferences.png); 実行例

> ''ps&#9251;axu&#9251;|&#9251;awk&#9251;&apos;{print&#9251;$2,&#9251;$11}&apos;''

などとやってみれば一瞬で理解できるはず.

&br;&br;
&ref(/materials/warning.png); awk は本来はプログラム可能な高機能フィルタである.あとで学ぶ.


&br;&br;
** cut [#e993a273]

区切られたデータが連なる入力に対して,''区切られたデータを抜き出す'' フィルタ.
&br;
上の awk と似ているが,awk より融通が効かない.

&br;&br;
&ref(/materials/Gnome-Preferences.png); 実行例

> ''ps&#9251;axu&#9251;|&#9251;cut&#9251;-f&#9251;2,11&#9251;-d&#9251;&quot;&#9251;&quot;''

とすると,「区切り文字 = スペース」として 2番目と11番目の項目を出力してくれるのだが,
上の awk の例と比較すると,「融通が効かない」という意味がわかるだろう.

&br;&br;
&ref(/materials/notes.png); 実習

上に示した実例を実際に行い,何が起きているのかよく理解せよ.

&br;&br;
* Regular expressions: 正規表現 [#regex]

正規表現とは,文字列の集合を表すために考えられたルールの一つである.
&br;
人間が文字列処理を行なうとき,指定したい文字列を人間に伝えるのは簡単だが,
機械に対して正確に指定するのが難しい,ということは良くあることだ.

&br;&br;
&ref(/materials/warning.png);  例えば,re で始まって,tion で終わる単語を文書中から探してくれ,
という指示をコンピュータにどうしたら伝えられるだろうか?
&br;&br;

こうした人間の要望を正確かつ厳密に表現する方法として,正規表現がある.

&br;&br;
&ref(/materials/warning.png); 正規表現そのもののマニュアルは,

- Linux の場合 … ''man 7 regex''
- FreeBSD の場合 … ''man re_format''

とすれば読める.

&br;&br;
■ 正規表現の式とその意味 ■

|CENTER: 基本 |CENTER: 拡張 |CENTER: 意味 |h
|>|CENTER: 通常文字 | メタキャラクタでない文字. その文字自身を表す. メタキャラクタとは, &br; 基本正規表現では \ ^ $ . [ ] * の7文字を, &br; 拡張正規表現では \ ^ $ . [ ] * + ? { } ( ) &brvbar; の14文字をいう. |
|>|CENTER: \m | メタキャラクタ m の意味を打消し,通常文字として扱う(エスケープという). |
|>|CENTER: ^ | 行頭を表す. |
|>|CENTER: $ | 行末を表す. |
|>|CENTER: . (ピリオド) | 任意の一文字を表す. |
|>|CENTER: [ ] | [ ] で囲まれた文字列中のどれか一文字を表す. &br; [ ] 中では特別に &quot;-&quot; のみがメタキャラクタとなり, 他のメタキャラクタは通常文字として扱われる. &br; &quot;-&quot; を通常キャラクタとして扱うには, &quot;---&quot; (ハイフンを三つ繋げる) と書けばよい. |
|>|CENTER: [ c1 - c2 ] | 文字 c1 から c2 までの範囲の文字中のどれか一文字を表す. |
|>|CENTER: [^  ] | [^  ] で囲まれた文字列中に「含まれない」一文字を表す. |
|>|CENTER: * | 直前の正規表現の 0 回以上の繰り返しを表す. |
|BGCOLOR(white):|CENTER: + | 直前の正規表現の 1 回以上の繰り返しを表す. |
|BGCOLOR(white):|CENTER: ? | 直前の正規表現が 0 回か 1回現れることを表す. |
|CENTER: \{ m \} |CENTER: { m } | 直前の正規表現の m 回の繰り返しを表す. |
|CENTER: \{ m, \} |CENTER: { m, } | 直前の正規表現の m 回以上の繰り返しを表す. |
|CENTER: \{m, n\} |CENTER: {m, n} | 直前の正規表現の m 回以上 n 回以下の繰り返しを表す. |
|CENTER: \( \) |CENTER: ( ) | 囲まれた部分をグループ化する. |
|>|CENTER: \N | N 番目のグループ化された正規表現が合致した結果(N= 1,2,..9). |
|BGCOLOR(white):|CENTER: &brvbar; | 直前と直後の正規表現の「どちらか」を表す |

&br;&br;
なお,GNU ソフトウェアには以下のような拡張も追加されている.
&br;&br;

|CENTER: GNU拡張 |CENTER: 意味 |h
|CENTER: \&lt; | 単語の先頭を表す. |
|CENTER: \&gt; | 単語の末尾を表す. |
|CENTER: \b | 単語の先頭か末尾を表す. |
|CENTER: \B | 単語の(先頭か末尾)以外を表す. |

&br;
&br;
&ref(/materials/Gnome-Preferences.png); 例(1)
&br;
例えば,

> ''re.*tion''

は「re で始まって,tion で終わる単語」に近い正規表現だ.
&br;
そのものでないのはなぜか? よく考えてみよう.

&br;&br;
&ref(/materials/Gnome-Preferences.png); 例(2) emacs で正規表現を用いてみる
&br;
&br;
例えば,ある web page をファイル d.html として保存する.
&br;
そして,このファイルから,html のタグ
(&lt; ... &gt; というやつ)
を除去する作業を考える.
&br;
この作業は,例えば emacs でやるには次のようにすればよい.

+ emacs でファイルを読み込む.
&br;
&br;
+ ''
M-x&#9251;replace-regexp
''
とする.
&br;
&br;
+ ミニバッファに
''
Replace regexp:
''
と出て,入力を要求される.
&br;
これは
「置換すべき対象」を正規表現で入力しろ,
ということである.
&br;
&br;
そこで,
''
&lt;[^&gt;]*&gt;
''
と入力して Return を押す.
&br;
&br;
+ ミニバッファに
''
Replace regexp &lt;[^&gt;]*&gt; with:
''
と出て,入力を要求される.
&br;
これは
「何に置換するのか」を正規表現で入力しろ,
ということである.
&br;
&br;
そこで,今回は単に消去したいので,
「空」を入力する(つまり,何も書かずに Return を押す).
&br;
&br;
+ すると?
&br;
&br;

&ref(/materials/notes.png); 実習

+ 上の例の作業を実際に行おう.
ただし,用いられる正規表現を理解しながら.

&br;&br;
* Programable filters: プログラマブル フィルタ [#fd2d3764]

単機能フィルタでは少々荷が重い複雑なフィルタ処理を行なうには,
そのままでは,
シェルスクリプトを作成するか,
通常言語でプログラムを組むなどの行為が必要となる.
&br;
しかし,シェルスクリプトは制限が強すぎて柔軟な処理は難しいし,
通常言語でフィルタの内容をプログラムするのは無駄が多い.
&br;
そこで,フィルタプログラム自身が複雑なフィルタ処理をプログラムできれば,
フィルタの便利な機能を生かしつつ,無駄無く複雑な処理が柔軟にできるというものだ.
これが「プログラミング可能な」フィルタの存在意義である.
&br;
&br;
こうしたプログラミング可能なフィルタプログラムとしては,
sed,
awk,
perl,
ruby
等が有名である.
本講義では, sed についてまず簡単に解説する.

* sed [#x247177d]

sed は,
1 行ずつ
''高速 かつ 正規表現の使える文字列置換ツール''
といってよい.

** How to use sed: sed の使い方(文法) [#w6af72b0]

フィルタとしては
&br;
&ref(./sed-syntax.png);
CENTER:&ref(./sed-syntax.png,150%);

&br;
というように用いる.
ここでいう「スクリプト」とは,sed の動作を指定するために(我々がこれから作る)プログラムのことである.
&br;
オプション
''-n''
についてはすぐあとで学ぶ.

&br;
&br;
** Actions of sed: sed の動作,スクリプトの基本 [#i2313291]

sed は入力を一行ずつ処理していく.
つまり,処理単位は「行」である.
そして,
sed のスクリプトは基本的に,

  処理対象となる行の指定 {
  コマンド
  コマンド
  コマンド
  コマンド
  }

という構造をしている.
&br;
コマンドが一つしかない場合は { } は不要だ.
&br;
さらに対象行の指定が不要である(つまり,全ての入力行を対象とする)場合は,

  コマンド
  コマンド
  コマンド
  コマンド

とコマンドだけを書き連ねてもよい.
&br;
&br;
そして,sed の動作は

- 処理対象となる行には… 全てのコマンドを適用してから,
- 対象外の行には…なにもしないでそのままにして,

どちらにせよ出力する というのが基本動作である.
&br;
&br;
&ref(/materials/warning.png); ただし,起動時に
オプション
''-n''
を使用した場合は,「出力を命令されたとき以外は」出力しなくなる.

// - 処理対象となる行には… 全てのコマンドを適用し,
// ''p''
// コマンドの時だけ出力する.
// - 対象外の行には…なにもしないで出力もしない.
// という動作になる.
まあ,とりあえず
&br;
「処理された行のみ見たい場合は,''-n'' オプションと,''p'' フラグか ''p'' コマンドを使う」
// 「''p'' コマンドと をペアで使用する」
&br;
と覚えておけば大丈夫だろう.

&br;&br;
** How to indicate the target lines in sed scripts: sed スクリプトでの処理対象行の指定方法 [#ce6009e0]

処理対象となる行を指定する方法は,

- ''行番号'' … 10 と書いたら 10行目のこと.
- ''/正規表現/'' … その正規表現を「含む」行(複数ありうる).
- ''$'' … 最終行.

の 3通り.
&br;
ただし,
''10, /win/''
のように二つの指定を ,(カンマ) で繋げて書いた場合には,
''10行目から  win を含む行まで'' という範囲指定になる.
&br;
&br;
** Commands of sed: sed スクリプトでのコマンド [#obcababe]

sed のスクリプトコマンドは以下の通り.

| コマンド | 意味 |h
| s/置換元パターン/置換新パターン/フラグ | 元パターン(正規表現可) に合致する文字列を新パターンで置き換える. &br; 置換新パターン中には,特別に ''&amp;'' というメタキャラクタも使える. これは置換元パターンに合致した文字列そのもの,を表す. &br;&br; フラグは以下の通り.&br; ''g'' … 行内の該当する文字列を「全て」置換. &br; ''N'' … 行内の該当する文字列の「N 番目」を置換. &br; ''p'' … 置換が行なわれたならば「表示」. |
| p | その行を必ず表示する.  |
| d | その行を削除する. |
| q | sed そのものを終了する. |

&br;&br;
&ref(/materials/Gnome-Preferences.png); 実行例 (1)

> ''ps&#9251;axu&#9251;|&#9251;sed&#9251;-e&#9251;'s/daemon/WhoAreYou?/g'&#9251;|&#9251;less''

とすると… 何が起こっているか?

&br;&br;
&ref(/materials/Gnome-Preferences.png); 実行例 (2)

さらに,

> ''ps&#9251;axu&#9251;|&#9251;sed&#9251;-n&#9251;-e&#9251;'s/daemon/WhoAreYou?/gp'&#9251;|&#9251;less''

とすると,上より分かりやすい.

&br;&br;
&ref(/materials/Gnome-Preferences.png); 実行例 (3)
&br;
たくさんあるファイルの名前を,一斉に機械的に変更したい.
例えば,*.text というファイルをすべて *.txt というファイル名に変更したいとする.
すると

> ''\ls&#9251;*.text&#9251;|&#9251;sed&#9251;-n&#9251;-e&#9251;&apos;s/\(.*\).text$/mv&#9251;&amp;&#9251;\1.txt/gp&apos;''

とすると,「その変更操作のための下準備」ができる.
&br;
具体的には,例えば a.text, b.text, c.text, ... というファイルがある
状態でこのコマンドを打つと,

  mv a.text a.txt
  mv b.text b.txt
  mv c.text c.txt

という結果が得られる.
&br;&br;
&ref(/materials/warning.png); 最初の ''ls'' の頭に ''\'' が付いている理由は,こうすると alias を無視して「素の」ls が起動されるからである.
これにより,(自分で設定した) alias によって入力が期待と違うという状況を避けることができる.
&ref(/materials/warning.png); ここでは念の為に ''ls'' の頭に ''\'' をつけている.
理由は上でも説明したが,こうすると alias を無視して「素の」ls が起動されるからである.
// これにより,(自分で設定した) alias によって入力が期待と違うという状況を避けることができる.
&br;&br;
さて,あとはこの「結果」をシェルスクリプトにしてもよいし,シェルそのものに渡しても良い.
&br;
例えば,

> ''\ls&#9251;*.text&#9251;|&#9251;sed&#9251;-n&#9251;-e&#9251;&apos;s/\(.*\).text$/mv&#9251;&amp;&#9251;\1.txt/gp&apos;&#9251;|&#9251;sh''

などとすると,一斉にファイル名を希望通りに変更できる.

&br;&br;
&ref(/materials/notes.png); 実習: 実習用にディレクトリを用意し,その中に実習用の
ファイルを幾つか作って,上の例を試してみよう.

&br;&br;
* Report: レポート [#e72f6f9b]

以下の課題について能う限り賢明な調査と考察を行い,
&br;
''AppliedMath7-Report-05''
&br;
という題名をつけて e-mail にて教官宛にレポートとして提出せよ. なお,レポートを e-mail の代わりに TeX で作成した書面にて提出してもよい.

** Exercises: 課題 [#f6fb40d9]

+ Tsushima か Tsusima Tushima か Tusima か,という文字列を含む行をデータの中から探すにはどうしたらよいか述べよ.
&br;
&br;
+ あるファイルの中身の行頭全てに
&quot; &gt; &quot;
という一文字を付け加えたい. どうすればよいか.
&br;&br;
+ あるファイル中で,年月日が全て
''
2012.05.18
2013.05.18
''
というような形式で記述されている.
&br;
これを全て
''
18/May/2012
18/May/2013
''
という形式に修正したいが,どうすればよいか(もちろん 1月は Jan. に,2月は Feb. に…と全部直すのである).
&br;&br;
+ (余裕のある者向け) 普通の文章を,「関西弁風」に変換する方法を考えよ.
&br;&br;
+ (余裕のある者向け) データベースを用いる通常の方法をやめ,大規模な業務用システムをフィルタを使う単純な仕組みに直してしまっている例 [[&ref(/materials/JNorth_arrow-right-sm.png); システム統合にSOA? RDBMS? bashで十分!:http://www.atmarkit.co.jp/news/200909/07/lltv03.html]] がある.読んでみて,感想があれば書こう.


&br;&br;
* about Icons, ClipArts [#ld887acf]

For details, see [[&ref(/materials/JNorth_arrow-right-sm.png); this>materials]].

// ━┃┏┓┛┗┣┳┫┻╋


// コマンドライン入力は「行頭を > で始める」.
// コマンドライン出力は「行頭をブランクで始める」.

// 実習アイコン
// &ref(/materials/notes.png);

// 注意アイコン
// &ref(/materials/warning.png);

// Link アイコン
// &ref(/materials/JNorth_arrow-right-sm.png);

// OK アイコン
// &ref(/materials/OK.png);

// NG アイコン
// &ref(/materials/NG.png);

// サンプルアイコン
// &ref(/materials/Gnome-Preferences.png);

// 大文字での強調
// CENTER:&size(24){''ほげほげ''};

// programu source 表記
// #highlighter(language=ruby,number=on,cache=on){{}}

// パイプ
// &brvbar;