Have you ever used "./configure" in any Open source applications, before compiling them. There it will stop the user from moving ahead if something is not found. I was searching for such a thing in Perl and atlast wrote this piece. As like any programmer I've added many cool options to it. This script will check for necessary tools in system and returns 0 on sucess, 1 on failure. This would be primarily used in "pre-checking" stage, before doing anything big. Pod also added.

#!/usr/bin/perl $OSDEF = `uname` ; chomp($OSDEF); use Getopt::Long ; use subs qw( error_exit usage precheck generic_check perl_module_check print_ok print_notok print_info print_verbose ); $VERSION = "1.0" ; @Default_Tools_List = ( [ 'gzip' ,'app', '' , 'generic_check' ], [ 'gunzip' ,'app', '' , 'generic_check' ], [ 'tar', ,'app', '' , 'generic_check' ], [ 'tr', ,'app', '' , 'generic_check' ], [ 'date' ,'app', '' , 'generic_check' ], [ 'ps', ,'app', '' , 'generic_check' ], [ 'cat', ,'app', '' , 'generic_check' ], [ 'id', ,'app', '' , 'generic_check' ], [ 'echo' ,'app', '' , 'generic_check' ], [ 'pwd', ,'app', '' , 'generic_check' ], [ 'chmod' ,'app', '' , 'generic_check' ], [ 'ln', ,'app', '' , 'generic_check' ], [ 'cp', ,'app', '' , 'generic_check' ], [ 'rpm', ,'app', '' , 'generic_check' ], [ 'clear' ,'app', '' , 'generic_check' ], [ 'dirname' ,'app', '' , 'generic_check' ], [ 'df' ,'app', '' , 'generic_check' ], [ 'Carp' ,'perlm', 'Perl Module - Carp', 'perl_module_check' + ], ) ; $Tools_Hash = \@Default_Tools_List ; $Debug = 1; @default_paths = qw( /bin /usr/bin /usr/sbin /usr/local/bin ); $progname = "ensure" ; $KSH = "/bin/ksh" ; $PERL = "/usr/bin/perl" ; %precheck_stuff = ($KSH, $PERL); $res = GetOptions( "v" => \$verbose, "q" => \$quiet, "h" => \$help, "s" => \$skip_default_checking, "o" => \$check_only_mypath, "l" => \$list_items, "e=s" => \$exclude_list, "c=s" => \$implicit_list, "p=s" => \$more_paths, "x=s" => \$extra_tools, "f=s" => \$tools_from_file, ); #------ Option Processing ----------------# if(! $res || defined($help)) { usage(); exit(1); } if (defined($check_only_mypath)) { $ENV{'PATH'} = "." ; } else { $ENV{'PATH'} = join(":", ($ENV{'PATH'}, @default_paths)); } @paths_to_check = split(/:/, $ENV{'PATH'}); if (defined($more_paths)) { chomp($more_paths); my @more_paths = split(/\s+/, $more_paths); foreach my $path (@more_paths) { if( -d $path || -l $path ) { push(@paths_to_check, $path ); } } } $Debug = 2 if(defined($verbose)); $Debug = 0 if(defined($quiet)) ; if(!defined($skip_default_checking)) { foreach my $comp ( @{$Tools_Hash} ) { my $entity = $comp->[0] ; $things_to_check{$entity} = 1 ; } } if (defined($extra_tools)) { chomp($extra_tools); my @extra_tools_tochk = split(/\s+/, $extra_tools); foreach $elem (@extra_tools_tochk) { push(@{$Tools_Hash}, [ $elem, 'app', $elem, 'generic_check']); $things_to_check{$elem} = 1; } } if (defined($tools_from_file)) { chomp($tools_from_file); open(TL,"$tools_from_file") or die "Can't open file $tools_from_fi +le for reading: $!\n" ; my @TList = <TL> ; close(TL); my @extra_tools_tochk ; foreach my $l (@TList) { chomp($l); push (@extra_tools_tochk, split(/\s+/, $l)); } foreach my $elem (@extra_tools_tochk) { push(@{$Tools_Hash}, [ $elem, 'app', $elem, 'generic_check']); $things_to_check{$elem} = 1; } } if (defined($exclude_list)) { chomp($exclude_list); my @exclude_checks = split(/\s+/, $exclude_list); foreach $elem (@exclude_checks) { $things_to_check{$elem} = 0 ; } } if (defined($implicit_list)) { chomp($implicit_list); my @implicit_checks = split(/\s+/, $implicit_list); if(@implicit_checks) { foreach $elem (keys(%things_to_check)) { $things_to_check{$elem} = 0 ; } } foreach $elem (@implicit_checks) { if(!exists($things_to_check{$elem})) { push(@{$Tools_Hash}, [ $elem, 'app', $elem, 'generic_check']); } $things_to_check{$elem} = 1 ; } } if(defined($list_items)) { foreach my $t ( keys(%things_to_check) ) { print "$t\n" if($things_to_check{$t} == 1); } exit(0); } #-------------- START: Main Program -------------------# $Failed = 0; $cf_flen = 40 ; precheck(); # its funny to check for perl inside a perl program :) foreach $component ( @{$Tools_Hash} ) { my $entity = $component->[0] ; # first thing is the entity to ch +eck. if($things_to_check{$entity} == 1 ) { $check_function = $component->[-1] ; # last thing is the function + to check. $ret = \&$check_function($entity, splice(@{$component},1,-1)); #r +est of the inbetween things are args to function. } } if($Failed == 1) { exit(1); } else { exit(0); } #------------- END: Main Program -------------------# sub error_exit { print STDERR @_ , "\n" ; exit(1); } sub usage { print <<HELP; Usage: $progname [-lqvhso ] [-e <exclude list> ] [ -c <implicit check +list> ] [ -x <extra tools list> ] [ -f <tools list filename> ] -l list the checking items -q deal quiet. just give the final return status. -v verbose output. -h show this help message. -e exclude checking list of items (separated by space +. use '') -s skip default tools checking. (check only implicit or from +file) -c check only listed items (separated by space. use ' +' ) -x additionaly check the listed items ( separated by space. u +se '') -f read the additional tools list from file ( one per line) -p add the provided paths (space separated ) to the list of s +earch path. -o search only in given paths (otherwise, dont check in defau +lt paths). HELP } # Check to see, if we can check others. sub precheck { my $ret = 0; for $ent (@precheck_stuff) { if( ! -f $ent ) { print_info "$ent not found\n" ; $ret = 1 if($ret == 0); } } error_exit("Exiting") if($ret); return(0); } # This is generic_check for all tools. It will search for a particular + tool in all directory in PATH variable, # and also in user provided directories if anything. sub generic_check { my($entity, @ent_args ) = @_ ; my $found = 0; ($type, $string ) = @ent_args ; $string = $entity if($string eq "" ) ; $show_user = "Checking for $string ..."; print_info sprintf "%-${cf_flen}s", $show_user ; $| = 1; # first check for simple existence. if found set status and return # else foreach path - attach the entity and check for existence -f . i +f found stop checking set status and return. # if not found return 1 print_verbose("\n checking for $entity ") ; if( -f $entity ) { $found = 1 ; print_verbose(" found\n"); } else { print_verbose("\n"); foreach $path (@paths_to_check) { $abscmd = "$path/$entity" ; print_verbose(" checking for $abscmd ") ; if( -f $abscmd ) { $found = 1 ; print_verbose(" found\n"); last ; } else { print_verbose("\n"); } } } if($found) { print_ok(); return(0); } else { $Failed = 1; print_notok(); return(1); } } sub perl_module_check { my($entity, @ent_args ) = @_ ; ($type, $string ) = @ent_args ; $string = $entity if($string eq "" ) ; $show_user = "Checking for $string ..."; print_info sprintf "%-${cf_flen}s", $show_user ; $| = 1; $ret = system("$KSH -c '$PERL -M$entity -e 0' 2>/dev/null"); if(($ret >> 8) == 0) { print_ok(); return(0); } else { $Failed = 1; print_notok(); return(1); } } sub print_ok { print_info " ok\n" ; } sub print_notok { print_info " not found\n" ; } sub print_info { if($Debug > 0) { print STDOUT @_ ; } } sub print_verbose { if($Debug > 1) { print STDOUT @_ ; } } __END__ =pod =head1 NAME ensure - Ensure that the environment is good. =head1 SYNOPSIS ensure [-lqvhso ] [-e <exclude list> ] [ -c <implicit check list> ] [ +-x <extra tools list> ] [ -f <tools list filename> ] [-p <additional paths> ] -l list the checking items -q deal quiet. just give the final return status. -v verbose output. -h show this help message. -s skip default tools checking. (check only implicit +or from file) -o search only in given paths (otherwise, dont check +in default paths). -e exclude checking list of items (separated by space +. use '') -c check only listed items (separated by space. use ' +' ) -x additionaly check the listed items ( separated by +space. use '') -f read the additional tools list from file ( one per + line) -p additional paths to search for. (space separated ) =head1 DESCRIPTION This tool checks for necessary programs in the environment ( aka. +PATH ) like gzip, tar, ls, cp, rm, etc., and report if anything is not found. It also returns '1' in case of failur +e and 0 if all tools are found. This tool can be used as pre checking script before attempting any shell script/perl re +lated installation, upgrade of any software. In case of failure you can just abort the task and report the user to +make it available. This is inspired by "configure" script in Open source packages (C, C++ ) which checks for the system f +or minimum requirement before even compiling the software. =head1 OPTIONS -l - list the name of the tools prepared to check and exit. Don't c +heck actually. -q - Do everything in quiet mode, dont display anything. Just retur +n 0 or 1 on sucess or failure respectively. -v - show what is happening internally, verbose display. -h - display help message and exit. -e - exclude the list of provided items for checking. For eg. ./ens +ure -e 'gzip tar md5sum' -c - check only list of provided items. This can be useful for sing +le tool testing. For eg. ./ensure -c 'gzip tar md5sum' -x - In addition to default tools, check also the listed items with + this option. For eg. ./ensure -x 'gzip tar md5sum' -f - Take the list of items to check from the provided file. (separ +ated by one of "\n<space>\t") -o - check only in the given paths,( see -p ) not in default $PATH +content. -p - add provided (space separated paths) to the list of paths to s +earch. -s - skip default tools checking. (check only provided list. see -c + -x -f ) =head1 EXAMPLES =head2 Simple Run [user@host:~]$ ./ensure Checking for gzip ... ok Checking for gunzip ... ok Checking for tar ... ok Checking for tr ... ok Checking for date ... ok Checking for ps ... ok Checking for cat ... ok Checking for id ... ok Checking for echo ... ok Checking for pwd ... ok Checking for chmod ... ok Checking for ln ... ok Checking for cp ... ok Checking for rpm ... ok Checking for clear ... ok Checking for dirname ... ok Checking for df ... ok Checking for Perl Module - Carp ... ok =head2 Exclude checking [user@host:~]$ ./ensure -e 'df gunzip gzip cat rpm clear dirnam +e' Checking for tar ... ok Checking for tr ... ok Checking for date ... ok Checking for ps ... ok Checking for id ... ok Checking for echo ... ok Checking for pwd ... ok Checking for chmod ... ok Checking for ln ... ok Checking for cp ... ok Checking for Perl Module - Carp ... ok =head2 Implicit Checking [user@host:~]$ ./ensure -c 'ls rm cp' Checking for cp ... ok Checking for ls ... ok Checking for rm ... ok =head2 Skip Default checking and pick the list from provided file (std +in) [user@host:~]$ ./ensure -s -f - fdisk awk at atrm crontab ^D Checking for fdisk ... ok Checking for awk ... ok Checking for at ... ok Checking for atrm ... ok Checking for crontab ... ok [user@host:~]$ =head2 Skip default checking(-s), Search only in provided paths (-o, - +p), Check only listed items. The following checking failed, because all the listed tools are + available in /bin [user@host:~]$ ./ensure -o -s -p '/usr/bin' -c 'ls cp rm' Checking for cp ... not found Checking for ls ... not found Checking for cp ... not found Checking for rm ... not found [user@host:~]$ =head1 TODO As of now only default tools existence check can be added or removed +. For any other Perl Module existence checking, we need to add the elements in array inside the program. This needs to be cu +stomized. Needs to check anything, even particular options to programs. =head1 AUTHOR S.Murali Krishnan <muralikrishnan.s@gmail.com>

