I would like to write some functions in perl that set $! in a similar way to the built in perl functions. When I try to do this it complains that 'Argument "Cannot create admin user" isn't numeric in scalar assignment'. I have tried googling this but unfortunately google won't watch on $! so results are difficult to come by.
if(!createUser('admin')) { print $!; }
sub createUser
{
my ($user) = #_;
if($user eq "admin")
{
$! = "Cannot create admin user";
return 0;
}
#create user here
return 1;
}
PS. I know about Try::Tiny etc but I want to maintain consistancy with the rest of the script, if I switch to try/catch I want to move the entire script across which I am not ready to do.
$! is a magic variable that corresponds to the numerical error codes of your operating system (errno in the Unix-y libc libraries, something else like GetLastError() from kernel32.dll in Windows).
You can only set it to a number. When Perl wants to use it in a string context, it converts it to an appropriate text error message.
Example:
$! = 4;
print $!, "\n";
print 0+$!, "\n";
Produces (on my system, anyway):
Interrupted system call
4
It is perfectly appropriate (if non-portable, since different systems are free to use different integer values to represent standard error codes) to set $! to correspond to some standard error on your system:
sub my_do_something_with_file {
my $file = shift;
if (! -f $file) {
$! = 2; # "No such file or directory" on my system
return;
}
...
}
But there is no facility to create your own custom error messages to be returned with $!.
As documented in perlvar, $! contains a C errno, which is a number. Textual Perl-level error messages appear in $#, which is usually set by die. I would not recommend setting it manually, not least because it's a transpackage global.
Just use Try::Tiny if that's what you need. If the rest of the script is written poorly, you don't gain much from being consistent with it.
You can set $! to a numeric value that happens to match a C errno value and that sets the expected string value. Unfortunately this is not very portable.
If $! was not a magic variable you could assign a dual numeric/string value using dualvar(), which is available in Scalar::Util, which is a core package.
use Scalar::Util qw(dualvar) ;
my $x = dualvar( 44, "Hello, world" );
print "string=$x, number=" . (0+$x), "\n"
But with $! only the number survived my prior attempts.
Related
I'm using full text search with user generated input on a PostgreSQL table, "the a" in this example.
my $dbh = DBI->connect("...", { RaiseError => 0, AutoCommit => 1 });
my $sth = $dbh->prepare("SELECT name from mytable WHERE tsv ## plainto_tsquery('the a')");
my $rv = $sth->execute();
If the user input only contains stop words, I get a NOTICE on STDERR and the query returns no results, but produces no error.
I would like to capture that NOTICE in Perl to possibly alert the user to rephrase the search, but I can't seem to access it.
Setting RaiseError to 1 doesn't change anything and $dbh->pg_notifies returns undef.
Any ideas ?
I presume that the mention of "NOTICE" means that something does a RAISE. How that behaves is configurable but delving into that would require far more detail.
At perl level, there are various ways to get to the STDERR stream and capture what's sent to it.
If these are warn-ings then setting a hook for $SIG{__WARN__} runs that subroutine whenever a warning is to be printed
{
local $SIG{__WARN__} = sub { say "Got warning: $_[0]"; };
warn "a warning";
}
So you can capture it this way, do what you wish with it, and then perhaps reemit. The $_[0] in the example has the string a warning, and after the anonymous sub runs the control is back at the next line after a warning is issued (the warn statement in this snippet). See %SIG.
I put this in a block only to be able to local-ize the change to SIG{__WARN__}, what is practically compulsory (if this global isn't local-ized its change affects all code). So if this code is in a suitable lexical scope anyway then that block isn't needed.
But this won't catch things printed directly to STDERR. For that the most practical way is to use libraries, and a simple and convenient one for this purpose is Capture::Tiny
use Capture::Tiny qw(capture);
my ($stdout, $stderr, $exit) = capture {
say "to STDOUT";
say STDERR "to STDERR";
warn "a warn-ing";
# ...
};
Now $stdout has text to STDOUT while $stderr has to STDERR followed by a warn-ing. So your database code would go in such a block and the NOTICE should wind up in that $stderr variable. There is also a capture_stderr function if you prefer to capture only that this way.
That doesn't seem to work as it should, and it is recognized as an unsolved bug.
This question may look simple, but I am thinking on this over past some days, I couldn't find the answer.
I have multilevel scripting architecture (the code is shown below)
CallingScript.pl (Include toplevel library and check for compiler-error)
do "IncludesConsumer.pm";
print "\n callingScript error : $#" if($# || $!);
do "IncludesConsumer.pm";
print "\n callingScript error : $#" if($#);
do "IncludesConsumer.pm";
print "\n callingScript error : $#" if($#);
IncludesConsumer.pm (adds the library INCLUDES.pm and has its own functions)
do "INCLUDES.pm";
print "\nin IncludesConsumer";
INCLUDES.pm ( multiple modules in one place, acts as a library )
use Module;
print "\n in includes";
Module.pm (with syntax-error)
use strict;
sub MakeSyntaxError
{
print "\nerror in Module
}
1;
In concept, once of the core Modules(e.g. Module.pm) may contain syntax errors. So I need to capture them in CallingScript.pl. i.e: I would like to capture the syntax error in Module.pm (low-level) in the CallingScript.pl file.
OUTPUT:
D:\Do_analysis>CallingScript.pl
in IncludesConsumer
in IncludesConsumer
in IncludesConsumer
Why is the compiler-erro not caught in CallingScript.pl?
Please pour-in your thoughts.
Thanks!
Five errors, starting with the one causing the problem you are asking about:
You didn't handle errors from some of the do.
You only check $! some of the time.
$! can be true even if there was error. Only check $! is both do and $# are false.
All but one of the your included file didn't signal a lack of error (1;). You need to return true so do returns true, so you know whether an error occurred or not.
You used a file that doesn't have a package.
CallingScript.pl:
do "IncludesConsumer.pm" or die "CallingScript: ".($# || $!);
do "IncludesConsumer.pm" or die "CallingScript: ".($# || $!);
do "IncludesConsumer.pm" or die "CallingScript: ".($# || $!);
IncludesConsumer.pm (which isn't actually a "pm"):
do "INCLUDES.pm" or die "IncludesConsumer: ".($# || $!);
print "\nin IncludesConsumer";
1;
INCLUDES.pm (which isn't actually a "pm"):
use Module;
print "\n in includes";
1;
Module.pm (with syntax-error)
package Module;
sub MakeSyntaxError {
1;
There's really no reason to use do in this fashion. It's a very poor programming practice. please avoid it in favor of modules.
Well, you have the following hierarchy:
CallingScript
do IncludesConsumer
do INCLUDES
use Module
The use Module is processed at compile time of INCLUDES.pm, which then also fails. After do "INCLUDES.pm", the $# variable is set.
However, the $# refers to the last eval (of which do FILE is a variant). In CallingScript.pl, this is the do "IncludesConsumer.pm", which ran fine.
The do FILE syntax is unneccessary since the advent of modules to Perl. You want to use your files in nearly all cases instead, or require them if runtime effects are needed.
If you want to assert that modules can be loaded fine, I refer you to Test::More with the use_ok function.
Is this a good way of returning an error messages from a subroutine in perl?
sub some_subroutine{
# do something
$something = 14;
if(1 == 2){
$_ = "This should not be happening!";
return undef;
}
return $something;
}
my $ret=some_subroutine();
print "ERROR: $_" unless(defined $ret);
The code runs OK (in a parallel world, where 1 == 2), but using $_ to return the error message is a good way? I didn't found any documentation regarding the usage of $_ for this kind of purposes.
Thank you!
$_ is not a good mechanism as so many other things use and set it.
You could set some other global variable, like $Error, but the best way is to throw an exception. Then the user doesn't have to always check, and forget, it just happens. In Perl, exceptions are generated with "die". die will take a string or an object. There's a number of modules to make throwing exceptions easier including Exception::Class, Exception::Simple, Ouch and autodie.
You can catch the exception with eval {}, but there are a number of problems with that so you should use something like Try::Tiny.
It's not unknown to use a global variable to hold the error message. For example, built-in function use $!, and DBI uses $DBI::errstr. But then you inherit all the problems of using global variables. Signal handlers need to localise them, destructors can clobber them, multi-threading issues, etc.
It seems to me that throwing an exception (e.g. using die) is a more common choice.
Whatever you do, don't use $_. It is often aliased to something, so using it could have unintended consequences.
One thing you can do to make this slightly better is to simply return; on error/undefined result. There are contexts where return undef; can be evaluated as true. You might check out Perl Best Practices's chapter on error handling as it covers this and has other good pointers.
For example, if:
my $ret=some_subroutine();
print "ERROR: $_" unless(defined $ret);
becomes
my #ret=some_subroutine(); # oops!
print "ERROR: $_" unless(defined $ret);
You have a weird bug (not catching the error) that may be hard to track down, whereas if you return; from some_subroutine you get:
my #ret=some_subroutine(); # oops!
print "ERROR: $_" unless($ret); # but error is still caught
Furthermore, in the case of a function that should return a list:
my #ret=some_other_subroutine(); # OK
print "ERROR: $_" unless($ret); # error will be caught if you "return;"
# ... but later ...
my $ret=some_other_subroutine(); # oops!
print "ERROR: $_" unless($ret); # if you "return;" $ret will equal 0
# (the length of the returned list)
# and the error will be caught
So, while there are other patterns for returning error/undefined status from a function, always using return; allows the use of one pattern for nearly all functions, both in returning the appropriate value and when it comes time to check for error status.
It totally depends on what behavior you are expecting to see from your error handler. It is very common in perl to use die or warn in case a runtime error/warning happens.
Take a look at this tutorial on error handling in perl: http://www.tutorialspoint.com/perl/perl_error_handeling.htm
For a web service, you could write a subroutine for printing custom error responses (40x errors, etc.).
I've just been caught out when using someone else's API in conjunction with the default variable $_
foreach (#rps_server_details) {
#server_data = ();
#server_data = split(/,/);
#$esp_hosts = ();
$filters{server_name} = $server_data[0];
print "--->$_<--\n";
$esp_hosts = $esp->get_hosts(fields => $fields, %filters) || die "$#";
print "--->$_<--\n";
The output for this is:
--->igrid8873.someone.com,app_10<--
Use of uninitialized value in concatenation (.) or string at ./rps_inv_lookup.pl line 120.
---><--
Specifying my own loop variable instead of relying on $_ fixes the problem.
Am I just being naive by using $_ in conjunction with an API someone else has written? Or is this a bug in that API module?
It is a bug in the API. If you use $_ in a function it is important to add a
local($_);
inside the function to avoid clobbering the caller's $_, or otherwise avoid using $_in a library function to be called by others.
If you can limit yoursel to Perl versions > 5.9.1 then you can also make $_ lexical which makes it easier to understand than localwith
my $_;
But this will break on earlier versions of Perl.
From man perlvar:
As $_ is a global variable, this may lead in some cases to
unwanted side-effects. As of perl 5.9.1, you can now use a
lexical version of $_ by declaring it in a file or in a block
with "my". Moreover, declaring "our $_" restores the global $_
in the current scope.
I would say it's:
a violation of best practices on your part (always use as-local-as possible variable scope and avoid using $_ due to just the issue your encountered)
coupled with a bug in the API caused by the same violation of the best practices as well as not localizing the special variable with local $_ as proscribed by perldoc perlvar.
In addition to perldoc, the API violates Perl Best Practices (as in Conway's book's rules):
Section 5.6. Localizing Punctuation Variables
If you're forced to modify a punctuation variable, localize it.
The problems described earlier under "Localization can also crop up whenever you're forced to change the value in a punctuation variable (often in I/O operations). All punctuation variables are global in scope. They provide explicit control over what would be completely implicit behaviours in most other languages: output buffering, input line numbering, input and output line endings, array indexing, et cetera.
It's usually a grave error to change a punctuation variable without first localizing it. Unlocalized assignments can potentially change the behaviour of code in entirely unrelated parts of your system, even in modules you did not write yourself but are merely using.
Using local is the cleanest and most robust way to temporarily change the value of a global variable. It should always be applied in the smallest possible scope, so as to minimize the effects of any "ambient behaviour" the variable might control:
Here's full perldoc perlvar documentation as well - search for the word "nasty_break" in the web page (I couldn't find direct in-page link but it's close to the start of the page)
You should be very careful when
modifying the default values of most
special variables described in this
document. In most cases you want to
localize these variables before
changing them, since if you don't, the
change may affect other modules which
rely on the default values of the
special variables that you have
changed. This is one of the correct
ways to read the whole file at once:
open my $fh, "<", "foo" or die $!;
local $/; # enable localized slurp mode
my $content = ;
close $fh;
But the following code is quite bad:
open my $fh, "<", "foo" or die $!;
undef $/; # enable slurp mode
my $content = ;
close $fh;
since some other module, may want to
read data from some file in the
default "line mode", so if the code we
have just presented has been executed,
the global value of $/ is now changed
for any other code running inside the
same Perl interpreter.
Usually when a variable is localized
you want to make sure that this change
affects the shortest scope possible.
So unless you are already inside some
short {} block, you should create one
yourself. For example:
my $content = '';
open my $fh, "<", "foo" or die $!;
{
local $/;
$content = ;
}
close $fh;
Here is an example of how your own
code can go broken:
for (1..5){
nasty_break();
print "$_ ";
}
sub nasty_break {
$_ = 5;
# do something with $_
}
You probably expect this code to
print:
1 2 3 4 5
but instead you get:
5 5 5 5 5
Why? Because nasty_break() modifies $_
without localizing it first. The fix
is to add local():
local $_ = 5;
foreach (#rps_server_details) {
#server_data = ();
#server_data = split(/,/);
#$esp_hosts = ();
$filters{server_name} = $server_data[0];
print "--->$_<--\n";
{
local *_; # disconnects the remaining scope from the implicit
# variables so you can clean up after the dirty api.
# NOTE: Submit a bug report against the offending module.
# If you notice this across multiple api features
# consider finding a different module for this task.
$esp_hosts = $esp->get_hosts(fields => $fields, %filters) || die "$#";
}
print "--->$_<--\n";
Is it OK to assign to $! on an error in Perl?
E.g.,
if( ! (-e $inputfile))
{
$! = "Input file $inputfile appears to be non-existent\n";
return undef;
}
This way I can handle all errors at the top-level.
Thanks.
If you assign to $!, it is placed in the system errno variable, which only takes numbers. So you can in fact do:
use Errno "EEXIST";
$! = EEXIST;
print $!;
and get the string value for a defined system error number, but you can't do what you want - setting it to an arbitrary string. Such a string will get you a Argument "..." isn't numeric in scalar assignment warning and leave errno set to 0.
The other problem is that $! may be changed by any system call. So you can only trust it to have the value you set until you do a print or just about anything else. You probably want your very own error variable.
Well, the documentation says it's an indicator for system errors, basically. So I wouldn't assign to it, you just make your lib's users mad.
Use exceptions instead:
eval { # this ain't the evil eval
# some code
die $something;
}
if (my $err = $#) {
# exception handling
}
Note that you can "throw", or die, with about anything you need..
My Rabbi said "no!"
Setting $! is fine, but:
you should do it at the end of your function
you should return a different value that indicates that an error occurred
you should use your OS's errno values rather than strings for setting purposes
the checking code needs to check the value should do so immediately on failure of the function (and only if a failure is indicated by the function)
Something to keep in mind is that die uses the value in $! for its exit code (so long as it is not zero).
Yes you can assign stuff(#'s) to $!, just be wary of where you do it so you don't mess up some other functions message.
If you only have one variable to store errors, you'll have problems if you have more than one error occurring in your program before checking the status of your error variable. That's worth avoiding if you can help it.
Thankfully in Perl you can help it. A really nice solution is to use object-oriented exception handling from Error.pm. This module will allow you to write try/catch blocks, like this:
try {
some code;
code that might thrown an exception;
more code;
return;
}
catch Error with {
my $ex = shift; # Get hold of the exception object
# handle the exception;
};
The CPAN documentation for the module is quite good, and there is a Perl.com article on the subject too.
$! has so many caveats, being a global variable which lots of functions assign to (some of them C functions which Perl calls), that I would simply throw an exception (which in Perl means dying) and let the user trap it if they care. So instead of writing:
$obj->foo or die $!;
$obj->bar or die $!;
$obj->baz or die $!;
or even
$obj->foo or die $obj->error;
$obj->bar or die $obj->error;
$obj->baz or die $obj->error;
you can just write
$obj->foo;
$obj->bar;
$obj->baz;
and know that if there is an error you'll be informed of it. Also anyone above you will be informed and can trap it. Since that's the most common case make it happen without the user needing to remember it and type it over and over and over again.
Should you want to ignore or recover from the error, simply use eval BLOCK.
eval { $obj->foo }; # don't care if it works or not
eval { $obj->bar } or do { ...something when it doesn't work... };
Since this is the exceptional case it should be where the user has to remember to add more code and does more work.
Examples of this approach include DBI's RaiseError flag, which is only off by default for backwards compatibility, and the wonderful autodie module.