0
#/usr/bin/env perl
# Last modified: Sat Aug 24 2024 10:48:44 PM -04:00 [EDT]
#
use File::Which;
use File::Basename;
use File::stat;
use Digest::MD5;
# use Data::Dump qw/dump/;

use warnings;

=head1 Installation

C<chmod 700 perlwitch> - on GNU/Linux and similar *nix-ish systems

=head1 Usage

C<perl perlwitch>

=cut

my @heap = which( 'perl' );
my $trimmed;
my $struct; my $index = 0;
my @versions;

print "We are running [$^X] on $^O which is perl $^V\n";

if (($^O eq 'cygwin') or ($^O eq 'MSWin32')) {
    no warnings;
    my $pn;
    my $i = 0;
    for $pn (@heap) {
        unless ( $pn =~m/(\.exe)$/i ) {
        splice (@heap, $i, 1);
        } else {
        #   print "\nItem $i Not removed from data: matched $1 :", $pn, qq[\n];
        $i++
        }
    }
}

$index = 0;
my $strl = 0; my $s = 0; my $first = 0;
for $s (@heap) {
  if (length($s) >= $strl) { $strl =  length $s; }
      open READPIPE, '-|', $s, '-v' || warn "Call a plumber! Pipe broken on attempted fork!\n  $!";
      while (<READPIPE>) {
            if ($_ =~m/This is perl .+(v[\.[[:digit:]]+)\) /) { push @versions, ($1) }
      }
      close READPIPE || warn "Got a bad close: $?\n";
}

my $fmt  = $strl + 6;

for $path (@heap) {
     $struct->[$index]->[0] = $path;
     $struct->[$index]->[1] = $versions[$index];
     my $arro = csum($path);
     my $st = stat($path);
     my $bytesize = $st->size;
     $struct->[$index]->[2] = $bytesize;

     my (undef, $dir, undef) = fileparse($path);
     $struct->[$index]->[3]  = $dir;
     $struct->[$index]->[4]  = $arro;
     $trimmed = $strl - 4;
     print sprintf(qq/%-${fmt}s/, $path), $versions[$index], q[ ], $bytesize, q[ ],
        q[ ], sprintf(qq/%-${trimmed}s/, $dir), q[ ], sprintf("%36s", $arro), qq[\n];
     $index++;
}

# print dump $struct; print qq[\n];

sub csum {
# we pass in a fqual filename
    my $fpath = shift @_;
    open( my $fhandl, '<',  $fpath ) || warn "Failed open on file $fpath: $!" && return q[];
    binmode( $fhandl );
    my $check = Digest::MD5->new->addfile($fhandl)->hexdigest;
    close( $fhandl );
    return $check;
}

__END__

=pod

