generated at
Rubyワンライナー
まえがき
シェル芸でワンライナーの芸の幅を広くしたい
awkはそれなりに書けるようになってきた
他にも武器が欲しい
perlもワンライナーに強いけれど、今更やるのもなぁという気持ち
ワンライナーとrubyは相性が良い
モジュールを require できるのが強い
JSONとかCSVとかUnicodeとか扱いやすい
Railsのほうは何も知らないけれどやってみる
Rubyの構文よく知らないけれど雰囲気でいけるいける

オプション
癖でとりあえず ruby -lane 'puts うんちゃら' で実行してしまうけれど、それぞれの意味はここに記載
ワンライナースクリプト -e
sh
ruby -e 'puts "hello"'
標準入力とか特に扱わないならこれだけで良い
標準入力を受ける -n
入力1行を格納する変数 $_ (awkでいう $0 )
現在処理している行数を格納する変数 $. (awkでいう NR )
sh
echo sushi | ruby -ne 'puts $_' cat << E | ruby -ne 'print "#{$.} : #{$_}"' hello world HELLO WORLD E
BEGIN , END
awkみたいなブロック表現
sh
seq 10 | ruby -ne 'BEGIN{ puts "id" }; puts $_; END{ puts "end" }'
注意点はブロックの終了後に ; が必須なこと
これはセミコロンが抜けてるので構文エラーになる
sh
seq 10 | ruby -ne 'BEGIN{ puts "id" } puts $_; END{ puts "end" }'
awkみたいにフィールド変数として処理 -a
$F 変数に配列要素として代入される
sh
echo 1 2 3 | ruby -ane 'puts $F[2]'
フィールドの区切り文字の指定 -F
awkと同じ F オプション
-F',' みたいに F に区切り文字をくっつけないとダメ
sh
echo 1,2,3 | ruby -F',' -lane 'puts $F' # こっちはエラー echo 1,2,3 | ruby -F ',' -lane 'puts $F'
行末処理をする -l
このオプションを指定しないと $_ の末尾には改行文字が含まれる
指定すると $_ の改行文字を捨ててくれる
sh
seq 10 | ruby -ane 'BEGIN{ sum = 0 }; sum += $_.chomp.to_i; END{ print sum }' seq 10 | ruby -lane 'BEGIN{ sum = 0 }; sum += $_.to_i; END{ print sum }'
$_ をprint -p
-n とほぼ同じだけれど、 $_ をprintする
putsを省略できる
$_ を上書きする必要があるため副作用のあるメソッドを呼ぶ必要がある
rubyではメソッドが自身のインスタンスに対して破壊的変更を加えるものは ! がメソッド名に含まれる
例: tr!
sh
echo sushi | ruby -pe '$_.tr!("a-z", "A-Z")'
モジュールを読み込む (require) r
sh
ruby -rdate -e 'puts Date.today'
とりあえず
ruby -lane 'スクリプト' ってやっとけばawkと同じ感覚で実行できる

