Rubyワンライナー
まえがき
他にも武器が欲しい
perlもワンライナーに強いけれど、今更やるのもなぁという気持ち
モジュールを require
できるのが強い
Rubyの構文よく知らないけれど雰囲気でいけるいける
オプション
癖でとりあえず ruby -lane 'puts うんちゃら'
で実行してしまうけれど、それぞれの意味はここに記載
ワンライナースクリプト -e
標準入力とか特に扱わないならこれだけで良い
標準入力を受ける -n
入力1行を格納する変数 $_
(awkでいう $0
)
現在処理している行数を格納する変数 $.
(awkでいう NR
)
shecho sushi | ruby -ne 'puts $_'
cat << E | ruby -ne 'print "#{$.} : #{$_}"'
hello
world
HELLO
WORLD
E
BEGIN
, END
shseq 10 | ruby -ne 'BEGIN{ puts "id" }; puts $_; END{ puts "end" }'
注意点はブロックの終了後に ;
が必須なこと
これはセミコロンが抜けてるので構文エラーになる
shseq 10 | ruby -ne 'BEGIN{ puts "id" } puts $_; END{ puts "end" }'
$F
変数に配列要素として代入される
shecho 1 2 3 | ruby -ane 'puts $F[2]'
フィールドの区切り文字の指定 -F
-F','
みたいに F
に区切り文字をくっつけないとダメ
shecho 1,2,3 | ruby -F',' -lane 'puts $F'
# こっちはエラー
echo 1,2,3 | ruby -F ',' -lane 'puts $F'
行末処理をする -l
このオプションを指定しないと $_
の末尾には改行文字が含まれる
指定すると $_
の改行文字を捨ててくれる
shseq 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!
shecho sushi | ruby -pe '$_.tr!("a-z", "A-Z")'
モジュールを読み込む (require) r
shruby -rdate -e 'puts Date.today'
とりあえず
ruby -lane 'スクリプト'
ってやっとけば
awkと同じ感覚で実行できる
芸
1行目を無視する
awk '1<NR'
相当
後置ifを使う
shseq 3 | ruby -lane 'puts $_ if 1 < $.'
文字列の n 文字目の文字を取得
インデックス0アクセスは先頭の文字に
インデックス-1アクセスは末尾の文字に
0..終端
でレンジで文字列を取得
shruby -e 'puts "hello"[0]' # -> h
ruby -e 'puts "hello"[-1]' # -> o
ruby -e 'puts "hello"[0..-2]' # -> hell
集計
合計
shseq 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'
平均
shseq 10 | ruby -ne 'BEGIN{ sum = 0 }; sum += $_.to_i; END{ puts sum.to_f / $. }'
変数初期化のためだけの BEGIN
を省略
var ||= 0
は変数が未定義の場合だけ0を代入する
shseq 10 | ruby -ne 'sum ||= 0; sum += $_.to_i; END{ puts sum.to_f / $. }'
GroupByしてsum
||=
を使えばキー未定義のハッシュマップの初期化もできる
shcat << 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!
だと破壊的変更をする
shruby -e 'puts "hello".gsub(/l/, "L")'
echo 1年と12ヶ月と22日 | ruby -pe '$_.gsub!(/\d{2,}/){|s| s.tr("0-9", "0-9")}'
変数を文字列に埋め込む "#{var}"
shruby -e 'x = "hello"; puts "<#{x}>"'
変数を埋め込むというより、 #{}
の中に式がそのまま埋め込める
bashruby -e 'puts "#{1 + 1}"' # -> 2
リストを要素ごとに処理 map
shruby -e 'puts ["s", "u", "s", "h", "i"].map{|s| "<#{s}>"}.join'
こういう引数にメソッドを受け取るメソッドでそのまま引数を渡せる場合は &:メソッド名
という書き方もできる
shecho 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
shruby -e 'puts ["s", "u", "s", "h", "i"].map.with_index{|s, i| "<#{i}:#{s}>"}.join'
ランダムに要素を入れ替える shuffle
shruby -e 'puts [1, 2, 3, 4, 5].shuffle'
grep検索 grep
shruby -e 'puts ["山田太郎", "ヤマダタロウ", "やまだたろう", "山田一郎", "やまだいちろう"].grep(/(山田|やまだ|ヤマダ)(太郎|たろう|タロウ)/)'
グループマッチした箇所を取り出すことも可能
shruby -e '["山田たろう"].grep(/(山田)たろう/){|i| puts "$1 = #{$1}, i = #{i}"}'
# -> $1 = 山田, i = 山田たろう
並べ替え sort
shruby -e 'puts (["ど","う","ぶ","つ","の","森"] * 199999).sort_by{rand}.each_slice(6).to_a.map.with_index{|n, i| i.to_s + " " + n.join("")}' | grep どうぶつの森 #シェル芸
現在日時 Time.now
shruby -e 'puts Time.now'
ruby -rdate -e 'puts Date.today'
日時演算 next_day
prev_day
他にもいろいろある
shruby -rdate -e 'puts Date.today.next_day(3)' # 3日後
ruby -rdate -e 'puts Date.today.prev_day(7)' # 7日前
日付をパース
shruby -rtime -e 'puts Time.parse("2021/01/15")'
UTCをJSTに変換
shruby -rtime -e 'puts Time.parse("2021/01/15 00:00:00 +00:00").getlocal("+09:00")'
繰り返し times
each
shruby -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
まで
shruby -e '(1..10).each{ |i| puts i }'
# ↑↓同じ
ruby -e '(1..10).each{ puts _1 }'
特定の文字を含む行だけ出力
~ /REGEXP/
これは /REGEXP/ ~ $_
と等価
shseq 9 | ruby -lane 'puts $_ if ~ /[2468]/'
多重代入
配列をそれぞれ別々の変数に代入できる
shruby -e 'a,b = [1,2,3]; puts a, b' # -> 1, 2
shcat << EOS | ruby -F, -lane 'a,b,c = $F; puts b' # -> 1, 2, 3 だけ出力される
a,1,A
b,2,B
c,3,C
EOS
shruby -rjson -e 'puts JSON.generate({"name":"taro", "age":22, "admin":true})'
標準入力からJSONを読み込んでハッシュに変換
shcat << JSON | ruby -rjson -e 'puts JSON.parse(STDIN.read)'
{
"id":1,
"name":"taro",
"admin":true
}
JSON
shcat << 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に変換
shcat << 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)に
2進数(string)を10進数(integer)に
日本語の文字のコードポイントを取得
rubyputs "あ".ord
puts "あいうえお".chars.map(&:ord)
コードポイントを文字に戻す
rubyputs 97.chr # -> a
puts 12354.chr(Encoding::UTF_8) # -> あ
参考