トップ > Linuxらしい使い方を覚える >
パイプラインで複数のコマンドの流れをつなぐ

  

パイプラインを使ってベルトコンベアのように次のコマンドに情報を受け渡す

この記事では、シェルの機能である "パイプライン" について解説します。 パイプラインとは、コマンドの標準出力を次のコマンドの標準入力へ渡すためのベルトコンベアのような機能です。

なお、前の記事と同じく "UNIX系OSでは何でもファイルとして扱う" ということにも触れています。

では、パイプラインを使ってみましょう。 今回は、USBフラッシュメモリの抜き挿しを通してパイプラインを利用してみます。 ただし、USBフラッシュメモリはまだ挿さないでください

それでは端末を開き、キーボードから ls /dev と入力して、Enterキーを押してください。


ls /dev

 

以下のようにディレクトリ /dev にあるファイルの一覧が表示されます。

taro@myhostname:~$ ls /dev
autofs           i2c-7         rfkill    tty19  tty48   ttyS19     vcs5
block            i2c-8         rtc       tty2   tty49   ttyS2      vcs6
bsg              i2c-9         rtc0      tty20  tty5    ttyS20     vcs7
btrfs-control    initctl       sda       tty21  tty50   ttyS21     vcsa
bus              input         sda1      tty22  tty51   ttyS22     vcsa1
char             kmsg          sda2      tty23  tty52   ttyS23     vcsa2
console          kvm           sda3      tty24  tty53   ttyS24     vcsa3
core             log           sda4      tty25  tty54   ttyS25     vcsa4
cpu              loop-control  sda5      tty26  tty55   ttyS26     vcsa5
cpu_dma_latency  loop0         sda6      tty27  tty56   ttyS27     vcsa6
cuse             loop1         sda7      tty28  tty57   ttyS28     vcsa7
disk             loop2         sda8      tty29  tty58   ttyS29     vcsu
dma_heap         loop3         sg0       tty3   tty59   ttyS3      vcsu1
dri              loop4         shm       tty30  tty6    ttyS30     vcsu2
drm_dp_aux0      loop5         snapshot  tty31  tty60   ttyS31     vcsu3
drm_dp_aux1      loop6         snd       tty32  tty61   ttyS4      vcsu4
drm_dp_aux2      loop7         stderr    tty33  tty62   ttyS5      vcsu5
ecryptfs         mapper        stdin     tty34  tty63   ttyS6      vcsu6
fb0              mcelog        stdout    tty35  tty7    ttyS7      vcsu7
fd               mei0          tpm0      tty36  tty8    ttyS8      vfio
full             mem           tty       tty37  tty9    ttyS9      vga_arbiter
fuse             mqueue        tty0      tty38  ttyS0   ttyprintk  vhci
hpet             net           tty1      tty39  ttyS1   udmabuf    vhost-net
hugepages        null          tty10     tty4   ttyS10  uhid       vhost-vsock
hwrng            nvram         tty11     tty40  ttyS11  uinput     zero
i2c-0            port          tty12     tty41  ttyS12  urandom    zfs
i2c-1            ppp           tty13     tty42  ttyS13  userio
i2c-2            psaux         tty14     tty43  ttyS14  vcs
i2c-3            ptmx          tty15     tty44  ttyS15  vcs1
i2c-4            ptp0          tty16     tty45  ttyS16  vcs2
i2c-5            pts           tty17     tty46  ttyS17  vcs3
i2c-6            random        tty18     tty47  ttyS18  vcs4
taro@myhostname:~$

ディレクトリ /dev のこれらのファイルは、前の記事で登場した "デバイスファイル" です。 つまり、キーボードやマウス、ハードディスクやUSBフラッシュメモリなどの機器に対応したファイルです。

ただ、ファイルの数が多すぎてやや見づらいです。 ハードディスクとUSBフラッシュメモリのデバイスファイルだけが表示されるように絞り込みましょう。 キーボードから ls /dev | grep sd と入力して、Enterキーを押してください。


ls /dev | grep sd

 
  
|(パイプ) は、SHIFT+ "¥"の刻印のあるキーで入力することができます。

以下のように "sd" という文字を含むデバイスファイルだけが表示されます。