-- With kind Regards, S.Murali Krishnan.

Replies are listed 'Best First'.
Data helper (was Re: ensure - Ensure that the environment is good.)
by merlyn (Sage) on Sep 18, 2007 at 15:01 UTC
    You have:
    @Default_Tools_List = ( [ 'gzip' ,'app', '' , 'generic_check' ], [ 'gunzip' ,'app', '' , 'generic_check' ], [ 'tar', ,'app', '' , 'generic_check' ], [ 'tr', ,'app', '' , 'generic_check' ], [ 'date' ,'app', '' , 'generic_check' ], [ 'ps', ,'app', '' , 'generic_check' ], [ 'cat', ,'app', '' , 'generic_check' ], [ 'id', ,'app', '' , 'generic_check' ], [ 'echo' ,'app', '' , 'generic_check' ], [ 'pwd', ,'app', '' , 'generic_check' ], [ 'chmod' ,'app', '' , 'generic_check' ], [ 'ln', ,'app', '' , 'generic_check' ], [ 'cp', ,'app', '' , 'generic_check' ], [ 'rpm', ,'app', '' , 'generic_check' ], [ 'clear' ,'app', '' , 'generic_check' ], [ 'dirname' ,'app', '' , 'generic_check' ], [ 'df' ,'app', '' , 'generic_check' ], [ 'Carp' ,'perlm', 'Perl Module - Carp', 'perl_module_check' + ], ) ;
    I'd never write that in a program. I'm too lazy. Plus, there's too many ways this could go wrong when a maintainer comes long. I'd write this as:
    @Default_Tools_List = ( (map [$_, 'app', '', 'generic_check'], qw( gzip gunzup tar tr date ps cat id echo pwd chmod ln cp rpm clear + dirname df )), [ 'Carp' ,'perlm', 'Perl Module - Carp', 'perl_module_check' + ], ) ;
    If you're "cutting and pasting" with an editor while writing a program, you're almost always going to give the maintainer a headache. Please don't.
