Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

Using system(): converting error num to human readable message

by chrism01 (Friar)
on Apr 18, 2007 at 01:39 UTC ( [id://610677]=perlquestion: print w/replies, xml ) Need Help??

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

Monks,
When I run system(), I want to get the CORRECT error msg
if an error has occurred.
However, I have discovered that sometimes Perl gives me
the same msg that the shell generates (which I want), and
sometimes it doesn't!
See the following

#!/usr/bin/perl -w use strict; # Enforce declarations my ( $cmd, $status ); print "Test1\n\n"; $cmd = "somprog var"; $status = system($cmd); if( $status != 0 ) { $! = $? >> 8; print "fail $!\n"; } print "\nTest2\n\n"; $cmd = "someprog \"var\""; $status = system($cmd); if( $status != 0 ) { $! = $? >> 8; print "fail $!\n"; } print "\nTest3\n\n"; $cmd = "ls t.t"; $status = system($cmd); if( $status != 0 ) { $! = $? >> 8; print "fail $!\n"; } exit;
Results:
Test1 Can't exec "somprog": No such file or directory at ./err_cvt.pl line 1 +0. fail Unknown error Test2 sh: someprog: not found fail Not owner Test3 t.t: No such file or directory fail No such file or directory
As you can see, in Test1 & Test2, the Perl msg is wrong, but correct in Test3.

1. Why?
2. how can I get all uses of system() to give me the the right msg ie same as shell produces?

Cheers
Chris

Replies are listed 'Best First'.
Re: Using system(): converting error num to human readable message
by naikonta (Curate) on Apr 18, 2007 at 03:48 UTC
    First of all, $? and $! are two different things and they're not correlated. The $? comes from executing external program, and this external program may exit with arbitrary number (except 0, which is for success or true) when error occures. The $! comes from running a system call such as but limited to open, sysread, and socket. The $! can be used in either numeric or string context. All $! values and their counterpart strings are defined by the C library of underlying system, so you can't just set $! at will, unless you know what you're doing.

    To find out what $? of a given external command really means you have to read its respective manual like one what ls says:

    Exit status is 0 if OK, 1 if minor problems, 2 if serious trouble.
    And we use the shift operator on $? to find whether it's 1 or 2 in this case. If that's 1 for example, we know that there's a minor problem, but that's all you can get. While external programs spits out their arbitrary error code that gets set in $?, they might throw the equally arbitrary error message (mostly) to the standar error (STDERR). So you see that error code ($?) and error message are not relevant. What make thing worse is that every program in the system has their own idea about what error code to use and how many of them. And not all of them are documented. Shortly, this isn't something you can rely on. The $! is, in contrast, well defined by the C Errno library so it's portable across systems using the same C libs.

    So that hopefully answers to your number "1. Why?". As for the number 2, I gave a hint in the explanation above about error message is thrown to STDERR. So, in order to get the error message you have to capture it from STDERR. One (in)famous way and the short answer is with the bactick operation. But the perldoc -q capture will give you the long and more descriptive answer. Or, you can go here. For example,

    my @cmd = ('someprog', 'somearg', 'anotherarg'); my $msg = qx/@cmd 2>&1/;
    The 2>&1 bit tells the system to include whatever thown to STDERR to STDOUT. But again, you first check whether is error or not by checking $?. If it's non-zero, then error occures and $msg contains the error message thrown by someprog. The backtick operation is there for capturing output, in contrast with system. But both are for executing external/shell programs. Let me make clear that 2>&1 is not part of Perl syntax whatsoever, it's a shell construct.

    I want to also suggest that instead of writing if( $status != 0 ) {}, you can write,

    if ($?) { # error occures # do whatever }

    A side note: Years ago in my early Perl programming days, I used to play a lot with external program using system and backtick operation. My favourite program was useradd. It documents a number of error codes. So you may guess what I was doing with useradd and friends in Perl ;-)

    See also: perlvar, system, perlop, STDERR


    Update: Added note about 2>&1 being a shell construct.


    Open source softwares? Share and enjoy. Make profit from them if you can. Yet, share and enjoy!

      Actually, i do know the diff between shell and Perl, I've been programming for a few yrs now.. ;-)
      From what you two are saying, it sounds like it's impossible to do what I want anyway.
      I tried your use of qx, but it failed thus:
      @cmd_arr = ("someprog", "var"); $msg = qx/@cmd_arr 2>&1/; if( $? ) { print "fail $?\n"; print "$msg\n"; } Result: fail -1 Use of uninitialized value in concatenation (.) or string at ./err_cvt +.pl line 17.
      I read about the $? >> 8 in the Cookbook and I read the info about $! in a string context in perlvar http://perldoc.perl.org/perlvar.html#%24ERRNO, which is why I was trying to use those techniques.
      I like to check rtn status' as much as possible and I've always hated the fact I can't get reliable sensible msgs from system(), so I thought I'd ask if there was a known solution.
      Apparently not ... :-(

      Cheers
      Chris

      PS any idea on Qn 3 ?

        I didn't mean to say that everytime you do $msg = qx/prog 2>&1/ you always get error message in $msg because the error message totally depends on the program in question. In fact, your example shows that your execution on "someprog" doesn't say anything at all so $msg remains undefined, hence the warning message.

        Take a real progam as example,

        $ perl -wle '$msg = qx/ls -K 2>&1/; print "error: $?"; print $msg' error: 512 ls: invalid option -- K Try `ls --help' for more information.

        If what you meant is that you want to capture the error on the fact that "someprog" command doesn't exist on the system, then you need to capture the error of the shell, not "someprog". Well, depending on your shell,

        $ perl -wle '$msg = qx/bash -c ProgramNotExist 2>&1/; print "error: $? +"; print $msg' error: 32512 bash: ProgramNotExist: command not found

        Update: Rephrased the "In fact, ...." sentence.


        Open source softwares? Share and enjoy. Make profit from them if you can. Yet, share and enjoy!

Re: Using system(): converting error num to human readable message
by BrowserUk (Patriarch) on Apr 18, 2007 at 03:18 UTC

    The real problem here is that the messages the shell gives are internal to the shell, and the code the shell chooses to return caller are chosen by the shell.

    If the code returned translates directly into a 'system message', and the shell chooses to also report that 'system message', as in the case of "No such file or directory", then perl can translate the code into that same system message.

    But, if the shell chooses, given it's intimate knowledge of what you asked it to do, and what error it encountered trying to do it, to report that at a level of detail, or in a manner that for which there is no convenient system message/code, then Perl cannot hope to translate the code returned into the same message the shell produces.

    That's a long-winded way of saying, what you are asking for it probably not possible. Consider the case where you passed the same eroneous command to two different shells. Each will produce it's own message, with it's own level of detail. Each will choose what return code to pass back to Perl--they maybe the same or different. Unless they are consistant, and relate to some system message, Perl cannot translate it as you would wish.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Using system(): converting error num to human readable message
by dana (Monk) on Apr 18, 2007 at 01:55 UTC

    Looking at your code I think the error messages are valid.

    In Test3, you try to run a valid command (ls) on a non-existant file (t.t).

    However you're running 'someprog "var"' and 'somprog var' in Test2 and Test1 respectively. Unless you have two programs, someprog and somprog, you are going to get differing error messages relating to either non-existing files or an error in the someprog program.

    If you have 'somprog' and 'someprog' it would be helpful to look at those scripts to see how they should be called. Do you have someprog and a typo in the program name 'somprog' or are these in fact two valid program calls?

Re: Using system(): converting error num to human readable message
by chrism01 (Friar) on Apr 18, 2007 at 03:14 UTC
    Actually, re Test1, 2, that was a typo as you suspected, but neither of those progs exist (deliberately), as indeed neither does the t.t file.
    I am trying to get Perl to give me the same err msg that the shell does, which it does for Test3, but not Test1 or 2.
    Bonus Qn3: Why are the msgs for 1 & 2 different?

    Cheers
    Chris

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://610677]
Approved by Sidhekin
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (5)
As of 2024-04-25 08:45 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found