(準備) この実習で使うファイルを smallfiles.tar.gz に入れてあるので、ダウンロード・展開しておいてください。
tarファイルには以下のテキストファイルが含まれています。
ruler.txt
hello.txt
wrong.txt
pen.txt
pencil.txt
num.txt
what.txt
フィルタとは
- 標準入力から入力データを読み取り、
- しかるべき処理を行って、
- 標準出力にデータを書き出す
という形で動作する、比較的小さな、単純に定義される処理を行うプログラムです。
(注) 単独で動作する場合
- 標準入力は、キーボード
- 標準出力は、ディスプレイ
に割り付けられていますが、リダイレクションを行うと、それぞれがファイルに割り付け直されます。
また、二つのプログラムをパイプでつなぐときには、パイプの前のプログラムの標準出力がパイプの後のプログラムの標準入力となります。
このように入出力を切り替える仕組みは、Unixに限らずいろいろなオペレーティングシステムが備えていますが、Unixの特徴は、フィルタの処理の対象としてテキストファイルを用いることにより、独立性の高い汎用的なプログラム (それがフィルタ) を組み合わせて複雑な処理を構成するというアプローチを意識的に採用しているところではないかと思います。入出力がテキストファイルだと、人間が目で見てフィルタによる加工の状況を確認することができるので、順々 (incremental) に処理を組み立てていくことができます。
以下では、代表的なフィルタをいくつか見ていきます。なお、ここではどのUnix/Linuxでもほぼ共通に利用できるフィルタを取り上げていますが、オプションが微妙に違うことがありますので、変だなと思ったらman pageを参照してください。
catは「ファイルを画面に表示する」コマンドとして使われることが最も多いのですが、実際には「入力を出力にコピーする」フィルタです。ただファイルの内容をcatに与えるときにいちいちリダイレクトを使って
$ cat < filename
とするのは面倒なので、入力ファイル名を引数として指定できるようになっています。つまり、上のコマンドは、
$ cat filename
と同じことになります。ファイル名を引数として与えてcatを実行するとそのファイルが出力にコピーされることになります。出力が画面につながっている場合 (つまり出力がリダイレクションやパイプになっていない場合) には画面に表示されることになるという訳です。さらに、入力ファイルを複数指定した場合は、それらをつなげて出力します。
以下は余談:"cat"は猫ではなく "catenate"の略として名付けられたということになっています。"catenate"は小さな辞書には出ていませんが「鎖状につなぐ」という意味です。(似た意味の"concatenate"は知っているのではないでしょうか。)こういうのを面白いと見るか、わかりにくいと見るか、好き嫌いが分かれるところです。
ファイル名を引数として与えたときには、そのままだと標準入力は読み込まれないようになりますが、ファイル名のところに'-'を使うとそこに標準入力から読み込んだものが入ります。
例: ファイル ruler.txt
を使います。ここでは example.tex
や、そこから生成した原稿などの入っているディレクトリで作業しています。
$ cat ruler.txt
----+----+----+----+----+----+----+----+----+----+----+----+----+----+
$ ls -l example.* | cat ruler.txt - ruler.txt
----+----+----+----+----+----+----+----+----+----+----+----+----+----+
total 40
-rw-r--r-- 1 ichii ichii 8 May 27 00:04 example.aux
-rw-r--r-- 1 ichii ichii 1704 May 27 00:04 example.dvi
-rw-r--r-- 1 ichii ichii 3286 May 27 00:04 example.log
-rw-r--r-- 1 ichii ichii 3866 May 27 00:04 example.pdf
-rw-r--r-- 1 ichii ichii 1097 May 27 00:04 example.tex
----+----+----+----+----+----+----+----+----+----+----+----+----+----+
'example.*' を取り除いたものを試してみましょう。
単にコピーするだけでなく、途中に簡単な加工を加えるオプション もあります。
-n : 各行に行番号を付け加える (やってみよ)
trは入力の各文字を文字毎に別の文字に変換 ("TRanslate") して出力するフィルタです。
例えば:
$ cat hello.txt
hello, world
$ tr h H < hello.txt
Hello, world
$ tr hw HW < hello.txt
Hello, World
$ tr hwo HWO < hello.txt
HellO, WOrld
つまり基本的な使い方は
tr 文字列1 文字列1
とすると、文字列1の1文字目から文字列2の1文字目、文字列1の2文字目から文字列2の2文字目というように対応する文字の変換が (標準入力に対して) 行われる訳です。他の多くのフィルタと異なり、ファイル名を引数としてとることはできません。(manページで確認すること。)
文字を8進文字コードで表現することもできます。8進コードはman ascii
で見られます。
$ tr h '\110' < hello.txt
Hello, world
(歴史的な理由で10進や16進でなく8進数で表わします)
文字列を範囲で示すこともできます。
$ tr a-z A-Z < hello.txt
HELLO, WORLD
もし文字列2のほうが文字列1より短ければ、文字列1のあふれた文字には文字列2の最後の文字が対応します。
$ tr a-z @ < hello.txt
@@@@@, @@@@@
trにもいくつかオプションがあります。
-d : 文字列1に現れる各文字を消去する。この場合、文字列2は指定しない。
-c : (下で使うのでマニュアルで確認してください)
-s : (同上)
例:
$ tr -d ,' ' < hello.txt
helloworld
trは少し癖がある印象がありますが、うまく使うと意外な力を発揮します。
grepは「入力の中にあるパターンを含む行があったら、その行を出力する」という機能を提供するフィルタです。一つ目の引数がパターンで、catと同様にして検索対象にしたいファイル名を二つ目以降の引数として指定できます。二つ目以降の引数がなければ、標準入力を検索します。
たとえば、"that"をいつも"taht"と打ってしまう癖がある人が、wrong.txt
ファイルの中にあるその間違いをチェックするには
$ grep taht wrong.txt
What is taht?
パターンとしては、単純な文字列だけでなく、複数のパターンを同時に検索することもできます。上の例で、文頭の"Taht"も同時に検索するには
$ grep '[tT]aht' wrong.txt
Taht is a cat.
What is taht?
のようにします。(パターンをシングルクオート ' '
で囲んでいるのは、複数の文字からの選択を表す [ ]
の記号が、シェルによって解釈されてしまうのを防ぐためです。)
grepで用いられるような、複数の文字列にマッチする可能性のある拡張されたパターンを正規表現 (regular expression) と呼びます。正規表現については後ほど扱う予定です。
実際、grepの名前の由来は、昔使われていたエディタedなどで、ファイル全体から正規表現を検索して表示するコマンド
g/reg/p
("reg"に正規表現が入る。g for global, p for print) に由来しています。
grepの仲間にはfgrep, egrepなどがあり (Linuxで使われるGNU grepではオプションによって区別される)、受け付けるパターンが微妙に違いますが、当面grepを使えれば十分でしょう。
いろいろなオプションがありますが、よく使うものとして:
-v : パターンを含む行の代わりに「含まない行」を出力する
-n : 行を出力する際に行番号を一緒に出力する
sortは入力の行を指定した順番に並べ替える機能を持ちます。catやgrepと同様に入力ファイル名や、ルールを指定するオプション指定文字列を引数として与えることができます。
何もオプションを指定しないと、sortは行全体を比較して、その文字コードの大小順に基づいて、行を小さい順に並べます。
$ cat < pen.txt
this
is
a
pen
$ sort < pen.txt
a
is
pen
this
文字コード順だと、A < B < ... < Z < a < b < ... < zと並んでいる (man ascii
で確かめること) ので、大文字小文字を混ぜて並べ替えたいときには都合がよくありません。そのときには、"-f
"オプションを使います。 (GNU sortの場合。他のUnixでは違うかも。)
$ cat pencil.txt
This
is
a
pencil
$ sort pencil.txt
This
a
is
pencil
$ sort -f pencil.txt
a
is
pencil
This
また、数値データを、文字列としてでなく、数値の大小で並べたい場合には"-n
"オプションを使います。
$ cat num.txt
2
1
10
$ sort num.txt
1
10
2
$ sort -n num.txt
1
2
10
逆順にするには、いずれの場合でも、"-r
" (for "reverse") オプションを加えます。
$ sort -n -r num.txt
10
2
1
$ sort -nr num.txt
10
2
1
二番目の例のように、オプションをくっつけても大丈夫です。その他のオプションについては、"man sort
"を参照してください。
sortとよく一緒に使われるフィルタにuniqがあります。隣り合った行を調べ、重複を除去する (uniqueにする) 機能を提供します。
$ cat what.txt # 元のファイル
This is a pen.
What is this?
$ cat what.txt | tr A-Z a-z | tr -cs a-z '\012' # 大文字を小文字に直し、それ以外の文字 (正の個数連続する 空白・改行・句読点を1つの改行へまとめる)
this
is
a
pen
what
is
this
$ cat what.txt | tr A-Z a-z | tr -cs A-Za-z '\012' | sort # 文字コード順に並べる
a
is
is
pen
this
this
what
$ cat what.txt | tr A-Z a-z | tr -cs A-Za-z '\012' | sort | uniq # 重複を除去
a
is
pen
this
what
"-c"オプションを使うと、同じ行がいくつあったかを教えてくれます。
sort | uniq -c | sort -n
のパイプラインで、単語の出現頻度調査ができます。
$ cat what.txt | tr A-Z a-z | tr -cs A-Za-z '\012' | sort | uniq -c | sort -nr
2 this
2 is
1 what
1 pen
1 a
入力の一部だけを切り出してきて表示するものです。
head -n
先頭からn行のみ取り出すtail -n
末尾からn行のみ取り出す
今までのフィルタと同様に入力ファイル名を引数として与えることができます。長いファイルはcatすると画面上で流れていってしまいますが、headやtailをうまく使うとスマートに情報を取り出せます。また、tailには"-f
"というオプションがあります。実行中のプログラムが今まさに生成しているファイルの末尾を追いかけるのに使います。たとえば長い数値計算をするプログラムがあって、その出力をモニターしたい場合とか、システムが生成するログ ("/var/log
"にある) をモニターするのに便利です。
テキスト入力の行数、単語数、バイト数を表示します。 行数、単語数、バイト数の順に表示されます。
ふたつのファイルの違いを表示します。比較したいファイル名を二つ引数として与えますが、片方を '-' にすることで標準入力からのデータを使うように指定して、フィルタとして使うことができます。
(実習) 次のコマンドの列を実行し、何をやっているか考えよ。知らないコマンドやオプションはmanで調べること。
$ ls -s /usr/share/info | tail -n +2 | sort -n
このページの内容を作成するにあたっては、久野靖『UNIXによる計算機科学入門』改訂2版, 丸善 (2004) を参考にしました。