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

Hi all

I am normally using the following line to capture the die output into a logfile.

 INIT {$SIG{__DIE__}=sub {LOG_MSG("normal",3,"GENERAL","Script died: $_[0]") and close LOG;}}

Now I am using also Getopt::Long. I don't want to have a logfile generated if somebody is chosing the wrong parameter. Therefore I let the script die with an usage output.

Unfortunately if somebody choses a wrong getopt parameter now - I get a log error message because of the INIT-"die" setting as the log file is not opened yet.

Example:
G:\development\bin>x.pl -x > 4,GENERAL,Script warning: Unknown option: x print() on unopened filehandle LOG at G:\development\bin\x.pl line 45. + ### Version:2.0.0 NAME xxx > 3,GENERAL,Script died: 1 at G:\development\bin\x.pl line 14. ### > 4,GENERAL,Script warning: print() on unopened filehandle LOG at G:\d +evelopment\bin\x.pl line 45. ### print() on unopened filehandle LOG at G:\development\bin\x.pl line 45. + ### 1 at G:\development\bin\x.pl line 14. ### G:\development\bin>

Every line marked with "###" at the end I do not want to have as output to STDOUT.

Do you have an ideas how can fix it? Thanks.

kind regards de Michi

Code:
use strict; use warnings; use Getopt::Long qw(:config no_ignore_case bundling); # Get options / my $VERSION = "2.0.0"; INIT {$SIG{__DIE__}=sub {LOG_MSG("normal",3,"GENERAL","Script died: $_ +[0]") and close LOG;}} INIT {$SIG{__WARN__}=sub {LOG_MSG("normal",4,"GENERAL","Script warning +: $_[0]")}} # Check Flags my $flag_help; my $flag_version; my $flag_config; GetOptions ( 'h|help' => \$flag_help, 'V|VER' => \$flag_version, 'c|config=s' => \$flag_config, ) or die USAGE(); # Check flags and print usage if ($flag_version) { print "Version: $VERSION\n"; exit; } if ($flag_help) { USAGE(); exit; } open(LOG,"> SCRIPTLOG_FILE") or die ("Can't open SCRIPTLOG_FILE: $!\n" +); close LOG; ### subs sub LOG_MSG { my $par_LEVEL = shift (@_); my $par_SEVERITY = shift (@_); my $par_FUNCTION = shift (@_); my @line = @_; print "> $par_SEVERITY,$par_FUNCTION,@line\n"; print LOG "$par_SEVERITY,$par_FUNCTION,@line\n"; } sub USAGE { my ($message)=<<'MESSAGE'; NAME xxx MESSAGE print "Version:${VERSION}\n$message"; }

