この記事は ゆめみ Advent Calendar 2017 の 16 日目の記事です。
Linuxのパイプを理解して、Linuxをコマンドを便利に使いこなそうとする記事です。
Linuxのプロセスの入出力
パイプはプロセス間の入出力を繋げる仕組みなので、まずは、プロセスの入出力を知る必要があります。
標準ストリーム
プロセスは、明示的にファイルをオープンしなくても利用できる入出力があります。
- 標準入力 (stdin)
- 標準出力 (stdout)
- 標準エラー出力 (stderr)
Javaでは System.in
、 System.out
、 System.err
。PHPでは STDIN
、STDOUT
、 STDERR
で扱えるやつです。
C言語で割り当てられるファイルディスクリプタ番号は 0番が標準入力、1番が標準出力、標準エラー出力が2番です。そして、標準入力はキーボード、標準出力と標準エラー出力はモニータが接続されています。
上の図は wikipedia の 標準ストリーム にあるものです。
無名パイプ
無名パイプは、コマンド間の入出力を繋げるときによく使う |
です。これを使うことで |
の左側のコマンドの標準出力を右側の標準入力に繋げる仕組みです。
例えば ls | more
は下の図になります。
ls
の#1と more
の #0 をつなげているのがパイプです。ls
コマンドは ls(1) を見ると下のように書かれています。
出力は標準出力に対して行われ、 -C オプションで複数列出力が要求されない限り、1 行に 1 エントリである。 しかし、端末に対する出力では、出力が 1 列または複数列の どちらになるかが定められていない。 オプション -1 と -C は、 それぞれ 1 列出力と複数列出力を強制させるために使用される。
パイプで処理したいコマンドがあるときには、manを見て、入出力を確認すると良いです。
複数のパイプを利用することもできるので、ファイル名に qiita を含むファイル名を数えるために ls |grep 'qiita' |wc -l
を実行すると下のようになります。
名前付きパイプ
無名パイプは使い終わると廃棄されますが、名前付きパイプは mkfifo
コマンドで ファイルシステムに作ることができます。作成した名前付きパイプはリダイレクトを利用して、標準入出力と繋げることができます。
$ echo "Regular file" > regular_file
$ mkfifo named_pipe
$ ls -l
total 4
prw-rw-r-- 1 user1 group1 0 Dec 14 03:22 named_pipe
-rw-rw-r-- 1 user1 group1 13 Dec 14 03:21 regular_file
$
上では、 named_pipe という名前付きパイプを作成しています。ls -l
で(左端の「p」だから)ファイルタイプがパイプということが分かります。
この名前付きパイプを使って ls -l |more
と同等のことを行なってみます。
二つのターミナルを使い、一つ目のターミナルでは、 named_pipe
を作成したディレクトリで 下のコマンドを実行します。
$ more named_pipe
実行した直後は何も動きがありませんが、そのままにしておきます。more
コマンドは第1引数に指定したファイルを読み込みます。今回はnamed_pipe
を指定したので、名前付きパイプ named_pipe
からの読み込みを待っている状態です。
二つ目のターミナルでは named_name
を作成したディレクトリで下のコマンドを実行します。
$ ls -l /usr/bin/ > named_pipe
ls -l
の結果を事前に作成した名前付きパイプ named_pipe
に書き込みます。そうすると、 一つ目のターミナルに more
コマンドの結果が表示され、1ページを表示すると待機します。あとは、スペースキーでページ送りやq
でmoreを終了できます。
名前付きパイプの使い所
処理結果の書き込み先はコマンドごとにオプション指定があります。
- mysqldumpは --result-file (-r)でファイルを指定します。オプションが無ければ標準出力です。
- pg_dumpは --file(-f) でファイルを指定します。オプションがなければ標準出力です。
- tar は
c
オプションで指定するファイル名に-
を指定すると標準入力、標準出力を使います。 - zip は ファイル名に
-
を指定すると、標準出力です。
これらの様にオプションで標準出力を指定できるコマンドもありますが、そうではないコマンドもあります。古いコマンドで恐縮ですが、 Oracleの ダンプファイルを作成するexp
は標準出力に出力することができませんでした。そういう時に名前付きパイプを使いました。
$ cat named_pipe | zip > db_hoge.dmp.gz &
$ exp user_name file=named_pipe full=y
先に、バックグラウンドでzipコマンドを起動しておき、exp
コマンドの file
オプションに名前付きパイプを指定しています。
インポートする時はバックグラウンドで unzip
を行い、名前付きパイプに書き込みます。imp
ユーティリティは名前付きパイプを読み込みます。
$ cat db_hoge.dmp.gz | unzip - > named_pipe &
$ imp user_name file=name_pipe
ディスクを節約するためにお世話になりました。