This commit is contained in:
Rikuoh Tsujitani 2025-02-16 21:39:06 +09:00
commit 6f5e27fad4
Signed by: riq0h
GPG key ID: 010F09DEA298C717
4 changed files with 195 additions and 0 deletions

1
README.md Normal file
View file

@ -0,0 +1 @@
GNU Coreutils commands reimplemented in Ruby.

28
cal.rb Executable file
View file

@ -0,0 +1,28 @@
#!/usr/bin/ruby
# frozen_string_literal: true
require 'date'
require 'optparse'
year = Date.today.year
month = Date.today.month
week = %w[日 月 火 水 木 金 土]
opt = OptionParser.new
opt.on('-m month') { |v| month = v.to_i }
opt.on('-y year') { |v| year = v.to_i }
opt.parse(ARGV)
first_day = Date.new(year, month, 1)
last_day = Date.new(year, month, -1)
puts "#{month.to_s.rjust(7)}\s#{year}"
puts week.join(' ')
print "\s\s\s" * first_day.wday
(first_day..last_day).each do |date|
day_string = date.day.to_s.rjust(2, "\s")
day_string = "\e[7m#{day_string}\e[0m" if date == Date.today
print "#{day_string}\s"
puts if date.saturday?
end
puts

106
ls.rb Normal file
View file

@ -0,0 +1,106 @@
# frozen_string_literal: true
require 'optparse'
require 'etc'
COLUMNS = 3
MARGIN = 3
TYPES = {
'fifo' => 'p',
'characterSpecial' => 'c',
'directory' => 'd',
'blockSpecial' => 'b',
'file' => '-',
'link' => 'l',
'socket' => 's'
}.freeze
PERMISSIONS = {
'0' => '---',
'1' => '--x',
'2' => '-w-',
'3' => '-wx',
'4' => 'r--',
'5' => 'r-x',
'6' => 'rw-',
'7' => 'rwx'
}.freeze
def run
params = ARGV.getopts('a', 'r', 'l')
filenames = params['a'] ? Dir.glob('*', File::FNM_DOTMATCH) : Dir.glob('*')
filenames = filenames.reverse if params['r']
params['l'] ? output_file_datails(filenames) : output(filenames)
end
def output(filenames)
rows = filenames.size.ceildiv(COLUMNS)
pads = pad_created(filenames, rows)
(0...rows).each do |row|
COLUMNS.times do |column|
index = row + column * rows
print filenames[index].ljust(pads[column] + COLUMNS) if filenames[index]
end
puts
end
end
def pad_created(filenames, rows)
filenames.each_slice(rows).map { _1.map(&:length).max }
end
def output_file_datails(filenames)
puts total_file_blocks(filenames)
filenames.each do |file|
file_stat = File::Stat.new(file)
print TYPES[file_stat.ftype]
print file_mode(file_stat)
print owner_info(file_stat, filenames)
print time_stamp(file_stat)
print symbolic(file)
puts
end
end
def owner_info(file_stat, filenames)
[
file_stat.nlink.to_s.prepend(' '),
Etc.getpwuid(file_stat.uid).name,
Etc.getgrgid(file_stat.gid).name,
file_stat.size.to_s.rjust(max_filename_length(filenames))
].join(' ')
end
def symbolic(filenames)
if File.lstat(filenames).symlink?
" #{filenames} -> #{File.readlink(filenames)}"
else
" #{filenames}"
end
end
def file_mode(file_stat)
file_count = file_stat.mode.to_s(8).slice(-3, 3)
file_permission = file_count.split('').map do |file|
PERMISSIONS[file]
end
file_permission.join('')
end
def max_filename_length(filenames)
filenames.map { |file| File.size(file) }.max.to_s.length + MARGIN
end
def time_stamp(file_stat)
file_stat.mtime.strftime('%_m月 %_d %H:%M')
end
def total_file_blocks(filenames)
blocks = filenames.map do |file|
File::Stat.new(file).blocks
end
"total #{blocks.sum}"
end
run

60
wc.rb Normal file
View file

@ -0,0 +1,60 @@
# frozen_string_literal: true
require 'optparse'
def main
options, filenames = parse_options
file_stats = collect_file_stats(filenames)
total_stat = calculate_total_stat(file_stats)
max_widths = calculate_max_widths(total_stat)
file_stats << total_stat if filenames.size > 1
file_stats.each { |file_stat| puts format_row(file_stat, max_widths, options) }
end
def parse_options
options = {}
OptionParser.new do |opts|
opts.on('-l') { options[:lines] = true }
opts.on('-w') { options[:words] = true }
opts.on('-c') { options[:bytes] = true }
end.parse!
options = { bytes: true, lines: true, words: true } if options.empty?
filenames = ARGV.empty? ? [''] : ARGV
[options, filenames]
end
def collect_file_stats(filenames)
filenames.map do |filename|
text = filename.empty? ? ARGF.read : File.read(filename)
{
filename: filename,
lines: text.lines.count,
words: text.split.size,
bytes: text.bytesize
}
end
end
def calculate_total_stat(file_stats)
total_stat = { filename: '合計', lines: 0, words: 0, bytes: 0 }
file_stats.each do |stats|
total_stat[:lines] += stats[:lines]
total_stat[:words] += stats[:words]
total_stat[:bytes] += stats[:bytes]
end
total_stat
end
def calculate_max_widths(total_stat)
%i[lines words bytes].to_h { |key| [key, total_stat[key].to_s.length] }
end
def format_row(stats, max_widths, options)
columns = %i[lines words bytes].filter_map do |key|
stats[key].to_s.rjust(max_widths[key]) if options[key]
end
[*columns, stats[:filename]].join(' ')
end
main