以前、Rubyの標準ライブラリのみで実装したLinuxやUnix系のOSで使われるlsコマンドを、オブジェクト指向版として実装しました。
要件
- オプションなし 最大3列で表示。 ファイルの並び順は以下のとおり。
1 5 9 2 6 10 3 7 4 8
ls_command.rb
- コマンドラインからの入力を受け取り、それに基づいて上記のクラスを利用してファイル一覧を表示。
OptionParser
を用いてコマンドライン引数(-a
,-r
,-l
)を解析し、その結果をFileListクラスのインスタンスに渡して表示を行う。
#!/usr/bin/env ruby # frozen_string_literal: true require 'optparse' require_relative 'file_info' require_relative 'file_permission' require_relative 'file_list' params = {} OptionParser.new do |opt| opt.on('-a') { |v| params[:a] = v } opt.on('-r') { |v| params[:r] = v } opt.on('-l') { |v| params[:l] = v } opt.parse!(ARGV) end file_list = FileList.new(params) file_list.display_lists
file_info.rb
- 特定のファイルに関する情報を取得。
# frozen_string_literal: true require 'etc' class FileInfo attr_reader :file def initialize(file) @file = file end # ファイルの統計情報(サイズ、所有者、グループ、更新時間など)を取得 def stat File.stat(file) end # ファイルのパーミッションをFilePermissionクラスを用いて取得 def permissions FilePermission.new(file).format_permissions end # ファイルのハードリンク数を取得 def hard_link stat.nlink end # ファイルの所有者のユーザー名を取得 def user Etc.getpwuid(stat.uid).name end # ファイルの所有グループ名を取得 def group Etc.getgrgid(stat.gid).name end # ファイルのサイズを取得 def filesize stat.size end # ファイルの最終更新時間を取得 def mtime File.mtime(file) end end
file_list.rb
- ディレクトリ内のファイル一覧を取得し、その一覧を指定された形式で表示。
# frozen_string_literal: true class FileList COLUMNS = 3 def initialize(params) @params = params end def display_lists file_match_option = @params[:a] ? File::FNM_DOTMATCH : 0 files = Dir.glob('*', file_match_option).map { |file| FileInfo.new(file) } current_files = @params[:r] ? files.reverse : files @params[:l] ? display_in_long_format(current_files) : display_in_short_format(current_files) end private def display_in_short_format(lists) max_filename_length = lists.map { |file_info| file_info.file.length }.max line_count = (lists.length.to_f / COLUMNS).ceil line_count.times do |line| lists.each_slice(line_count) do |columns| print columns[line].file.ljust(max_filename_length + 2) if columns[line] end print "\n" end end def display_in_long_format(current_files) display_total_block_number(current_files) max_lengths = calculate_maximum_lengths(current_files) current_files.each { |file_info| display_file_details(file_info, max_lengths) } end def display_total_block_number(current_files) total = current_files.sum { |file| file.stat.blocks } puts "total #{total}" end def display_file_details(file_info, max_lengths) permissions = file_info.permissions hard_link = file_info.hard_link.to_s.rjust(max_lengths[:hard_link]) user = file_info.user.rjust(max_lengths[:user]) group = file_info.group.rjust(max_lengths[:group]) filesize = file_info.filesize.to_s.rjust(max_lengths[:filesize]) time = file_info.mtime.strftime('%m %d %H:%M') filename = file_info.file puts "#{permissions} #{hard_link} #{user} #{group} #{filesize} #{time} #{filename}" end def calculate_maximum_lengths(file_stats) max_lengths = { hard_link: 0, user: 0, group: 0, filesize: 0 } file_stats.each do |file| max_lengths[:hard_link] = [max_lengths[:hard_link], file.hard_link.to_s.length].max max_lengths[:user] = [max_lengths[:user], file.user.length].max max_lengths[:group] = [max_lengths[:group], file.group.length].max max_lengths[:filesize] = [max_lengths[:filesize], file.filesize.to_s.length].max end max_lengths end end
file_permission.rb
- ファイルのパーミッションを表す文字列を生成。
File.lstat(file).mode.to_s(8)
を使用してファイルのモード(パーミッションとファイルの種類を含む)を8進数の文字列として取得し、読みやすい形式(例: 'rwxr-xr-x')に変換。
class FilePermission PERMISSIONS = ['---', '--x', '-w-', '-wx', 'r--', 'r-x', 'rw-', 'rwx'].freeze FILE_TYPE = { '10' => 'p', '20' => 'c', '40' => 'd', '60' => 'b', '100' => '-', '120' => 'l', '140' => 's' }.freeze attr_reader :file def initialize(file) @file = file end def format_permissions type_of_file + permission_code end private def type_of_file FILE_TYPE[file_mode[0..-4]] end def permission_code permission_numbers.map { |n| PERMISSIONS[n] }.join end def file_mode File.lstat(file).mode.to_s(8) end def permission_numbers file_mode[-3..].chars.map(&:to_i) end end
【参考】