開発者であればターミナル操作を行う機会は多いでしょう。各コマンドは独立しており、パイプなどを通じて一繋ぎの操作を実現します。しかしシェル芸という言葉があるくらい、熟練者とそうでない人とで実現できるものが異なります。さらにシェルスクリプトですべてを記述するのはコード量も多くなったり、正規表現を駆使しなければならないでしょう。 そこで使ってみたいのがrbです。標準出力を受け取って、Rubyで処理が書けるユーティリティです。

rbの使い方

例えば df の結果をサイズ順に並び替えます。

$ df -h | rb '[first].concat drop(1).sort_by { |l| l.split[-2].to_f }'
Filesystem                                         Size   Used  Avail Capacity iused               ifree %iused  Mounted on
/dev/disk1s4                                      466Gi  5.0Gi   66Gi     8%       5 9223372036854775802    0%   /private/var/vm
/dev/disk2s1                                      119Gi   73Gi   46Gi    62% 2101576          4292865703    0%   /Volumes/SD
devfs                                             211Ki  211Ki    0Bi   100%     729                   0  100%   /dev
map -hosts                                          0Bi    0Bi    0Bi   100%       0                   0  100%   /net
map auto_home                                       0Bi    0Bi    0Bi   100%       0                   0  100%   /home

ファイルを検索して、拡張子ごとにファイル数をリストアップします。

$ find . -type f | rb 'group_by(&File.method(:extname)).map { |ext, o| "#{ext.chomp}: #{o.size}" }'

: 27
.sample: 10
.idx: 1
.pack: 1
.js: 1
.py: 1
.json: 1

rbは標準出力をRubyの変数として受け取ります。後はevalで評価しています。コードは以下の通りです。

#!/usr/bin/env ruby

def execute(_, code)
  puts _.instance_eval(&code)
rescue Errno::EPIPE
  exit
end

single_line = ARGV.delete('-l')
code = eval("Proc.new { #{ARGV.join(' ')} }")
single_line ? STDIN.each { |l| execute(l.chomp, code) } : execute(STDIN.each_line, code)

このコードさえ分かっていれば、後は自由にコードが書けるようになるでしょう。もちろん、出力結果を別なコマンドに送ることもできます。シェルスクリプトの活用する幅が広がりそうなソフトウェアです。

rbはRuby製のオープンソース・ソフトウェア(MIT License)です。

thisredone/rb: Turns Ruby into a versatile command line utility