日々之迷歩

世の中わからんことだらけ

ITが複雑で難しくなっていく様に翻弄される日々です。微力ながら共著させていただいた「シェル・ワンライナー160本ノック」をよろしくお願い申し上げます。

パイプラインに横槍

シェル芸と言えば、一糸乱れぬ一筋のストリームが流れる事に心奪われ、あな美しかなと心躍らせることこそ醍醐味かと存じます。だがしかし人間たるもの、完璧なものこそ壊したがる一面も否定出来ません。

ということで、シェルのパイプラインに横槍を入れざるを得ない心境になったのもあり、現状理解していることをまとめてみました。大きく4パターンあります。

1. 文字列やファイルの乱入

1.1. 文字列乱入

シェルの ( ) おまとめ記法とcatの組み合わせで、文字列を混ぜ込むことが出来ます。

$ seq 2 | (echo 'Header'; cat; echo 'Footer')
Header
1
2
Footer

bcコマンドで2進数表示させたい時などに使っています。

$ seq 5 | (echo 'obase=2'; cat) | bc
1
10
11
100
101

1.2. ファイル乱入

catで引数に'-'を使うと、ファイル名の代わりに標準入力の指定になります。他にも引数に-にすると標準入力になるコマンドが存在します(GNU grepのフィルタモードとか)。

$ cat data 
1
2

$ seq 11 12 | cat data -
1
2
11
12

ローテーションして圧縮したログと、現在のログを時間順に合体させる時に使っています。

$ ls -tr /var/log/httpd-access.log.*.bz2 |\
> xargs zcat |\
> grep -v 'logfile turned over' |\
> cat - /var/log/httpd-access.log

2. ファイルにぶん投げ

teeコマンドでファイルに書き出せます、まあ基本でしょうか。書き出すファイル名は複数指定可能です。

$ seq 3 | tee num1 num2 | gtac
3
2
1

$ cat num1
1
2
3

$ cat num2
1
2
3

処理を確認したいコマンドがある時(デバッグ)、前後に挟み込んで使っています。

$ ... | tee before | コマンド | tee after | ...
Process Substitutionについて

次に進む前にProcess Substitution(プロセス置換)というシェルの機能についてです。ksh、bash、zsh限定の機能ですが個人的に気に入っています。

コマンドに対する入出力を、ファイルのように指定することが出来ます。

  • コマンドの標準出力を入力ファイルとして<(コマンド)
  • コマンドの標準入力を出力ファイルとして>(コマンド)

言葉だけでは分かりにくいので、diffコマンドを使った事例を示します。diffは2つのファイルの差分を取りますが、ファイルではなく2つのコマンド出力の差分を取りたい場合にプロセス置換を利用します。

$ \ls -1
a
b

$ \ls -a1
.
..
.dot
a
b

$ diff <(\ls) <(\ls -a)
0a1,3
> .
> ..
> .dot

さて、Process Substitution(プロセス置換)の機能を踏まえたところで続きを。

3. 別のコマンドが乱入

echoとcatの組み合わせで文字列を混ぜ込みます。先ほどのbcコマンドで2進数表示をする例です。

$ seq 5 | cat <(echo 'obase=2') - | bc
1
10
11
100
101

これは下記の記事でも利用していた機能です。 papiro.hatenablog.jp

4. 別のコマンドにぶん投げ

teeとの組み合わせで、別の複数のコマンドに流れを分岐出来ます。

$ seq 3 | tee >(gtac > reverse) >(gsort -R > random) | wc -l
       3

$ cat reverse 
3
2
1

$ cat random 
3
1
2

パイプ処理の途中結果をtrコマンドに渡してCSVに変換して保存する、などに使えます。

$ awk '{print substr($4,2,11),substr($4,14,2)}' acccess_log |\
> count 1 2 |\
> tee >(tr ' ' ',' > syukei.csv) |\
> map num=1 |\
> keta | head -n 3
          *  00  01 05 06 07 08 09  10   11  12  13   14   15   16   17   18  19   20  21  22  23
03/Apr/2008   0   0  0  0  0  0  0   0    0   0   0    1    2    0    0    0   0    0   0   0   0
04/Apr/2008   0   0 27  0  0  0  0   0    0   0  15   41    0    0    0    0   0    0   0   0   0
07/Apr/2008   0   0  0  0  0  0  0   0    2   0   0    0    0    0    0    0   0    0   0   0   0
10/Apr/2008   0   0  0  0  0  0  0   0    0   0   0    0    0    0    0    0   0    0   6   0   0

$ head -n 3 syukei.csv 
03/Apr/2008,14,1
03/Apr/2008,15,2
04/Apr/2008,05,27

ところでmoreutilsのspongeというコマンドがありますが、moreutilsを調べてたらpeeというコマンドを発見しました。

Process Substitution同様に、パイプラインを複数に分けることが出来るようです。ただ、peeコマンド以降の出力は打ち止めみたいですね。

$ seq 3 | pee 'gtac > reverse' 'gsort -R > random'

手練れのシェル芸人達には基本だったかもしれないが、これらを応用すればパイプラインに分岐を作ることが出来て便利になるかもしれないし、混乱するかもしれません。それはあなた次第・・・