LoginSignup
4
3

More than 3 years have passed since last update.

【Linuxコマンド再現】ls -xをRubyで表現してみた

Last updated at Posted at 2019-11-05

はじめに

こちらの記事lsコマンドを学習中の言語で実装してみるといい練習になるという内容があったので、試してみました。

既存機能を自分で実装するのはロジックを組む練習にいいですね。

この記事が役に立つ方

  • ロジックを組むことに慣れていない
  • Ruby初心者

この記事のメリット

  • ls -xコマンドを再現出来る
  • ロジックを考える練習になる

環境

  • OS: macOS Mojave version 10.14.6
  • シェル: zsh
  • 使用言語: Ruby 2.6.3

ls -xコマンドがやっていること

以下がls -xコマンドの機能です。これを練習として、自作してみます。

  1. カレントディレクトリ内のファイル・ディレクトリ一覧を取得
  2. 取得した一覧から、デフォルトで非表示のもの(’.’や ‘..’など)を間引く
  3. 間引いた一覧を繰り返し処理で出力していく。出力時のルールは以下3点
    • ファイル名・ディレクトリ名は見やすいように折返し表示にしない
    • 表示幅は「最大のファイル・ディレクトリ名の長さ」+半角スペース1つ
    • 名前の頭を昇順ソートし、Z字の順に表示

ターミナルの幅・サンプルファイル名

作成するプログラム名:ls_command.rb
ターミナルの幅:50
サンプルファイル・ディレクトリ:以下(ls -1で出力)

banana8.txt
banana9.txt
ls_command.rb
orange #ディレクトリ
ruby #ディレクトリ
ruby4.txt
ruby5.txt
ruby6.txt
ruby7.txt
sample #ディレクトリ
sample1.txt
sample2.txt
sample3.txt

プログラム

自分で考えた結果がこちらです。
先日学んだ%による幅指定も活用しています。
こういうところで使うんですね。

ls_command.rb
children = Dir::entries(".").sort

# 文字数の最大値を算出。
string_length_max = 0 

children.each do |n|
  if string_length_max <= n.length
    string_length_max = n.length
  end
end


# ターミナルの幅 を 文字数の最大値+1 で割ることで、見やすく整形する準備を行う。
p column = (`tput cols`).to_i / (string_length_max + 1)

# 不要なファイルを配列から削除。
without_list = [".", ".."]
new_children = children - without_list

# 実表示部分。
new_children.each_with_index do |item, index|
    print "%-#{string_length_max}s " % [item]
    case (index + 1) % column
    when 0
      print "\n"
    end unless index == 0
end

出力結果

うまくいきました!

❯ ruby ls_command.rb
banana8.txt   banana9.txt   ls_command.rb 
orange        ruby          ruby4.txt     
ruby5.txt     ruby6.txt     ruby7.txt     
sample        sample1.txt   sample2.txt   
sample3.txt    

参考:ls -xコマンドでの出力

ls -x
banana8.txt   banana9.txt   ls_command.rb
orange        ruby          ruby4.txt
ruby5.txt     ruby6.txt     ruby7.txt
sample        sample1.txt   sample2.txt
sample3.txt

悔しいポイント

1. lsコマンドが再現出来ていない

ls -xコマンド:「Z字」の順にファイル・ディレクトリ名が出力される(左上→右上→左下→右下)

lsコマンド:「N字の左右反転」順に出力される(左上→左下→右上→右下)

どうやったら縦にならびつつ、列方向にも出力できるのか?

2. 動作が遅い

通常のls -xよりも動作がワンテンポ遅く、どこの部分が速度を落としているのか?

each_with_indexの中にcaseを入れてしまっているなど、条件分岐が多いからでしょうか。このあたりはまだ全然わかりません。

おわりに

いつもお世話になっているlsひとつでも自分で考えると大変でした。
いい勉強。

小さなプログラムのロジックであればパッと頭に浮かんでくるよう、今後も継続して学習していきます!

参考にさせて頂いたサイト(いつもありがとうございます)

Rubyでターミナルのサイズを取得する - Qiita
配列の複数要素の削除はdelete_ifかreject、-なんかを使おう - Qiita

【追記】 リファクタリング後のls -x 17行→6行

@scivolaさんのアツいコメントを受け、早速修正してみたので追記します。
なんと、11行も間引くことができました!

元の自分のコードがなんて冗長だったのかと猛省です。

リファクタリングでこんなにも簡素化できるんですね。
mapjoinの使い方は特に応用が効きそうなので、今後も意識的に「使えないか?」という疑いを持っていきます。

ls_command.rb
children = Dir::children(".").sort

# 文字数の最大値を算出。
string_length_max = children.map{ |child| child.length }.max

# ターミナルの幅 を 文字数の最大値+1 で割ることで、見やすく整形する準備を行う。
column = (`tput cols`).to_i / (string_length_max + 1)

# 実表示部分。
children.each_slice(column) do |items|
  puts items.map{ |item| "%-#{string_length_max}s " % item }.join
end
4
3
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3