taro@myhostname:~$ ls /dev | grep sd
sda
sda1
sda2
sda3
sda4
sda5
sda6
sda7
sda8
taro@myhostname:~$

このように "sd" という文字を含むデバイスファイルだけに絞り込むことができました。 絞り込むための役割を果たしたのが | grep sd の部分です。

まずは |(パイプ) の部分ですが、これは前後のつのコマンドをつなぐためのものです。 | の前のコマンドの標準出力を | の後ろのコマンドの標準入力として渡します。 つまり、ls /dev のコマンドの実行結果を grep sd の入力として渡していることになります。

grep はコマンドであり、機能は条件に一致した行の表示です。 今回は grep コマンドの引数に sd を渡していますので、"sd" というテキストを含む行だけが表示され、その他の行は消去されます。

わかりやすく表現すれば、

ls /dev | grep sd

というコマンドは、/dev の下のファイルのうち "sd" を含むものだけ表示する、ということになります。

sd という名前で始まるデバイスファイルについて

"sd" という名前で始まるデバイスファイルは、ハードディスク または USBフラッシュメモリに対応しているデバイスファイルです。

最初に見つかったハードディスク(またはUSBフラッシュメモリ)が sda に、2番目に見つかったハードディスク(またはUSBフラッシュメモリ)が sdb に対応します。

ハードディスクやUSBフラッシュメモリは、内部に "パーティション" と呼ばれる区画を持ちます。 sda1 は1台目のハードディスク(またはUSBフラッシュメモリ)の最初のパーティションのことで、sda2が2番目のパーティションのことです。 同様に sdb3 は2台目のハードディスク(またはUSBフラッシュメモリ)の3番目のパーティションを表します。

ではここで、USBフラッシュメモリがマウント(連結)されるディレクトリである "/media/taro" を見てみましょう。 キーボードから ls /media/taro と入力して、Enterキーを押してください。


ls /media/taro

 

以下のように /media/taro ディレクトリには何もありません。

taro@myhostname:~$ ls /media/taro
taro@myhostname:~$

USBフラッシュメモリはまだ挿していませんので当然の結果です。


では、USBフラッシュメモリをPCに挿してください

1. 開いたファイルマネージャを終了する
1. 開いたファイルマネージャを終了する

上図のようにファイルマネージャが開きますので、右上の[X]ボタンを押して終了してください。

では、再度デバイスファイルを見てみましょう。 キーボードから ls /dev | grep sd と入力して、Enterキーを押してください。


ls /dev | grep sd

 

以下のように sdb および sdb1 が新たに増えています。

taro@myhostname:~$ ls /dev | grep sd
sda
sda1
sda2
sda3
sda4
sda5
sda6
sda7
sda8
sdb
sdb1
taro@myhostname:~$

このように、USBフラッシュメモリを挿したことでデバイスファイルが増えました。 sdb がUSBフラッシュメモリそのもののデバイスファイルであり、sdb1 がその中の最初のパーティションのデバイスファイルです。

では、USBフラッシュメモリがマウント(連結)されるディレクトリ "/media/taro" も、もう一度見てみましょう。 キーボードから ls /media/taro と入力して、Enterキーを押してください。


ls /media/taro

 

以下のようにUSBフラッシュメモリが出現しました。

taro@myhostname:~$ ls /media/taro
YAMADA_4G
taro@myhostname:~$

これは、USBフラッシュメモリがディレクトリツリーにマウント(連結)されたことを表しています。

では続いて、USBフラッシュメモリの内容を見てみましょう。 キーボードから ls /media/taro/YAMADA_4G と入力して、Enterキーを押してください。


ls /media/taro/YAMADA_4G

 

以下のようにUSBフラッシュメモリの内容が表示されます。

taro@myhostname:~$ ls /media/taro/YAMADA_4G
'System Volume Information'   対象のファイル.txt
taro@myhostname:~$

対象のファイル.txt という名前のファイルがあることがわかります。


では、USBフラッシュメモリをアンマウント(連結解除)しましょう。 ただし、USBフラッシュメモリはまだPCからは抜きません

2. "取り出し" を実行する
2. "取り出し" を実行する

上図のようにデスクトップのUSBフラッシュメモリのアイコンをマウスの右ボタン(マウスの右ボタン)でクリックし、表示されるメニューの"取り出す(E)"を実行します。

