■ Ruby でディレクトリの tree 表示

■ ディレクトリの読みだし

ディレクトリからファイル名を読みだすと、tree と呼ばれるよくあるコマンドと同じようなものを自分で作れます。 ディレクトリの階層構造を視覚的に見ることができるので、忘れていた? ファイルの置場所を思い出せるかもしれません?

あまり大きなディレクトリなどは、tree で表示しても見にくいですが、とりあえずサンプルとして使ってください。

■ 実行方法

オプションを指定しない場合、カレントディレクトリの表示します。

$ ruby tree.rb
|-+ .
  |-- t1.rb
  |-- tree.rb
  |-- tree.rb,v
  |-- tree.rb~

4 files

ディレクトリのみ表示、引数に -d と表示を行いたいディレクトリを指定します。

$ ruby tree.rb -d ~/nifty 
|-+ /home/tetsu/nifty
  |-+ awk
  |-+ oracle
  |-+ perl
  | |-+ j
  | |-+ qa
  |-+ ruby

6 directores

ディレクトリの深さを -level=「数字」で指定します。

$ ruby tree.rb -d -l=3 /home/tetsu

■ オプション

オプション説明
-dディレクトリのみ表示
-l=数字ディレクトリを深さ「数字」まで表示
-a.」で始まるファイルも表示
-fファイル名をソートしない
ディレクトリ名 表示を開始するディレクトリの指定

■ ソースコード

Web ブラウザでの表示上は Ruby のスクリプトですが、HTML の特殊文字をエスケープしています。 このためこのソースコードを利用する場合には、 テキストファイルとしてセーブしてください。

#! /usr/local/bin/ruby
# /home/tetsu/src/ruby/toolbox/tree.rb
# Created: February 21,1998 Saturday 03:12:14
# Author: tetsu(WATANABE Tetsuya)
# $Id: tree.rb,v 1.13 2001/10/09 19:35:41 tetsu Exp $
# usage: tree.rb [-adf] [-l=number] [dir_name]

def usage
  STDERR.print <<E
usage: #$0 [-adf] [-level=number] [directory_name]
 -a     all files(include .file)
 -f     no sort
 -d     directory only
 -l=num depth of directores
E
  exit 1
end

def tree(dir, level)
  return if $opt_l and level >= $opt_l.to_i

  dirp = Dir.open(dir)
  files = []
  for f in dirp
    next unless $opt_a or f !~ /^\./
    next if f == '.' or f == '..'
    full = dir + File::Separator + f
    next if $opt_d and not File.directory? full
    files.push(full)
  end
  dirp.close

  files = files.sort unless $opt_f

  prefix = '  ' + $prefix.join('')
  $prefix.push('| ')

  cnt = 1

  for f in files
    if files.size == cnt
      $prefix[level] = '  '
    end
    cnt += 1

    lstat = File.lstat(f)

    if lstat.directory?
      print prefix, '|-+ ', File.basename(f),
        if lstat.symlink? then ' -> ' + File.readlink(f) else '' end,
        "\n"
      $cnt_dir += 1
      tree(f, level + 1) unless lstat.symlink?
    else
      print prefix, '|-- ', File.basename(f),
        if lstat.symlink? then ' -> ' + File.readlink(f) else '' end,
        "\n"
      $cnt_file += 1
    end
  end
  $prefix.pop
end

def pr_cnt
  print "\n"
  if ($cnt_dir > 0)
    print $cnt_dir, ' director', if $cnt_dir > 1 then 'es' else 'y' end 
  end
  if ($cnt_file > 0)
    print ' ' if $cnt_dir
    print $cnt_file, ' file', if $cnt_file > 1 then 's' else '' end
  end
  print "\n"
end

while $_ = ARGV[0] and ~/^-/
  ARGV.shift
  if ~/[adf]/
    $opt_a = 1 if ~/a/
    $opt_d = 1 if ~/d/
    $opt_f = 1 if ~/f/
  elsif ~/^-(l|level)=(\d+)/
    $opt_l = $2
  else
    usage
  end
end

$cnt_dir = $cnt_file = 0
$prefix = []
dir = ARGV.shift || '.'
print '|-+ ', dir, "\n"
tree(dir, 0)
pr_cnt

■ 解説

ディレクトリ(最近はフォルダと書いたほうがなじみがありますか?)は、階層的になっています。 これをそのまま階層的に表示しようというコマンドです。 あるディレクトリ以下のすべてのファイル名を使いたい場合には Find モジュールなどがあります。 ディレクトリ以下を探しまわるようなときなどに参考にしてみてください。

ある日の私のホームディレクトリです。

$ cd
$ tree.rb | tail -1
1667 directores 82350 files
Mozilla の cache などもあるためと思いますが、たくさんですね。 このくらいのファイルやディレクトリを扱うのに top コマンドで確認したところ 2.5MB 程度のメモリを使っているみたいです。 思ったよりすくないのですね。 これは、すべてのファイルの情報を抱え込むのではなく、一番ディレクトリが深いところまでを抱え込むためです。

同様にあるディレクトリ以下をすべて扱う例として 「 du コマンド 」 があります。 こちらもどうぞ。

Find モジュールの使用例としては 「 dircmp コマンド 」 があります。

■ 履歴

1.13 2001/10/9

File::Stat.readlink なんてやっていたので File.readlink に修正。 あれまぁ。

1.12 2001/9/17

コメントの調整。

1.11 2001/8/15

ruby 1.7.1 2001-08-06 対応です。


渡辺哲也(WATANABE Tetsuya): Tetsuya.WATANABE atmark nifty.com