Multiple Which

Find all Executables in the Path

The standard Unix utility which locates executables in the user's path. On occasion, I have found myself trying to figure out if identically named executables exist in multiple directories in my path. I am sure it is possible to do this using find but then I decided a long time ago that I was incapable of doing anything moderately interesting with that program.

In the same time it would have taken to try to re-read the man page for find, I wrote the following short script which I believe is fairly portable.

On Windows, it allows one to just enter the name of the executable without the extension. It will then use the executable extensions shown in %PATHEXT% environment variable to check for the filename with those extensions appended.

The script reads program names from the command line. Program names must be plain filenames without any path information.

Here is output from a test run on my machine:

C:\Temp> multiwhich perl tex notepad
perl:
        C:\opt\Perl\bin\perl.exe
        C:\opt\cygwin\bin\perl.exe
tex:
        C:\opt\texmf\miktex\bin\tex.exe
        C:\opt\cygwin\bin\tex.exe
notepad:
        C:\WINDOWS\system32\notepad.exe
        C:\WINDOWS\notepad.exe

Please note that while I provide this script in the hopes that it will be useful to you, I make NO GUARANTEES OR WARRANTIES OF ANY KIND. The code is provided subject to the same terms as Perl itself. For more information, please refer to the Perl Artistic License.

#!/usr/bin/perl

use strict; use warnings;

use File::Basename;
use File::Spec::Functions qw( catfile path );

my $myname = fileparse $0;
die "Usage: $myname program_name\n" unless @ARGV;

my @path = path;
my @pathext = ( q{} );

if ( $^O eq 'MSWin32' ) {
    push @pathext, map { lc } split /;/, $ENV{PATHEXT};
}

PROGRAM: for my $progname ( @ARGV ) {
    unless ( $progname eq fileparse $progname ) {
        warn "Not processed: $progname\n\tArgument is not a plain file name\n";
        next PROGRAM;
    }

    my @results;

    for my $dir ( @path ) {
        for my $ext ( @pathext ) {
            my $f = catfile $dir, "$progname$ext";
            push @results, $f if -x $f;
        }
    }

    print "$progname:\n";
    print "\t$_\n" for @results;
}