最小限のls -laを書いてみた
プログラミング初心者にオススメのPerl本にて、リャマ本(『初めてのPerl』)を読了した段階でls(1)
やgrep(1)
の簡易版を作れるようになっていればよい、と書きました。そこで実装例として、最小限のls(1)
を実装してみました。また、リャマ本読了程度の知識を前提にすることと、Perl 5.8ベースで標準モジュールを以外は使わない<ことを制約としました。なお、所要時間は一時間程度です。
仕様は最小限ながらそれなりに役立つようにしたかったので、以下のようにしました。
- 引数としてディレクトリ名をうけとる
- 引数を省略するとカレントディレクトリ(.)になる
- オプションは
-la
(長い出力ですべてのファイルを表示)相当で固定 - group名、user名は省略し、パーミッション・ファイル更新日時・ファイル名を出
実装の際のポイントは以下のとおりです。
- stat()やlocaltime()の戻り値は扱いづらいため、File::stat/Time::localtimeモジュールを使った
perldoc -f stat
,perldoc -f localtime
にこれらモジュールを使うと楽である旨が書かれている
- パーミッションは扱いづらいのでFcntlモジュールを使った
- その際
perldoc -f stat
やstat(2)
を参照した
- その際
- ファイルサイズや日付を固定幅で出力するためにsprintf()を使った
パーミッションの文字列化以外はそれほど難しいところはありません。ぜひ、自分でも実装してみてください。
#!perl # tinyls.pl use strict; use warnings; use File::stat qw(stat); use Time::localtime qw(localtime); use Fcntl qw(:mode); main(@ARGV); sub main { my(@argv) = @_; @argv = ('.') if !@argv; foreach my $dir(@argv) { print list_one($dir); } } sub list_one { my($dir) = @_; my @output; opendir my $dh, $dir or die "Cannot opendir '$dir': $!"; foreach my $file(sort readdir $dh) { my $st = stat("$dir/$file") or die "Cannot stat '$dir/$file': $!"; my $mode = make_mode( $st->mode ); my $size = sprintf '%8s', $st->size; my $tm = localtime($st->mtime); my $mtime = sprintf '%d-%02d-%02d %02d:%02d:%02d', $tm->year + 1900, $tm->mon + 1, $tm->mday, $tm->hour, $tm->min, $tm->sec; push @output, "$mode $size $mtime $file\n"; } closedir $dh; return join '', @output; } sub make_mode { my($mode) = @_; my @m; push @m, $mode & S_IFDIR ? 'd' : '-'; push @m, make_perm($mode, S_IRUSR, S_IWUSR, S_IXUSR); push @m, make_perm($mode, S_IRGRP, S_IWGRP, S_IXGRP); push @m, make_perm($mode, S_IROTH, S_IWOTH, S_IXOTH); return join '', @m; } sub make_perm { my($m, $r, $w, $x) = @_; return join '', $m & $r ? 'r' : '-', $m & $w ? 'w' : '-', $m & $x ? 'x' : '-', ; } __END__
実行結果はたとえば以下のようになります。
App-tinyls$ perl tinyls.pl drwxr-xr-x 4096 2011-05-14 13:00:39 . drwxr-xr-x 4096 2011-05-14 11:32:31 .. drwxr-xr-x 4096 2011-05-14 13:02:55 .git -rw-r--r-- 200 2011-05-14 11:32:31 .gitignore -rw-r--r-- 174 2011-05-14 11:32:31 .shipit -rw-r--r-- 201 2011-05-14 11:32:31 Changes -rw-r--r-- 98 2011-05-14 11:32:31 MANIFEST.SKIP -rw-r--r-- 993 2011-05-14 11:33:06 Makefile.PL -rw-r--r-- 766 2011-05-14 11:32:31 README drwxr-xr-x 4096 2011-05-14 11:32:31 author drwxr-xr-x 4096 2011-05-14 11:32:31 lib drwxr-xr-x 4096 2011-05-14 13:00:31 script drwxr-xr-x 4096 2011-05-14 11:32:31 t drwxr-xr-x 4096 2011-05-14 11:32:31 xt
同じディレクトリで普通のls -la
を実行すると以下のようになります。だいたい合ってますね。
App-tinyls$ ls -la total 56 drwxr-xr-x 8 gfx gfx 4096 May 14 13:00 . drwxr-xr-x 126 gfx gfx 4096 May 14 11:32 .. drwxr-xr-x 8 gfx gfx 4096 May 14 13:02 .git -rw-r--r-- 1 gfx gfx 200 May 14 11:32 .gitignore -rw-r--r-- 1 gfx gfx 174 May 14 11:32 .shipit -rw-r--r-- 1 gfx gfx 201 May 14 11:32 Changes -rw-r--r-- 1 gfx gfx 98 May 14 11:32 MANIFEST.SKIP -rw-r--r-- 1 gfx gfx 993 May 14 11:33 Makefile.PL -rw-r--r-- 1 gfx gfx 766 May 14 11:32 README drwxr-xr-x 2 gfx gfx 4096 May 14 11:32 author drwxr-xr-x 3 gfx gfx 4096 May 14 11:32 lib drwxr-xr-x 2 gfx gfx 4096 May 14 13:00 script drwxr-xr-x 2 gfx gfx 4096 May 14 11:32 t drwxr-xr-x 2 gfx gfx 4096 May 14 11:32 xt
See also:
- Perlの組み込み関数 stat の翻訳 - perldoc.jp
- Perlの組み込み関数 localtime の翻訳 - perldoc.jp
- Perlの組み込み関数 sprintf の翻訳 - perldoc.jp
- File::stat - by-name interface to Perl's built-in stat() functions - metacpan.org
- Time::localtime - by-name interface to Perl's built-in localtime() function - metacpan.org
- Man page of STAT
追記:
id:dankogai氏に勝手に添削されました。
404 Blog Not Found:perl - 勝手に添削 - 最小限のls -laを書いてみた
明らかに too tiny だったので。
ということでした!