シェルスクリプトにRuby等のスクリプトを埋め込む

一般的にシェルからRubyなどを呼ぶ場合、短ければワンライナー、長ければ別ファイルにすれば良い。

しかし、ワンライナーには長ずぎる、ファイルにするには少々短いというRubyPythonなどのコードもある。そういうコードをシェルスクリプトに埋め込む方法を記す。

その方法は、$()の中でヒアドキュメントをcatし、変数に入れて-eオプションでワンライナーと同じように実行するというもの。

#!/bin/bash

ruby_script=$(cat <<- 'EOF'
  lines = $stdin.readlines
  width = (lines.map{ |line| line.split.first.size }.max / 8 + 1) * 8
  lines.map(&:split).each { |a, b| puts "#{a.ljust(width)}#{b}" }
EOF
)

ruby -e "${ruby_script}" < data.txt

変数の代わりにプロセス置換を使ってファイル扱いでrubyに渡すこともできる

#!/bin/bash

ruby <(cat <<- 'EOF'
  lines = ARGF.readlines
  width = (lines.map{ |line| line.split.first.size }.max / 8 + 1) * 8
  lines.map(&:split).each { |a, b| puts "#{a.ljust(width)}#{b}" }
EOF
) < data.txt

デバッグが難しくなるのであまり長いコードを埋め込むのはお勧めしない。多くても5行程度に収めたい

ちなみに例示したRubyスクリプトはcatするとこんなふうに表示されるタブ区切りのファイルを

1234    abcdefg
123456789       abcdefg
1234567 abcdefg
12345678901234  abcdefg

以下のように整形してターミナルに出力するというもの

1234            abcdefg
123456789       abcdefg
1234567         abcdefg
12345678901234  abcdefg

タブ幅を調整するだけならtabsというコマンドでもできる。

tabs "-$(ruby -e 'puts (ARGF.map{ |line| line.split.first.size }.max / 8 + 1) * 8' < data.txt)"
cat data.txt