1行目を無視する
awk '1<NR' 相当
後置ifを使う
sh
seq 3 | ruby -lane 'puts $_ if 1 < $.'
文字列の n 文字目の文字を取得
インデックス0アクセスは先頭の文字に
インデックス-1アクセスは末尾の文字に
0..終端 でレンジで文字列を取得
sh
ruby -e 'puts "hello"[0]' # -> h ruby -e 'puts "hello"[-1]' # -> o ruby -e 'puts "hello"[0..-2]' # -> hell
集計
合計
sh
seq 10 | ruby -ne 'BEGIN{ sum = 0 }; sum += $_.to_i; END{ puts sum }' ruby -e 'puts [1,2,3,4,5,6,7,8,9,10].inject(:+)' ruby -e 'puts [1,2,3,4,5,6,7,8,9,10].sum'
平均
sh
seq 10 | ruby -ne 'BEGIN{ sum = 0 }; sum += $_.to_i; END{ puts sum.to_f / $. }'
変数初期化のためだけの BEGIN を省略
var ||= 0 は変数が未定義の場合だけ0を代入する
sh
seq 10 | ruby -ne 'sum ||= 0; sum += $_.to_i; END{ puts sum.to_f / $. }'
GroupByしてsum
||= を使えばキー未定義のハッシュマップの初期化もできる
sh
cat << EOS | ruby -F' ' -lane 'm ||= {}; k = $F[0]; m[k] ||= 0; m[k] += $F[1].to_i; END{ m.each{ |k,v| puts "#{k} #{v}" } }' apple 100 banana 200 apple 100 orange 300 banana 200 banana 200 EOS
正規表現マッチした部分だけ加工 gsub
gsub! だと破壊的変更をする
sh
ruby -e 'puts "hello".gsub(/l/, "L")' echo 1年と12ヶ月と22日 | ruby -pe '$_.gsub!(/\d{2,}/){|s| s.tr("0-9", "0-9")}'
変数を文字列に埋め込む "#{var}"
sh
ruby -e 'x = "hello"; puts "<#{x}>"'
変数を埋め込むというより、 #{} の中に式がそのまま埋め込める
bash
ruby -e 'puts "#{1 + 1}"' # -> 2
リストを要素ごとに処理 map
sh
ruby -e 'puts ["s", "u", "s", "h", "i"].map{|s| "<#{s}>"}.join'
こういう引数にメソッドを受け取るメソッドでそのまま引数を渡せる場合は &:メソッド名 という書き方もできる
sh
echo h e l l o | ruby -lane 'p $F.map(&:upcase)' # 以下と結果は同じ echo h e l l o | ruby -lane 'p $F.map{|i| i.upcase}' > ["H", "E", "L", "L", "O"]
リストを要素ごとに処理しつつ、インデックスを付与 with_index
sh
ruby -e 'puts ["s", "u", "s", "h", "i"].map.with_index{|s, i| "<#{i}:#{s}>"}.join'
ランダムに要素を入れ替える shuffle
sh
ruby -e 'puts [1, 2, 3, 4, 5].shuffle'
grep検索 grep
sh
ruby -e 'puts ["山田太郎", "ヤマダタロウ", "やまだたろう", "山田一郎", "やまだいちろう"].grep(/(山田|やまだ|ヤマダ)(太郎|たろう|タロウ)/)'
グループマッチした箇所を取り出すことも可能
sh
ruby -e '["山田たろう"].grep(/(山田)たろう/){|i| puts "$1 = #{$1}, i = #{i}"}' # -> $1 = 山田, i = 山田たろう
並べ替え sort
sh
ruby -e 'puts (["ど","う","ぶ","つ","の","森"] * 199999).sort_by{rand}.each_slice(6).to_a.map.with_index{|n, i| i.to_s + " " + n.join("")}' | grep どうぶつの森 #シェル芸
現在日時 Time.now
sh
ruby -e 'puts Time.now' ruby -rdate -e 'puts Date.today'
日時演算 next_day prev_day
他にもいろいろある
sh
ruby -rdate -e 'puts Date.today.next_day(3)' # 3日後 ruby -rdate -e 'puts Date.today.prev_day(7)' # 7日前
日付をパース
sh
ruby -rtime -e 'puts Time.parse("2021/01/15")'
UTCをJSTに変換
sh
ruby -rtime -e 'puts Time.parse("2021/01/15 00:00:00 +00:00").getlocal("+09:00")'
繰り返し times each
sh
ruby -e '10.times{ puts 1 }' ruby -e '["hello", "world"].each{|item| puts item }' ruby -e '(1 .. 10).each{ |i| puts i}' # 10 を含む ruby -e '(1 ... 10).each{ |i| puts i}' # 10 を含まない
番号指定パラメータ
_1 _9 まで
sh
ruby -e '(1..10).each{ |i| puts i }' # ↑↓同じ ruby -e '(1..10).each{ puts _1 }'
特定の文字を含む行だけ出力
~ /REGEXP/ これは /REGEXP/ ~ $_ と等価
sh
seq 9 | ruby -lane 'puts $_ if ~ /[2468]/'
多重代入
配列をそれぞれ別々の変数に代入できる
sh
ruby -e 'a,b = [1,2,3]; puts a, b' # -> 1, 2
sh
cat << EOS | ruby -F, -lane 'a,b,c = $F; puts b' # -> 1, 2, 3 だけ出力される a,1,A b,2,B c,3,C EOS
sh
ruby -rjson -e 'puts JSON.generate({"name":"taro", "age":22, "admin":true})'
標準入力からJSONを読み込んでハッシュに変換
sh
cat << JSON | ruby -rjson -e 'puts JSON.parse(STDIN.read)' { "id":1, "name":"taro", "admin":true } JSON
sh
cat << CSV | ruby -rcsv -e 'CSV(STDIN).each{|row| p row }' "id","name","age" "1","yamada","22" "2","suzuki","24" "3","uehara","19" CSV
CSVをJSONに変換
sh
cat << CSV | ruby -rcsv -rjson -e 'puts JSON.generate(CSV(STDIN).map{|x| x})' "id","name","age" "1","yamada","22" "2","suzuki","24" "3","uehara","19" CSV
10進数(integer)を2進数(string)に
ruby
puts 255.to_s(2)
2進数(string)を10進数(integer)に
ruby
puts 1100.to_i(2)
日本語の文字のコードポイントを取得
ruby
puts "あ".ord puts "あいうえお".chars.map(&:ord)
コードポイントを文字に戻す
ruby
puts 97.chr # -> a puts 12354.chr(Encoding::UTF_8) # -> あ

参考