読者です 読者をやめる 読者になる 読者になる

Perl日記

PerlとかRubyとかPHPとかPythonとか

lsコマンドをPerlで

「ls」コマンドをPerlで実装してみた。
端末の画面幅の取得の仕方がわからなかった。
なのでとりあえずファイル名出力列は4行固定。
汚染チェックもいるのか不明。

#!/usr/bin/perl -T
# ls.pl
# "ls"コマンドをPerlで実装
use strict;
use warnings;
use List::Util qw(max);
require Carp;

my $readkey_ok = eval {
	local $SIG{__DIE__};
	local $SIG{__WARN__};
	require Term::Readkey; # 端末画面幅を取得したい
};

# 表示ディレクトリ
my $dir = shift || './'; # 引数が指定されていなければカレントをデフォルトにする

# untainted
$dir =~ s/^(\w+)$/$1/;

# ディレクトリ内ファイル取得
opendir my $dh, $dir
	or Carp::croak("ls.pl: $dir: No Such file or directory");
my @files = map  { {name => $_.q{  }, len => length($_)} }
            sort grep { !/^\./ } # 不可視ファイルを無視
            readdir $dh;
closedir $dh;

# ファイル名最大長
my $maxlen_fname = max(@{[map($_->{len}, @files)]});
# ファイル数
my $count_file = @files;

# もしファイルがあれば
if ($count_file) {
	my $cols = 4; # 列数
	my $rows = ($count_file % $cols) # 行数
	         ? int($count_file / $cols + 1)
	         : int($count_file / $cols);
	my @print_arrays = ('dummy');
	while (@files) {
		my @arr = ('dummy');
		push @arr, shift(@files) for 1 .. $rows;
		for my $i (1 .. $rows) {
			my $suff = ($i % $rows) ? ($i % $rows) : $rows;
			if ($arr[$i]) { # そこに出力ファイルがあれば
				if (my $diff_len = $maxlen_fname - $arr[$i]->{len}) {
					$arr[$i]->{name} = $arr[$i]->{name}.(q{ } x $diff_len);
				}
				push @{$print_arrays[$suff]}, $arr[$i]->{name};
			}
		}
	}
	shift @print_arrays;
	for my $print_col (@print_arrays) {
		print "@$print_col\n";
	}
}

__END__