この記事は何
shellgei160を通じて言語習得 Advent Calendar 2023に参加しています。
書籍「シェル芸ワンライナー160本ノック」の例題をRubyで解いてみて、Rubyの学習に役立てようとするものです。
例題はこちらのリポジトリで公開されているものに限ります。
https://github.com/shellgei/shellgei160
実行環境など
- Docker image: ruby:3.0.2
- 上記リポジトリをクローンした上で、リポジトリのルートディレクトリ直下に
answer-ruby
ディレクトリを作り、その中に解答となるファイルを作成していきます。
今回のテーマ
$ declare -A x ; IFS=: ; while read {a..g} ; do x[$g]+=. ; done < /etc/passwd ; for s in ${!x[@]} ; do echo $s ${#x[$s]}; done ; unset x
/etc/passwd
の中に登場するshellについて、それぞれのshellごとに個数をカウントする処理です。
出力例
/bin/bash 1
/bin/sync 1
/usr/sbin/nologin 17
解
# /etc/passwd を行ごとに読み込んで、7番目のフィールド = シェル名があれば取り込む
shells = File.open('/etc/passwd', 'r').readlines.map do |line|
fields = line.split(':')
nil if fields.size < 7
fields[6].chomp
end
# シェルの種類ごとに集計する
# group_by(&:itself)だけでは各キーごとに要素をまとめた配列が得られるだけなので
# transform_values(&:count)で各要素の数を数える必要がある
counts = shells.sort.group_by(&:itself).transform_values(&:count)
counts.each do |shell, count|
puts "#{shell} #{count}"
end
所感
-
group_by(Enumeratorモジュール)
は、ブロック内の値ごとに要素をまとめる。なるほど。(&:itself)
とすることで、同じ値のものをまとめることになる。 -
transform_values
もいろいろ使えそうで良い。