malokam has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks. Perl noob here and already learned a lot from these forums. Thanks for the responses in my previous post. Below are my Perl/System details

pgp->perl -v This is perl, v5.10.1 (*) built for aix-thread-multi pgp->perl -V Summary of my perl5 (revision 5 version 10 subversion 1) configuration +: Platform: osname=aix, osvers=6.1.2.0, archname=aix-thread-multi uname='aix powerpc unknown aix ' config_args='-desr -Dinstallprefix=/usr/opt/perl5 -Dprefix=/usr/op +t/perl5 -Dcc=xlc_r -Duseshrplib -Dusethreads' hint=recommended, useposix=true, d_sigaction=define useithreads=define, usemultiplicity=define useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=und +ef use64bitint=undef, use64bitall=undef, uselongdouble=undef

I am trying to automate some PGP encryption/decryption and I have a situation. I am saving the output of certain PGP commands with something like this. Now in case of errors, the following set of strings would show up in the output.

3001:input file not found 1080:no private key could be found for decryption 1053:signing key not found 3013:no keys found 3011:invalid passphrase specified 3011:invalid passphrase specified 3090:operation failed, file not found 3028:multiple inputs cannot be sent to a single output file

When I try to compare the outputs with the strings, I am having issues. If I use a single string like "successfully" I am able to use the ~~ operator in Perl V5.10.1 How do I leverage ~~ operator with multiple strings?

$program_output3 = `pgp --<various arguments>`; if($program_output3 ~~ /"file wiped successfully"/i) { print "File encrypted successfully\n"; } elsif($program_output3 ~~ /"operation failed, file not found"/i) { print "signing key is not found?"; } elsif($program_output3 ~~ /"no keys found"/i) { print "Please check if the customer's signing key exists on our ri +ng"; }

I even tried creating an array with the error codes and loop through that array and compare with program output with ~~. Still no luck. Problem with trying to use something else like "List::MoreUtils qw" is that I cannot install any new modules on this server. (Due to various reasons that I cannot explain)

Replies are listed 'Best First'.
Re: Perl 5.10.1 - compare external program output with a list of known error codes
by choroba (Cardinal) on Sep 19, 2016 at 15:51 UTC
    You don't need the ~~ operator for simple matching, =~ is enough. Moreover, ~~ is experimental and will change or be removed in the future, so it's better to stay away from it.

    Also, when matching against a regex, don't include double quotes in it, as they are interpreted literally, but your strings don't contain them. The slashes are already the delimiters, no need for anything else.

    You can use the | alternative operator inside a regex:

    #!/usr/bin/perl -- use warnings; use strict; use feature qw{ say }; while (<DATA>) { if (/no private key could be found for decryption|signing key not +found|no keys found/) { say 'Missing key(s)'; } elsif (/file not found/) { say 'File not found' } else { say 'Unknown error'; } } __DATA__ 3001:input file not found 1080:no private key could be found for decryption 1053:signing key not found 3013:no keys found 3011:invalid passphrase specified 3011:invalid passphrase specified 3090:operation failed, file not found 3028:multiple inputs cannot be sent to a single output file

    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,

      What is this part doing?

      ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,

        That's not doing anything in relation to the code he's specified for you. Anything below the solid black line is choroba's Perlmonks signature, like the signature in an email.

        Copy/paste it into a script.pl file, and run perl script.pl on it at the command-line, and you'll see what it does ;)

Re: Perl 5.10.1 - compare external program output with a list of known error codes
by stevieb (Canon) on Sep 19, 2016 at 16:06 UTC

    This may be going a bit too far, but you could use a dispatch table to do the work for you. It's reusable, and eliminates a whole slew of if/else/elsif statements...

    use strict; use warnings; my %errors = ( 'input file not found' => sub { print "Bloody input file is missing!\n"; }, 'signing key not found' => sub { print "meh, we're screwed!\n"; }, 'no keys found' => sub { print "This doesn't need encrypting anyway...\n"; } ); my $program_output3 = '3001:input file not found'; chomp $program_output3; # chomp any newline $program_output3 =~ s/^\d+://; # remove the digits/colon $errors{lc $program_output3}->(); # lowercase, just in case

    Output:

    Bloody input file is missing!

      This is the code I have written

      #!/pgp/perl5/bin/perl ##Sample script to grep perl program output #use strict; #use List::MoreUtils qw(any); use warnings; use strict; use feature qw{ say }; #Added env variable for exporting pgp LIB_PATH $ENV{LIBPATH} = "/pgp/lib"; my %errors = ( 'input file not found' => sub { print "Bloody input file is missing...\n"; }, 'signing key not found' => sub { print "meh, we're screwed..\n"; }, 'no keys found' => sub { print "This doesn't need encrypting anyway...\n"; }, 'file wiped successfully' => sub { print "Woo Hoo..\n"; }, ); my $program_output3 = `pgp --args --verbose`; chomp $program_output3; # chomp any newline $program_output3 =~ s/^\d+://; # remove the digits/colon $errors{lc $program_output3}->(); # lowercase, just in case

      I am getting this at the end of the perl output

      Can't use an undefined value as a subroutine reference at ./error_chec +ker.pl line 34.

      The line 34 is this. I actually did not understand this as well

      $errors{$program_output3}; # lowercase, just in case

        You missed the ->() piece, which turns it into a function call:

        $errors{$program_output3}->();

        it means, dereference $errors{$program_output3} as a CREF (code reference), and execute it.

