Rubyでwhereコマンドを書いてみた (Windows)

実際書いたのはちょっと前ですが、Windowsにwhere(which)コマンドがないのでRubyのコーディング練習がてら自分で書いてみました。


whereっぽい実装ですが、厳密にはPATH中の実行ファイルを探してすべて表示するだけのプログラムです。
# whereとwhichの動作って違うんですね。


さっそくコードです。
スパゲッティコードなので、エラーとか例外とかの処理は省略してます。

find_target = ARGV.shift
paths = ENV["PATH"].split(";")
pathexts = [""] + ENV["PATHEXT"].split(";").map{|ext| ext.downcase}
found = paths.map {|path|
    pathexts.map {|ext|
        "#{path}\\#{find_target}#{ext}"
    }.delete_if {|target|
        not File.exist?(target)
    }
}.flatten!

if found.empty?
    puts "not found"
else
    puts found.join("\n")
end


コードを書いてて、検索部分が1行でかけてしまうRubyの素敵さに気づきました。
多少強引なところもありますけど。(flatten!とか)
やろうと思えばpathsとかpathextsとかも1行の中に入れれますね。
まぁ、可読性落として、そこまでする必要はないと思いますが・・・。


最初は、delete_ifの部分は、if文を使ってファイルがあればfoundに追加・・・ってしてました。
でも、先に調べるファイルの候補作ってるので、delete_ifで存在しない奴を消してしまったほうがスマートだろうと思い現在のコードに。
Cだと分岐がない方が高速になる可能性ありますけど、Rubyの場合無意味な気も・・・。


あと、Windowsに依存してるところの説明を。


まず、環境変数のPATHEXT。
ここにコマンドを実行する際に省略可能な拡張子が入っています。
これがあるから、example.exe を example とタイプするだけで実行できるみたいです。
このプログラムでは、一応拡張子がないものも検索するようにしてます。
実行はできないんですが。
pathexts に空の文字列を追加してるのを消せば本来の動作になるかと。


それと、cdとかdirについて。
コマンドプロンプトのビルトインコマンドです。
これは検索しても見つからないので、独自に対応するしかないかと。
tcshでも、

cd is a shell built-in

って出ます。


これを exerb でexeにしてやって立派な(?)where.exeとして働いてくれてます。