The purpose of this program is to locate all installations of Perl on a system that are locateable
in the system $PATH. It might be a box you haven't used for  while, or perhaps one you
are unfamiliar with. It will list these with a checksum for each, making it easy to see if there
are any duplicates (such as might be found where some symlink sorcery is involved, like linking F</bin> to
F</usr/bin>. The widely used (and wisely used) Debian GNU/Linux now does this, for example.

=head3 Sample output from a complex case: cygwin and MSWin32 perls on the same system

    We are running [C:\perl\perl\bin\perl.exe] on MSWin32 which is perl v5.32.1
    C:\ix\cygwin\bin\perl.EXE      v5.36.3 12819  C:\ix\cygwin\bin\         68ba8b0c3d9bb4859076e938e0cbdafe
    C:\perl\perl\bin\perl.EXE      v5.32.1 39936  C:\perl\perl\bin\         3686d8a7e98b82a6452f88fef293ca1a

=head3 TODO

Get script working on cygwin, which is not displaying correctly.

=head2 License

Copyright (c) 2024 Soren Andersen. All rights reserved.

This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=cut
enter code here
6
  • 3
    Hi amphibole, you may also want to try your question on codereview.stackexchange.com if you are specifically soliciting feedback on the code itself. Commented Aug 25, 2024 at 9:16
  • Trying to alter an array while iterating over it seems like a bad idea. Iterate over the indexes instead. Commented Aug 25, 2024 at 13:39
  • Instead of trying to assemble your struct at the end and relying on arrays being matched, you should start collecting with the struct and delete content as needed after. Commented Aug 25, 2024 at 13:45
  • I don't quite get the point of your code. You start by executing which( 'perl' ), which, I guess, returns an array of all executables named perl in your PATH (is which a standard function in Perl?). This means that you don't look for perl installed in your file system, but those that are also in your PATH. Is this really what you want to do? Commented Aug 26, 2024 at 10:26
  • Hmm... "installed" would be directly related to "in a folder included in $PATH" - all else is a half-hearted hack; available, but not installed. Commented Aug 26, 2024 at 10:36

3 Answers 3

3

In a Linux-like environment you should have type available.

$ type -a perl
perl is /usr/bin/perl
perl is /bin/perl

... so you could do a much simpler check,
or resort to running the portion of your script that displays a shorter version number:

$ type -a perl | cut -d' ' -f3 | while read name ;\  
  do echo -e "\n\n[ --- $name --- ]"; $name --version ;done


[ --- /usr/bin/perl --- ]

This is perl 5, version 38, subversion 2 (v5.38.2) built for x86_64-linux-gnu-thread-multi
(with 44 registered patches, see perl -V for more detail)

Copyright 1987-2023, Larry Wall

Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl".  If you have access to the
Internet, point your browser at https://www.perl.org/, the Perl Home Page.



[ --- /bin/perl --- ]

This is perl 5, version 38, subversion 2 (v5.38.2) built for x86_64-linux-gnu-thread-multi
(with 44 registered patches, see perl -V for more detail)

Copyright 1987-2023, Larry Wall

Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl".  If you have access to the
Internet, point your browser at https://www.perl.org/, the Perl Home Page.

$
Sign up to request clarification or add additional context in comments.

1 Comment

Nice use of *nix shell tools. I like shell scripting but I am a bit rusty, as I am with Perl. Writing this in Perl was an exercize to recall skills I'd forgotten, and also done because MS Windows does not have which or type; Thus the Perl script is more portable.
1

As suggested by @Hannu, in Linux-like environments, exploit the type -a, perhaps like:

$ cat showperls
#!/usr/bin/env bash
for which in $(type -pa perl|cut -d" " -f3)
do
    printf "%s %s\n" $which, $($which -V:version)
done
$ ./showperls 
/Users/jrf/perl5/perlbrew/perls/perl-5.40.0/bin/perl, version='5.40.0';
/usr/bin/perl, version='5.34.1';

The -p option of type:

...either returns the name of the disk file that would be executed if name were specified as a command name, or nothing if ''type -t name'' would not return file. See https://linux.die.net/man/1/bash

1 Comment

Nice one. Very adept use of unix shell tools. Thank you.
1

I've posted the final, debugged version of perlwitch on github:

This code is now working properly on Gnu/Linux, MSWindows, and Cygwin in Windows.

#/usr/bin/env perl
# Last modified: Thu Aug 29 2024 05:22:45 PM -04:00 [EDT]
#
use File::Which;
use File::Basename;
use File::stat;
use Digest::MD5;
# use Data::Dump qw/dump/;

use warnings;

=head1 Installation

(optional) C<chmod 700 perlwitch> - on GNU/Linux and similar *nix-ish systems

=head1 SYNOPSIS

C<perl perlwitch>

=cut

my @heap = which( 'perl' );
my $trimmed;
my $struct; my $index = 0;
my @versions;

print "We are running [$^X] on $^O which is perl $^V\n";
print 'All perls installed to the system $PATH:'."\n";

if (($^O eq 'cygwin') or ($^O eq 'MSWin32'))
{
    no warnings;
    my $pn; my @selected;
    for $pn (@heap) {
        if ( $pn =~m/(\.exe)$/i ) {
        push(@selected, $pn);
        }
    }
    @heap = @selected; # overwrite instance array
}

$index = 0;
my $strl = 0; my $s = 0; my $first = 0;
for $s (@heap) {
  if (length($s) >= $strl) { $strl =  length $s; }
      open READPIPE, '-|', $s, '-v' || warn "Call a plumber! Pipe broken on attempted fork!\n  $!";
      while (<READPIPE>) {
            if ($_ =~m/This is perl .+(v[\.[[:digit:]]+)\) /) { push @versions, ($1) }
      }
      close READPIPE || warn "Got a bad close: $?\n";
}

my $fmt  = $strl + 6;

for $path (@heap) {
     $struct->[$index]->[0] = $path;
     $struct->[$index]->[1] = $versions[$index];
     my $arro = csum($path);
     my $st = stat($path);
     my $bytesize = $st->size;
     $struct->[$index]->[2] = $bytesize;

     my (undef, $dir, undef) = fileparse($path);
     $struct->[$index]->[3]  = $dir;
     $struct->[$index]->[4]  = $arro;
     $trimmed = $strl - 4;
     print sprintf(qq/%-${fmt}s/, $path), $versions[$index], q[ ], $bytesize, q[ ],
        q[ ], sprintf(qq/%-${trimmed}s/, $dir), q[ ], sprintf("%36s", $arro), qq[\n];
     $index++;
}

sub csum {
# we pass in a fqual filename
    my $fpath = shift @_;
    open( my $fhandl, '<',  $fpath ) || warn "Failed open on file $fpath: $!" && return q[];
    binmode( $fhandl );
    my $check = Digest::MD5->new->addfile($fhandl)->hexdigest;
    close( $fhandl );
    return $check;
}

__END__

=pod

=head1 DESCRIPTION

The purpose of this program is to locate all installations of Perl on a system that are locateable
in the system $PATH. It might be a box you haven't used for  while, or perhaps one you
are unfamiliar with. It will list these with a checksum for each, making it easy to see if there
are any duplicates (such as might be found where some symlink sorcery is involved, like linking F</bin> to
F</usr/bin>. The widely used (and wisely used) Debian GNU/Linux now does this, for example.

=head3 Sample ERRONIOUS output from a complex case: cygwin and MSWin32 perls on the same system

  $ perl perlwitch
  /usr/bin/perl                           v5.36.3 12819  /usr/bin/                          68ba8b0c3d9bb4859076e938e0cbdafe
  /usr/bin/perl.exe                       v5.36.3 12819  /usr/bin/                          68ba8b0c3d9bb4859076e938e0cbdafe
  /cygdrive/c/perl/perl/bin/perl          v5.32.1 39936  /cygdrive/c/perl/perl/bin/         3686d8a7e98b82a6452f88fef293ca1a
  /cygdrive/c/perl/perl/bin/perl.exe      v5.32.1 39936  /cygdrive/c/perl/perl/bin/         3686d8a7e98b82a6452f88fef293ca1a

=head1 AUTHOR

Soren Andersen

=head1 BUGS / TODO

Get script working on cygwin, which is not displaying correctly.

=head1 COPYRIGHT

Copyright (c) 2024 Soren Andersen. All rights reserved.

This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.

=cut

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.