Bashのストリーム/パイプ処理を視覚的に理解する

Bash, Linux

はじめに

今日、サーバOSとしてLinuxが占める割合は非常に多いです。
Linuxサーバへログインして作業をすすめるにあたり、 Bash に上手に仕事を依頼できるかで生産性に大きな違いが出てきます。なぜなら、多くのLinuxサーバに標準搭載されているUIは、Bashだからです。

現在携わっているサービスのサーバも例に漏れずLinuxが利用されています。

"ストリーム/パイプ"を理解すれば"Bash"が理解できる

"ストリーム/パイプ"を理解すれば"Bash"が、そして"Bash"の 便利さ が理解できます。

ですが、"Bash" のコマンドは処理の複雑さの割に短く、理解が進むまでは非常に敷居の高い仕組みになっています。
そこで、 "ストリーム/パイプ芸" 達を視覚化してみることにしました。

以降、 "ストリーム/パイプ芸" たちをサンプルコードとともに掲載します。
サンプルコードの意味は説明を最小限にしますが、替わりに視覚化していきます。

当エントリの最後に"ストリーム/パイプ芸"を大変わかりやすく紹介しているページのURLを紹介しておきます。

図の見方

登場人物 は以下のものたちです。

"ストリーム/パイプ" パターン紹介

1.単純に実行(非ストリーム/パイプ)

まずはじめに ストリーム/パイプを利用していない例 から。
出力結果を次のコマンドに渡すためには、一時的にファイルに書き出す必要があります。

サンプルコード

$ C1 > S12.txt
$ C2 < S12.txt > S23.txt
$ C3 < S23.txt

具体例

$ seq 1 100             > S12.txt
$ sort -rn  < S12.txt   > S13.txt
$ cat -n    < S13.txt

視覚化してみました

2.単純なパイプ

比較的よく実行するタイプです。
データも左から右へと流れるため、直感的に理解し易いです。

サンプルコード

$ C1 | C2 | C3

具体例

$ seq 1 100| sort -rn| cat -n

視覚化してみました

3.T字型出力パイプ

コマンドをパイプでつなぎ、段階的に出力結果を加工していきたい場合があります。
その場合、加工途中の内容を残していきたい場合があります。(ex: 中間データを別の目的で利用する / 中間データに問題がないかを確認するために出力しておきたい)

teeコマンドは上記のような場合にしばしば利用されます。

サンプルコード

$ C1| tee S12.txt| C2| tee S23.txt| C3

具体例

$ seq 1 100| tee S12.txt | sort -rn| tee S23.txt| cat -n

視覚化してみました

4.名前付きパイプ

mkfifo コマンドを使って、FIFO(First In First Out)形式のデータファイルを作成することが可能です。

データの送信元と受信元で実行タイミングに同期が取れない場合に便利です。

サンプルコード

# パイプファイルを作成
$ mkfifo S12.txt
$ mkfifo S23.txt

# パイプファイル"S12"へ出力
$ C1 > S12.txt

# パイプファイル"S12"から入力し、 パイプファイル"S23"へ出力
$ C2 < S12.txt > S23.txt

# パイプファイル"S23"から入力
$ C3 < S23.txt

具体例

# パイプファイルを作成
$ mkfifo S12.txt
$ mkfifo S23.txt

# パイプファイル"S12"へ出力
$ seq 1 100 > S12.txt

# パイプファイル"S12"から入力し、 パイプファイル"S23"へ出力
$ sort -rn < S12.txt > S23.txt

# パイプファイル"S23"から入力
$ cat -n < S23.txt

視覚化してみました

5.プロセス置換

Bashでは、入力情報を3つ以上の出力先に流すことも可能です。

サンプルコード

$ C1| tee >(C2 > S2.txt) >(C3 > S3.txt)

具体例

$ seq 1 100| tee >(sort -rn > S2.txt) >(sort -rn| cat -n > S3.txt)

視覚化してみました

6. 複数の入力情報を処理する

今までは1つの入力情報を処理する例でしたが、2つ以上の入力情報を処理することもできます。
前回同様プロセス置換を利用します。

サンプルコード

$ C1 <(C2) <(C3)| C4

具体例

$ sort -nr <(seq 1 100) <(seq 101 200)| cat -n

視覚化してみました

7.あるDBサーバから別のDBサーバへデータをコピー

実業務でもたまに行うことがある作業です。
sshで接続可能なサーバA/B間でデータのやり取りを行う例です。

このあたりのコマンドが使えると、のんびりトイレにいっている間に一仕事終わらせることができます。

サンプルコード

$ ssh anyone@server_a "pg_dump ... |C1 |C2"| \
        ssh anyone@server_b "C3| C4| psql ..."

具体例

各DBの接続情報は以下のようになっているものとします。

文字列 説明
from_user fromサーバーユーザ
from_server fromサーバーホスト
from_dbuser fromDBユーザ
from_dbname fromDB名
------------- --------------------
to_user toサーバーユーザ
to_server toサーバーホスト
to_dbuser toDBユーザ
to_dbname toDB名
$ ssh from_user@from_server "pg_dump -c -U from_dbuser from_dbname | gzip -c" | \
  ssh to_user@to_server "gunzip -c | psql -U to_dbuser to_dbname"
  • 事前にsshする両サーバーに、ssh-keygenで秘密鍵を作っておくとパスワード入力に悩まされません。
  • sshのパスワードが解消されたとしても、pg_dumpコマンドやpsqlコマンドのパスワードの入力問題も残りますが、ここでは触れません。

視覚化してみました


参考資料

Bash, Linux