3. 安全に取り外しできるようになる
3. 安全に取り外しできるようになる

上図のように "安全に取り外しできます" というメッセージが表示されますが、まだ抜かないでください

USBフラッシュメモリを抜く前に、アンマウントされていることを確認しましょう。 キーボードから ls /media/taro と入力して、Enterキーを押してください。


ls /media/taro

 

以下のようにUSBフラッシュメモリは見えなくなりました。

taro@myhostname:~$ ls /media/taro
taro@myhostname:~$

アンマウント(連結解除)されましたから、見えなくなるのは当然の結果です。

では次に、デバイスファイルがどうなっているかも確認しておきましょう。 キーボードから ls /dev | grep sd と入力して、Enterキーを押してください。


ls /dev | grep sd

 

以下のようにデバイスファイル /dev/sdb は残っています。

taro@myhostname:~$ ls /dev | grep sd
sda
sda1
sda2
sda3
sda4
sda5
sda6
sda7
sda8
sdb
taro@myhostname:~$

USBフラッシュメモリがまだ抜かれていないため、デバイスファイルが残っているのです。


では、PCからUSBフラッシュメモリを抜いてください

さて、デバイスファイルは消えたでしょうか。 キーボードから ls /dev | grep sd と入力して、Enterキーを押してください。


ls /dev | grep sd

 

以下のように /dev/sdb は見当たらなくなりました。

taro@myhostname:~$ ls /dev | grep sd
sda
sda1
sda2
sda3
sda4
sda5
sda6
sda7
sda8
taro@myhostname:~$

USBフラッシュメモリをPCから抜いたことで、デバイスファイルが削除されました。

デバイスファイルって結局何なのか

この記事では、USBフラッシュメモリの内容を確認するのに、

ls /media/taro/YAMADA_4G

というコマンドを実行しました。 これにより、以下のようにUSBフラッシュメモリの内容が表示されました。

taro@myhostname:~$ ls /media/taro/YAMADA_4G
'System Volume Information'   対象のファイル.txt
taro@myhostname:~$

お気づきの通り、コマンドの中にデバイスファイルの /dev/sdb や /dev/sdb1 は登場していません。 じゃあ、デバイスファイルって何なのでしょうか。

デバイスファイルというのは、デバイス(機器)を1つのファイルとして扱うためのものです。 例えば、接続したUSBフラッシュメモリの容量が 4.0 GByteで、その中のパーティション1の容量が 3.998 GByteだったとします。 その場合には /dev/sdb は 4.0GByteのファイルで、/dev/sdb1 は3.998 GByteのファイルになります。

USBフラッシュメモリの使用率が 1% でも 90% でもデバイスファイルの大きさは変化しません。 未使用の領域も含めてファイルとして扱うためです。

なお、デバイスファイルの中を覗いても、我々が必要とする情報は得られません。 USBフラッシュメモリにはどんなファイルがあるのか、ファイル名は何なのか、というような情報は、デバイスファイルを見てもわからないのです

なぜわからないのか、ということを説明するのは難しいのですが、手帳を例に解説してみます。 目の前に、新品のカレンダ形式の手帳が置いてある場面を想像してください。

その手帳に、仕事や遊びの様々な予定を書き込んだとします。 8月1日は客先との打ち合わせ、8月7日は富士登山、8月11日は隅田川花火大会を見に行く、という具合です。

そして、あろうことか、手帳の各ページの印刷が消えたとします。 あなたが書き込んだ文字だけが残り、最初から印刷されていた文字や線が消えてしまいました。 なんてことでしょう、わけがわからなくなってしまいました。 予定は見えますが、それが何月何日の予定なのか、見当も付きません。

デバイスファイルは、印刷が消えた手帳に例えることができます。 それだけ見ても意味がわからないのです。

では、どうすれば意味がわかるようになるのでしょうか。 透明なフィルムにカレンダの枠や日付を印刷し、手帳の上に重ねればいいのです。 手帳の上にカレンダの枠や日付が描かれたフィルムを重ねる作業が、マウント(連結)なのです。

デバイスファイルやディレクトリで例えるなら、デバイスファイルの /dev/sdb や /dev/sdb1 は印刷の消えた手帳です。 一方、ディレクトリ /media/taro/YAMADA_4G がフィルム越しに見ている手帳です。