Re: Perl 5.10.1 - compare external program output with a list of known error codes
by Marshall (Canon) on Sep 20, 2016 at 00:17 UTC
    You are capturing the output of a backtick operator. One thing is missing from this, the actual status of that command.

    This might be helpful:
    PerlVar and new ${^CHILD_ERROR_NATIVE} variable in Perl 5.10

    ${^CHILD_ERROR_NATIVE}
    The native status returned by the last pipe close, backtick (`` ) command, successful call to wait() or waitpid(), or from the system() operator. On POSIX-like systems this value can be decoded with the WIFEXITED, WEXITSTATUS, WIFSIGNALED, WTERMSIG, WIFSTOPPED, WSTOPSIG and WIFCONTINUED functions provided by the POSIX module.

    Under VMS this reflects the actual VMS exit status; i.e. it is the same as $? when the pragma use vmsish 'status' is in effect. This variable was added in Perl v5.10.0.

    I am thinking that this would give you the summary of "it worked or it didn't work?". Maybe that is helpful? Parsing the output of the command seems to be a difficult way to do that?

    If you want the last line of the backtick output, you can assign that to an array and then just look at the last line. On Windows:

    my @out = `help copy`; print $out[-1]; #the last line of output
Re: Perl 5.10.1 - compare external program output with a list of known error codes
by malokam (Novice) on Sep 19, 2016 at 18:06 UTC

    The problem is that the external program output from pgp operations usually contain multiple lines like below

    pgp:encrypt (3157:current local time aa-bb-cc) /xxxx:open keyrings (1006:public keyring) /xxxx:open keyrings (1007:private keyring) yyyyy:encrypt (1030:key added to recipient list) yyyyy:encrypt (1050:key added as signer) sample.txt:encrypt (3090:operation failed, file not found)

    Here, the last line is what matters to me and this is what needs to be compared to the error strings I mentioned in my original post. I am still unable to successfully catch the appropriate errors.

      Since you're using backticks, you're only capturing the STDOUT output of the PGP commands, but the error messages that you're wanting to check possible could be sent to STDERR instead. You could redirect STDERR to STDOUT by adding 2>&1 to the end of the PGP commands. Another alternative is to use Capture::Tiny, which will capture STDOUT, STDERR and exit code for external commands.

      You did not specify that you were getting a whole screenful of output. My understanding was that you were receiving a single line like you had in your original post.

      For my example code to work, you have to extract *just* that piece and use it.

Re: Perl 5.10.1 - compare external program output with a list of known error codes
by malokam (Novice) on Sep 22, 2016 at 21:19 UTC

    I was able to fix this problem!! Woo Hoo! Thanks everyone for the responses and inputs

    Using "qx" instead of backticks and adding "2>&1" at the end of the PGP command did the trick. Below is the modified script

    #!/pgp/perl5/bin/perl ##Sample script to grep perl program output use warnings; use strict; use feature qw{ say }; #Added env variable for exporting pgp LIB_PATH $ENV{LIBPATH} = "/xyz/lib"; my $program_output3 = qx(pgp --options --verbose 2>&1); if($program_output3 !~ /0:file wiped successfully/i) { print "Something went wrong\n"; print "$program_output3\n"; if($program_output3 =~ /3013:no keys found/i) { print "Customer key is not found. Cannot encrypt\n"; } elsif($program_output3 =~ /3090:operation failed, file not found/i +) { print "File is not found. Check the path again\n"; } elsif($program_output3 =~ /1080:no private key could be found for +decryption/i) { print "Private key not found on our keyring.\n"; } elsif($program_output3 =~ /3001:input file not found/i) { print "File is not found?\n"; } elsif($program_output3 =~ /1053:signing key not found/i) { print "Signing key to encrypt is not found in the keyring\n"; } elsif($program_output3 =~ /3011:invalid passphrase specified/i) { print "Check the passphrase used. \n"; } elsif($program_output3 =~ /3083:could not create output file/i) { print "Check if the output location is valid or has access per +missions\n"; } elsif($program_output3 =~ /3028:multiple inputs cannot be sent to +a single output file/i) { print "This is a weird one. Check if there are any wildcards i +n the filename or command\n"; } } else { print "$program_output3\n"; print "Command ran successfully\n"; }

    Below are sample outputs from the commands

    ########### RUN 1 ################### /pgp/scripts->./error_checker.pl pgp:encrypt (3157:current local time 2016-09-22T17:15:21-04:00) /pgp/.pgp/pubring.pkr:open keyrings (1006:public keyring) /pgp/.pgp/secring.skr:open keyrings (1007:private keyring) 0x3279584E:encrypt (1030:key added to recipient list) 0x4B3DBEBE:encrypt (1050:key added as signer) /pgp/scripts/sample.txt:encrypt (3048:data encrypted with cipher AES-2 +56) /pgp/scripts/sample.txt:encrypt (0:output file /pgp/scripts/sample.txt +.pgp) /pgp/scripts/sample.txt:encrypt (0:file wiped successfully) Command ran successfully ########### RUN 2 ################### /pgp/scripts->./error_checker.pl Something went wrong pgp:encrypt (3157:current local time 2016-09-22T17:15:32-04:00) /pgp/.pgp/pubring.pkr:open keyrings (1006:public keyring) /pgp/.pgp/secring.skr:open keyrings (1007:private keyring) 0x3279584E:encrypt (1030:key added to recipient list) 0x4B3DBEBE:encrypt (1050:key added as signer) /pgp/scripts/sample.txt:encrypt (3090:operation failed, file not found +) File is not found. Check the path again ########### RUN 3 ################### /pgp/scripts->./error_checker.pl Something went wrong pgp:encrypt (3157:current local time 2016-09-22T17:16:05-04:00) /pgp/.pgp/pubring.pkr:open keyrings (1006:public keyring) /pgp/.pgp/secring.skr:open keyrings (1007:private keyring) x3279584E:encrypt (3013:no keys found) Customer key is not found. Cannot encrypt