Replies are listed 'Best First'.
Re: INIT {$SIG{__DIE__} and Getopt::Long
by shmem (Chancellor) on Jul 29, 2015 at 17:37 UTC

    Place your @SIG{qw(__DIE__ __WARN__)} handler initialisation after the Getopt::Long stanzas.
    If you want them to live in INIT blocks, wrap your option handling into an INIT block before the signal handler setup blocks.

    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
      Hi, placing it after does not fix it completely but it is better:
      G:\development\bin>x.pl -l > 4,GENERAL,Script warning: Unknown option: l print() on unopened filehandle LOG at G:\development\bin\x.pl line 45. Version:2.0.0 NAME xxx G:\development\bin>

      However using INIT for the option seems to do what I am looking for.

      Example:
      G:\development\bin>x.pl -b Unknown option: b Version:2.0.0 NAME xxx G:\development\bin>
      Code:
      use strict; use warnings; use Getopt::Long qw(:config no_ignore_case bundling); # Get options / my $VERSION ; my $flag_help; my $flag_version; my $flag_config; INIT { $VERSION = "2.0.0"; GetOptions ( 'h|help' => \$flag_help, 'V|VER' => \$flag_version, 'c|config=s' => \$flag_config, ) or die USAGE(); # Check flags and print usage if ($flag_version) { print "Version: $VERSION\n"; exit; } if ($flag_help) { USAGE(); exit; } } INIT {$SIG{__DIE__}=sub {LOG_MSG("normal",3,"GENERAL","Script died: $_ +[0]") and close LOG;}} INIT {$SIG{__WARN__}=sub {LOG_MSG("normal",4,"GENERAL","Script warning +: $_[0]")}} open(LOG,"> SCRIPTLOG_FILE") or die ("Can't open SCRIPTLOG_FILE: $!\n" +); close LOG; print "xx $VERSION"; ### subs sub LOG_MSG { my $par_LEVEL = shift (@_); my $par_SEVERITY = shift (@_); my $par_FUNCTION = shift (@_); my @line = @_; print "> $par_SEVERITY,$par_FUNCTION,@line\n"; print LOG "$par_SEVERITY,$par_FUNCTION,@line\n"; } sub USAGE { my ($message)=<<'MESSAGE'; NAME xxx MESSAGE print "Version:${VERSION}\n$message"; exit; }
      Thank you.

      Regard DeMichi
Re: INIT {$SIG{__DIE__} and Getopt::Long (local)
by tye (Sage) on Jul 30, 2015 at 07:26 UTC
    ... { local $SIG{__DIE__}; GetOptions( ... ) or die ...; }

    - tye        

Re: INIT {$SIG{__DIE__} and Getopt::Long
by fishmonger (Chaplain) on Jul 29, 2015 at 18:44 UTC

    An alternative to using print STDERR "error message"; as sundialsvc4 suggested, you could do warn "error message"; which does the same thing.

      An alternative to using print STDERR "error message"; as sundialsvc4 suggested, you could do warn "error message"; which does the same thing.

      Strictly speaking, no. They don't do the same.

      warn does more than just printing to STDERR. warn appends filename and line number to the text, unless the text ends with a newline. It also checks $SIG{__WARN__}. If a handler is set, the handler is called and nothing is printed by warn itself. This is documented in perlfunc.

      If you just want to print to the error output, use print STDERR. It does not modify the text, and it calls no handlers. If you need to warn that something is wrong, use warn.

      And if there is a fatal error, use die. Don't mess with warn, print STDERR and exit in that case, because only die errors can be caught by the caller using eval (or $SIG{__DIE__}).

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
      A reply falls below the community's threshold of quality. You may see it by logging in.
Re: INIT {$SIG{__DIE__} and Getopt::Long
by fishmonger (Chaplain) on Jul 29, 2015 at 18:22 UTC

    Open the log file before executing GetOptions().

      I don't want to have a log file generated if somebody is chosing the wrong parameter or just running something like x.pl -V to get the version information.

        Wouldn't it make more sense to create (or open in append mode) but not add an entry in that case?

        Personally, I'd handle the logging via the Log::Log4perl module and instead of your current usage sub, I'd write POD documentation and output that via the Pod::Usage module.

        UPDATE:
        Here's a short example using Pod::Usage instead of your custom die SIG handler.

        use strict; use warnings; use Getopt::Long qw(:config no_ignore_case bundling); use Pod::Usage; my $VERSION = "2.0.0"; # Check Flags my $help; my $man; my $version; my $config; GetOptions ( 'h|help' => \$help, 'm|man' => \$man, 'V|VER' => \$version, 'c|config=s' => \$config, ) or pod2usage(2); pod2usage(1) if $help; pod2usage(-verbose => 2) if $man; pod2usage( { -message => "Version: $VERSION\n" }) if $version; # I forgot to include the open call when I posted this update # This open call won't execute if any of the above pod2usage() stateme +nts get executed. open( my $log_fh, '>', "SCRIPTLOG_FILE") or die ("Can't open SCRIPTLOG_FILE: $!\n"); # note that I used a lexical filehandle and the 3 arg form of open __END__ =head1 NAME sample - Using GetOpt::Long and Pod::Usage =head1 SYNOPSIS sample [options] [file ...] Options: -help brief help message -man full documentation =head1 OPTIONS =over 4 =item B<-help> Print a brief help message and exits. =item B<-man> Prints the manual page and exits. =back =head1 DESCRIPTION B<This program> will read the given input file(s) and do something useful with the contents thereof. =cut
Re: INIT {$SIG{__DIE__} and Getopt::Long
by locked_user sundialsvc4 (Abbot) on Jul 29, 2015 at 18:38 UTC

    Okay, then ... instead of using die(), why not print STDERR your usage instructions?

    STDERR is a “second output-stream” that is specifically intended for error-messages, and for any other program output that is not “standard output.”

    I believe that it is customary for “usage” information to be directed to this file-handle anyway.   It normally outputs to the terminal as STDOUT does, but it can be redirected separately.   (In a Unix/Linux shell, it is redirected by means of 2> ...)

    And, in any case, if you need to output to a LOG file-handle within an exception handler, you will need to ensure that the file is open before an exception could possibly be trapped, or have the exception-handler open it on-demand.