Unix 系のコマンドで、ls コマンドはみなさんお世話になっていると思います。
この ls コマンドに -l オプションで使うこともとても多いと思います。
ll.rb スクリプトは、ls -l オプションと同等品で、そのほかに -F オプション --color オプションなどもサポートしています。
Ruby で、ファイルの情報をとりだすサンプルになりますので試してみてください。
デフォルトでは、ls -l と同じ動作を行います。
オプションは次のものがあります。
オプション 説明 -a「.」ではじまるファイル(ディレクトリ)名も表示 -cctime でソート -d引数でしていされたディレクトリは展開せずにそのまま -fソートしません -tmtime でソート -uatime でソート -Fファイル名にシンボルをつけてファイルのタイプ別に表示 -pPukiWiki ファイル名を漢字ファイル名に変換 --colorカラーでファイルのタイプ別に表示
実行サンプルです。
$ ll.rb /t/lib/ruby/1.6/i686-linux total 1060 -rw-r--r-- 1 root root 2596 Aug 25 21:46 config.h -r-xr-xr-x 1 root root 13788 Sep 1 02:05 curses.so* -rw-r--r-- 1 root root 1874 Aug 25 21:46 defines.h -rw-r--r-- 1 root root 622 Aug 25 21:46 dln.h -rw-r--r-- 1 root root 1093 Sep 1 01:05 env.h -r-xr-xr-x 1 root root 6724 Sep 1 02:05 etc.so* -r-xr-xr-x 1 root root 4272 Sep 1 02:05 fcntl.so* -r-xr-xr-x 1 root root 12212 Sep 1 02:05 gdbm.so* -r-xr-xr-x 1 root root 27844 Aug 29 02:28 getconf.so* -rw-r--r-- 1 root root 13202 Aug 29 19:58 intern.h -rw-r--r-- 1 root root 787520 Sep 1 20:01 libruby-1.6.0.a -r-xr-xr-x 1 root root 8004 Sep 1 02:05 md5.so* -r-xr-xr-x 1 root root 15348 Sep 1 02:05 nkf.so* -rw-r--r-- 1 root root 10512 Aug 30 21:28 node.h -r-xr-xr-x 1 root root 9108 Sep 1 02:05 pty.so* -rw-r--r-- 1 root root 3862 Sep 1 20:01 rbconfig.rb -rw-r--r-- 1 root root 981 Aug 25 21:46 re.h -r-xr-xr-x 1 root root 10368 Sep 1 02:05 readline.so* -rw-r--r-- 1 root root 7984 Aug 25 21:46 regex.h -rw-r--r-- 1 root root 15701 Sep 1 01:05 ruby.h -rw-r--r-- 1 root root 1713 Aug 25 21:46 rubyio.h -rw-r--r-- 1 root root 1404 Aug 25 21:46 rubysig.h -r-xr-xr-x 1 root root 17332 Sep 1 02:05 sdbm.so* -r-xr-xr-x 1 root root 27656 Sep 1 02:05 socket.so* -rw-r--r-- 1 root root 1049 Aug 25 21:46 st.h -r-xr-xr-x 1 root root 11032 Sep 1 02:05 tcltklib.so* -r-xr-xr-x 1 root root 4200 Sep 1 02:05 tkutil.so* -rw-r--r-- 1 root root 1220 Aug 25 21:46 util.h -rw-r--r-- 1 root root 133 Sep 1 20:01 version.h
zsh などのシェルでは、次のように alias するといいでしょう。
alias ll='ll.rb --color -F'
Web ブラウザでの表示上は Ruby のスクリプトですが、HTML の特殊文字をエスケープしています。 テキストとしてセーブしてご利用ください。
#! /usr/local/bin/ruby
# /home/tetsu/src/ruby/toolbox/ll.rb
# Created: August 22,2000 Tuesday 10:09:39
# Author: tetsu(WATANABE Tetsuya)
RCS_ID = %q$Id: ll.rb,v 1.26 2005/12/03 10:37:00 tetsu Exp $
# usage:
def usage
STDERR.print <<EOF
usage: #{$0} [-acdftuFp] [dir, file...]
-a all
-d directory
-F classify
-c sort of ctime
-t sort of mtime
-u sort of atime
-f no sort
-p pukiwiki name
#{RCS_ID}
EOF
exit 1
end
require 'etc'
class Dir_colors
FILE_NAME = '/etc/DIR_COLORS'
@@color_str = nil
def initialize
if @@color_str.nil?
@term = {}
@str = {}
begin
File.foreach(FILE_NAME) do |l|
next if l =~ /^\#/ || l =~ /^\s*$/
l.sub!(/\#.*$/, '')
l.sub!(/\s*$/, '')
# key, val = l.chop.split(/[\s]+/, 2)
key, val = l.split(/[\s]+/, 2)
if key == 'TERM'
@term[val] = true
elsif
@str[key] = "\C-[[" + val + 'm'
end
end
if @term[ENV['TERM']].nil?
@str.each do |k, val|
val.replace('')
end
end
rescue Errno::ENOENT
case ENV['TERM']
when 'kterm', 'xterm', 'linux'
@str = {
'NORMAL' => "\C-[[00m",
'DIR' => "\C-[[01m",
'FIFO' => "\C-[[40;33m",
'LINK' => "\C-[[01;36m",
'SOCK' => "\C-[[01;35m",
'EXEC' => "\C-[[01;32m",
'.jpg' => "\C-[[00;35m",
'.gif' => "\C-[[00;35m",
'.bmp' => "\C-[[00;35m",
'.xbm' => "\C-[[00;35m",
'.xpm' => "\C-[[00;35m",
'.png' => "\C-[[00;35m",
'.tif' => "\C-[[00;35m"
}
end
end
@@color_str = @str
else
@str = @@color_str
end
end
attr_reader :term, :str
end
class Ls
@@uid2n = {
0 => 'root'
}
@@gid2n = {
0 => 'root'
}
@@mode2str = {
0100644 => '-rw-r--r--',
0040755 => 'drwxr-xr-x',
0100755 => '-rwxr-xr-x',
0100444 => '-r--r--r--',
0100600 => '-rw-------',
0060660 => 'brw-rw----',
0100555 => '-r-xr-xr-x',
0040775 => 'drwxrwxr-x',
0020660 => 'crw-rw----',
0120777 => 'lrwxrwxrwx'
}
@@time_ck = Time.now - 6 * 30 * 24 * 60 * 60
@@istty = nil
def initialize(opt, arg = '.')
@opt = opt
@arg = arg
@color = Dir_colors.new if opt['color']
if @@istty.nil?
@@istty = STDOUT.tty?
@@istty = false if @color.nil?
end
@name_arr = []
@stat_arr = {}
@total = 0
@lstat = File.lstat(arg)
@type = if @lstat.symlink?
'file'
elsif @lstat.directory?
if opt['directory'] then 'file' else 'directory' end
else
'file'
end
case @type
when 'file'
@name_arr.push(arg)
@stat_arr[arg] = @lstat
when 'directory'
Dir.open(arg).each do |f|
next if f =~ /^\./ && !@opt['all']
@name_arr.push(f)
@stat_arr[f] = File.lstat(arg + '/' + f)
@total += @stat_arr[f].blocks
end
end
end
def add_file(arg)
@name_arr.push(arg)
@stat_arr[arg] = File.lstat(arg)
end
def ll(file, stat)
cfile = name2color(file, stat)
if @opt['pwiki_name'] && file =~ /[A-F\d]{2}/
cfile += ' ' + file.gsub(/[A-F\d]{2}/) {|x| [x.hex].pack('C*') }
end
time_val = if @opt['ctime_sort']
stat.ctime
elsif @opt['atime_sort']
stat.atime
else
stat.mtime
end
time_fmt = if time_val < @@time_ck then '%b %d %Y' else '%b %d %H:%M' end
begin
@@uid2n[stat.uid] = Etc.getpwuid(stat.uid).name unless @@uid2n.key?(stat.uid)
rescue ArgumentError
@@uid2n[stat.uid] = stat.uid.to_s
end
begin
@@gid2n[stat.gid] = Etc.getgrgid(stat.gid).name unless @@gid2n.key?(stat.gid)
rescue ArgumentError
@@gid2n[stat.gid] = stat.gid.to_s
end
if stat.blockdev? or stat.chardev?
str = sprintf("%-10s %3d %-8s %-8s %3d, %3d %s %s",
mode2str(stat),
stat.nlink,
@@uid2n[stat.uid],
@@gid2n[stat.gid],
stat.rdev_major,
stat.rdev_minor,
time_val.strftime(time_fmt).sub(/(\w{3}) 0(\d)\b/, '\1 \2'),
cfile)
else
str = sprintf("%-10s %3d %-8s %-8s %8d %s %s",
mode2str(stat),
stat.nlink,
@@uid2n[stat.uid],
@@gid2n[stat.gid],
stat.size,
time_val.strftime(time_fmt).sub(/(\w{3}) 0(\d)\b/, '\1 \2'),
cfile)
end
bad_link = false
if stat.symlink?
link = ''
begin
f = if @type == 'file' then file else @arg + '/' + file end
stat = File.stat(f)
linkname = File.readlink(f)
link = ' ' + name2color(linkname, stat)
rescue Errno::ENOENT
bad_link = true
end
str.concat(' ->' + link)
end
if ! bad_link && @opt['classify']
if stat.directory?
str.concat('/')
elsif stat.symlink?
str.concat('@')
elsif stat.socket?
str.concat('=')
elsif stat.pipe?
str.concat('|')
elsif stat.executable?
str.concat('*')
end
end
str.concat("\n")
end
def name2color(name, stat)
if @@istty && @opt['color']
if stat.directory?
@color.str['DIR'] + name + @color.str['NORMAL']
elsif stat.symlink?
@color.str['LINK'] + name + @color.str['NORMAL']
elsif stat.socket?
@color.str['SOCK'] + name + @color.str['NORMAL']
elsif stat.pipe?
@color.str['FIFO'] + name + @color.str['NORMAL']
elsif stat.executable?
@color.str['EXEC'] + name + @color.str['NORMAL']
elsif name =~ /\.(?:jpg|gif|bmp|xbm|xpm|png|tif)$/
@color.str[$&] + name + @color.str['NORMAL']
else
name
end
else
name
end
end
def display(port = $>)
begin
port.write self
rescue Errno::EPIPE
end
end
def to_s
unless @opt['no_sort']
if @opt['mtime_sort']
@name_arr.sort! do |a, b|
[@stat_arr[b].mtime, a] <=> [@stat_arr[a].mtime, b]
end
elsif @opt['ctime_sort']
@name_arr.sort! do |a, b|
[@stat_arr[b].ctime, a] <=> [@stat_arr[a].ctime, b]
end
elsif @opt['atime_sort']
@name_arr.sort! do |a, b|
[@stat_arr[b].atime, a] <=> [@stat_arr[a].atime, b]
end
else
@name_arr.sort!
end
end
str = ''
str.concat(sprintf("total %d\n", @total / 2)) if @type == 'directory'
@name_arr.each do |f|
str.concat(ll(f, @stat_arr[f]))
end
str
end
def mode2str(stat)
m = stat.mode
return @@mode2str[m] if @@mode2str.key?(m)
str = '----------'
str[0] = if stat.file?; '-'
elsif stat.directory?; 'd'
elsif stat.symlink?; 'l'
elsif stat.socket?; 's'
elsif stat.pipe?; 'p'
elsif stat.chardev?; 'c'
elsif stat.blockdev?; 'b'
else '-'
end
str[1] = if m & 0400 == 0 then '-' else 'r' end
str[2] = if m & 0200 == 0 then '-' else 'w' end
str[3] = if m & 0100 == 0
if stat.setuid? then 'S' else '-' end
else
if stat.setuid? then 's' else 'x' end
end
str[4] = if m & 040 == 0 then '-' else 'r' end
str[5] = if m & 020 == 0 then '-' else 'w' end
str[6] = if m & 010 == 0
if stat.setgid? then 'S' else '-' end
else
if stat.setgid? then 's' else 'x' end
end
str[7] = if m & 04 == 0 then '-' else 'r' end
str[8] = if m & 02 == 0 then '-' else 'w' end
str[9] = if m & 01 == 0
if stat.sticky? then 'T' else '-' end
else
if stat.sticky? then 't' else 'x' end
end
@@mode2str[m] = str
end
end
Opt = {
'all' => false,
'directory' => false,
'atime_sort' => false,
'ctime_sort' => false,
'mtime_sort' => false,
'no_sort' => false,
'classify' => false,
'color' => false,
'pwiki_name' => false
}
while ARGV[0] =~ /^-/
$_ = ARGV.shift
if ~/^--color/
Opt['color'] = true
elsif ~/^--/
usage
else
Opt['all'] = true if ~/a/
Opt['ctime_sort'] = true if ~/c/
Opt['directory'] = true if ~/d/
Opt['no_sort'] = true if ~/f/
Opt['mtime_sort'] = true if ~/t/
Opt['atime_sort'] = true if ~/u/
Opt['classify'] = true if ~/F/
Opt['pwiki_name'] = true if ~/p/
usage if ~/[^\-acdftuFp]/
end
end
if ARGV.size == 0
ls = Ls.new(Opt)
ls.display
elsif ARGV.size == 1
ls = Ls.new(Opt, ARGV.shift)
ls.display
else
flag_init = true
ARGV.each do |arg|
if File.symlink?(arg) || Opt['directory'] || !File.directory?(arg)
if flag_init
ls = Ls.new(Opt, arg)
flag_init = false
else
ls.add_file(arg)
end
end
end
ls.display unless flag_init
ARGV.each do |arg|
unless File.symlink?(arg) || Opt['directory'] || !File.directory?(arg)
ls = Ls.new(Opt, arg)
print "\n", arg, ':', "\n"
ls.display
end
end
end
この ll.rb は、まさに ls -l コマンドそのままです。
ということで、コマンドとしては特別なことはなく、オプションで指定した動きをします。
ただ、余計なマスクとかはしていないので漢字のファイル名もそのまま表示します。
Win のパーティションやドライブを mount している場合に便利だったりします。
処理的には次のようなことをしています。
/etc/DIR_COLORS 対応)
デバイスファイル対応。 stat.mode の数字を使っていた部分を File::Stat で置き換え。
-p の PukiWiki ファイル名のエンコードオプションの対象を「.txt」以外にも拡張。
-p オプションで PukiWiki ファイル名を漢字表示。 現在は漢字コードが EUC Japan 固定です。
Ruby 1.8 対応。
File.foreach chop
2003/3/23 対応修正。
[^-abc] 表記でエスケープが必要になったため。
パーミッションのよく使われるものを先に生成するようにしました。
いままで補足していなかった例外 Errno::EPIPE を補足するようにしました。
拡張子のカラー化の正規表現を間違っていました。
ruby 1.7.1 2001-08-06 対応です。