つまり、USBフラッシュメモリの内容を確認するために実行した、

ls /media/taro/YAMADA_4G

というコマンドは、/media/taro/YAMADA_4G を通して /dev/sdb1 を見ているのです。

コマンドの中には /dev/sdb も /dev/sdb1 も登場しませんが、/dev/sdb1 からの読み込みは行われています

標準エラー出力の内容をパイプラインで次のコマンドの標準入力に渡すこともできる

標準エラー出力の内容も、パイプラインで次のコマンドの標準入力に渡すことができます。 以下のように、

command1 2>&1 | command2

と記述することで、command1 の標準出力と標準エラー出力を合わせたものを command2 の標準入力として渡すことができます。 なお、短縮表記である、

command1 |& command2

と記述しても同じ結果になります。

パイプラインでもう少し遊んでみる

パイプラインでもう少し遊んでみましょう。 前の記事で登場した bc コマンドに計算式をパイプラインで渡してみましょう。

1 + 2 + 3 を計算させてみます。 キーボードから echo 1 + 2 + 3 | bc と入力して、Enterキーを押してください。


echo 1 + 2 + 3 | bc

 

以下のように計算結果の 6 が表示されます。

taro@myhostname:~$ echo 1 + 2 + 3 | bc
6
taro@myhostname:~$

このように、別のコマンドで生成した計算式を bc コマンドに渡すこともできます。

  
|(パイプ) は "2つのコマンド" ではなく "複数のコマンド" をつなぐためのものです。 command1 | command2 | command3 のように3つ以上のコマンドをつなぐこともできます。
  

grep コマンドや less コマンドはパイプライン専用ではない

この記事では、指定されたテキストを含む行を抽出する grep コマンドを紹介しました。 また以前の記事では、画面出力が長くなる場合にページ単位で進めたり・戻したりするための less コマンドを紹介しました。

紹介したこれらのコマンドは、

ls /dev | grep sd

や、

help cd | less

のようにパインプラインで利用していましたが、これらのコマンドはパイプライン専用ではありません

grep コマンドも less コマンドも、対象のファイルを指定することができます

例えば、Diary.txt というファイルから "天気" というテキストを探す場合は、

grep 天気 Diary.txt

と実行します。 また、ファイル Dairy.txt の内容をページ単位でスクロールしながら見たい場合には、

less Dairy.txt

と実行します。

もちろん、パイプラインを使って同様の結果を得ることもできます。 パイプラインを使う場合には、

cat Diary.txt | grep 天気

や、

cat Diary.txt | less

のように実行します。

  

まとめ

コマンドを |(パイプ) でつなぐことで、前のコマンドの標準出力を後ろのコマンドの標準入力に渡すことができます。 例えば、

ls | grep Doc

と実行することで、カレントディレクトリにあるファイルやディレクトリから "Doc" を名前に含むものだけが表示されます。 grep コマンドは引数のテキストに一致した行のみ表示するためのコマンドです。

操作/コマンド 説明
command1 | command2 command1 の標準出力の出力内容を command2 の入力として渡す
grep 引数で指定されたテキストを含む行を抽出する

command1 | command2 | command3 のように3つ以上のコマンドをつなぐこともできます。

デバイスファイルは、デバイス(機器)を1つのファイルとして扱うための仕組みです。 何でもファイルとして扱う、という設計思想で作られているUNIX系OSらしい仕組みです。 ただし、デバイスファイルを直接覗き込んでも、どんなファイルがあるのか、ファイル名は何なのか、というようなことはわかりません。

デバイスの内容を見るには、マウントしてディレクトリツリーに連結する必要があります。 Linux系OSでは、/media/の下のユーザIDのディレクトリにマウントされます。

標準エラー出力の内容も、パイプラインで次のコマンドの標準入力に渡すことが可能です。 以下のように、

command1 2>&1 | command2

と記述することで、command1 の標準出力と標準エラー出力を合わせたものを command2 の標準入力として渡すことができます。

これには短縮表記があり、

command1 |& command2

と記述しても同じ結果になります。

操作/コマンド 説明
command1 2>&1 | command2
(または)
command1 |& command2
command1 の標準出力の出力内容を command2 の入力として渡す
メニュー