Re: ensure - Ensure that the environment is good.
by jwkrahn (Abbot) on Sep 18, 2007 at 15:05 UTC

    You should enable the warnings and strict pragmas at the beginning of your code.

    #!/usr/bin/perl use warnings; use strict;
    7 use subs qw( 8 error_exit 9 usage 10 precheck 11 generic_check 12 perl_module_check 13 print_ok 14 print_notok 15 print_info 16 print_verbose 17 );

    You don't really need to use the subs pragma. The usual way to predeclare subs is to use the sub function:

    sub error_exit; sub usage; sub precheck; sub generic_check; sub perl_module_check; sub print_ok; sub print_notok; sub print_info; sub print_verbose;
    21 @Default_Tools_List = ( 22 [ 'gzip' ,'app', '' , 'generic_check' ], 23 [ 'gunzip' ,'app', '' , 'generic_check' ], 24 [ 'tar', ,'app', '' , 'generic_check' ], 25 [ 'tr', ,'app', '' , 'generic_check' ], 26 [ 'date' ,'app', '' , 'generic_check' ], 27 [ 'ps', ,'app', '' , 'generic_check' ], 28 [ 'cat', ,'app', '' , 'generic_check' ], 29 [ 'id', ,'app', '' , 'generic_check' ], 30 [ 'echo' ,'app', '' , 'generic_check' ], 31 [ 'pwd', ,'app', '' , 'generic_check' ], 32 [ 'chmod' ,'app', '' , 'generic_check' ], 33 [ 'ln', ,'app', '' , 'generic_check' ], 34 [ 'cp', ,'app', '' , 'generic_check' ], 35 [ 'rpm', ,'app', '' , 'generic_check' ], 36 [ 'clear' ,'app', '' , 'generic_check' ], 37 [ 'dirname' ,'app', '' , 'generic_check' ], 38 [ 'df' ,'app', '' , 'generic_check' ], 39 [ 'Carp' ,'perlm', 'Perl Module - Carp', 'perl_modul +e_check' ], 40 ) ; 41 42 43 $Tools_Hash = \@Default_Tools_List ;

    You are only using  @Default_Tools_List twice. You can assign the array reference directly to  $Tools_Hash. You should also use code references instead of strings for the subroutines. (Also  $Tools_Hash is an array not a hash.)

    my $Tools_Hash = [ [ 'gzip' ,'app', '' , \&generic_check ], [ 'gunzip' ,'app', '' , \&generic_check ], [ 'tar' ,'app', '' , \&generic_check ], [ 'tr' ,'app', '' , \&generic_check ], [ 'date' ,'app', '' , \&generic_check ], [ 'ps' ,'app', '' , \&generic_check ], [ 'cat' ,'app', '' , \&generic_check ], [ 'id' ,'app', '' , \&generic_check ], [ 'echo' ,'app', '' , \&generic_check ], [ 'pwd' ,'app', '' , \&generic_check ], [ 'chmod' ,'app', '' , \&generic_check ], [ 'ln' ,'app', '' , \&generic_check ], [ 'cp' ,'app', '' , \&generic_check ], [ 'rpm' ,'app', '' , \&generic_check ], [ 'clear' ,'app', '' , \&generic_check ], [ 'dirname' ,'app', '' , \&generic_check ], [ 'df' ,'app', '' , \&generic_check ], [ 'Carp' ,'perlm', 'Perl Module - Carp', \&perl_module_check + ], ];
    59 %precheck_stuff = ($KSH, $PERL); 220 for $ent (@precheck_stuff) { 221 if( ! -f $ent ) { 222 print_info "$ent not found\n" ; 223 $ret = 1 if($ret == 0); 224 } 225 }

    The hash  %precheck_stuff is never used after you assign to it. The array  @precheck_stuff is always empty.

    93 my @more_paths = split(/\s+/, $more_paths); 113 my @extra_tools_tochk = split(/\s+/, $extra_tools); 128 push (@extra_tools_tochk, split(/\s+/, $l)); 138 my @exclude_checks = split(/\s+/, $exclude_list); 146 my @implicit_checks = split(/\s+/, $implicit_list);

    You probably want to use  my @array = split ' ', $variable instead so that  $array[ 0 ] will always be non-empty.

    92 chomp($more_paths); 112 chomp($extra_tools); 121 chomp($tools_from_file); 137 chomp($exclude_list); 145 chomp($implicit_list);

    These variables do not need to be chomped unless the user is doing something really weird on the command line.

    115 push(@{$Tools_Hash}, [ $elem, 'app', $elem, 'generic_ch +eck']); 131 push(@{$Tools_Hash}, [ $elem, 'app', $elem, 'generic_ch +eck']); 154 push(@{$Tools_Hash}, [ $elem, 'app', $elem, 'generic_c +heck']);

    Again, the fourth element should be a code reference instead of a string.

    175 foreach $component ( @{$Tools_Hash} ) { 176 my $entity = $component->[0] ; # first thing is the enti +ty to check. 177 if($things_to_check{$entity} == 1 ) { 178 $check_function = $component->[-1] ; # last thing is the +function to check. 179 $ret = \&$check_function($entity, splice(@{$component},1, +-1)); #rest of the inbetween things are args to function. 180 } 181 }

    I would use shift for  $entity and pop for  $check_function so you don't have to use splice in the function call.

    for my $component ( @$Tools_Hash ) { my $entity = shift @$component; # first thing is the entity to ch +eck. if ( $things_to_check{ $entity } == 1 ) { my $check_function = pop @$component; # last thing is the func +tion to check. my $ret = $check_function->( $entity, @$component ); #rest of +the inbetween things are args to function. } }
    240 print_info sprintf "%-${cf_flen}s", $show_user ; 282 print_info sprintf "%-${cf_flen}s", $show_user ;

    You shouldn't use string interpolation in sprintf format strings.

    print_info sprintf '%-*s', $cf_flen, $show_user ;
      Thanks for Lots of Lots of comments. I'll implement all of them and upload the updated program soon. Thanks again.
      -- With kind Regards, S.Murali Krishnan.