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

Hello wise monks,

Here a little code snippet which opens the command "dir" for reading and then prints the output of "dir".

#!/usr/bin/perl use strict; use warnings; open(my $CMD, "dir |") || die $!; while( <$CMD> ) { print "$_"; } close($CMD);

Now I tried the same, but with a not-existing command "dira".

#!/usr/bin/perl use strict; use warnings; open(my $CMD, "dira |") || die $!; while( <$CMD> ) { print "$_"; } close($CMD);

Of course my goal would it be that the open command dies the program because the command "dira" does not exist.

But I get here on my german computer the following output:

Der Befehl "dira" ist entweder falsch geschrieben oder konnte nicht gefunden werden.

So my question is, how can I check when I open a process, if the command really exists?

Of course I could check if the output is "Der Befehl ...". But this is then dependent of the language, the OS, ... . In other words this would be a very specific solution.

Another possibility would be to loop through all pathes in the environment variable PATH and then checking if the command exists.

Unfortunately I do not have more ideas. That's the reason why I ask you.

Thank you for your help.

Greetings,

Dirk

Replies are listed 'Best First'.
Re: Opening not-existing command - Error Handling
by Perlbotics (Archbishop) on Aug 16, 2011 at 08:44 UTC

    So my question is, how can I check when I open a process, if the command really exists?
    Just try to run the command and see if that fails. Don't fail fatally (die()) if you do not want to.

    use strict; use warnings; my $run_cmd = "dira"; if ( open(my $CMD, "$run_cmd |") ) { while( <$CMD> ) { print "$_"; } close($CMD); } else { die "Problem executing command $run_cmd - $!"; # ... or something non-fatal }
    Alternatively, you could wrap your stuff into an eval { ... } block and then examine $@ (e.g. Problem executing command in the example above). You could also try one of the many CPAN modules that supports handling of exceptions.

    Update:

    Another possibility would be to loop through all pathes in the environment variable PATH and then checking if the command exists.
    There is this ancient thread and some CPAN modules like Dir::Which or File::Which that can be used to perform apriori checks - but see cdarke's comments below.

    Update2:
    The program above was tested and worked fine under Linux. Since I am not a Windows-expert, I can only do wild guesses on what the problem could be:

    • cmd is started successfully but then fails to run dira?
    • open() might return something that Perl thinks is true (e.g. -1)?
    • dira exists somewhere, but prints an error...?
    • copy/paste error?
    • ...

      Thanks. Problem is that even if I take the not-existing command "dira" I'm not going into the else block. I verified it with the debugger.

      Do you enter the else block on your machine if you choose a not-existing command?

        On GNU/Linux (line 12 is test in "if"; second message was generated in "else" body) & perl 5.8.8 ...

        Can't exec "dira": No such file or directory at p.pl line 12. Problem executing command dira - No such file or directory at p.pl lin +e 18.
        ...
Re: Opening not-existing command - Error Handling
by cdarke (Prior) on Aug 16, 2011 at 08:57 UTC
    It depends what you mean by 'command'. If you mean a shell built-in, then you will have to invoke the shell to find it.

    If you mean an external program, then there are a number of ways of finding if it exists. I assume you are on Windows, since you are using the dir command. On Windows, searching the %PATH% environment variable is not enough, Windows searches in several places before it looks at %PATH%. See Win32::SearchPath.

    You might find it simpler just to try and call it then handle the exception if it occurs.
Re: Opening not-existing command - Error Handling
by choroba (Cardinal) on Aug 16, 2011 at 08:38 UTC
    Of course my goal would it be that the open command dies the program because the command "dira" does not exist.

    Your program did exactly that. You told it to die with $!, which is the German text you see. You can make it die with another text if you do not like the German one.

      I tried the following:

      #!/usr/bin/perl use strict; use warnings; open(my $CMD, "dira |") || die "another text"; # ... close($CMD);

      But the program still produces the same error message:

      Der Befehl "dira" ist entweder falsch geschrieben oder konnte nicht gefunden werden.

        Not for me:

        #!/usr/bin/perl use strict; use warnings; open(my $CMD, "dira |") || die "something else\n"; while( <$CMD> ) { print "$_"; } close($CMD);

        Output:

        Can't exec "dira": No such file or directory at ./test.pl line 6. something else
        This form of open starts a child process. The return result is the child process ID. Thus, your open(...) || die ... will show the error if the child can't be created. You need to check $? to check for errors from the child. See this for an example of checking for errors in this situation.
Re: Opening not-existing command - Error Handling
by Khen1950fx (Canon) on Aug 16, 2011 at 11:19 UTC
    Using SVN::Notify, here's a very simple precheck and snippet:
    #!/usr/bin/perl use strict; use warnings; use SVN::Notify; my $command = 'xmllint --version'; open(my $CMD, "$command |") or die $!; my $exe = SVN::Notify->find_exe($command); while (<$CMD>) { print $_; } close $CMD;
    Update: Try this:
    #!/usr/bin/perl use strict; use warnings; use SVN::Notify; my $command = 'dir'; my $exe = SVN::Notify->find_exe($command); system($exe);

      Thank you. Sounds good. I tried it. But my problem is now that I always get the message "Cannot locate command" for a existing command and for a not-existing command.

        Remember that "dir" is not an executable. It's a shell built-in. So try "cmd /c dir" as your command (and thus your executable is "cmd"). Though, really, dir is a bad example because it's so trivial to do inside perl. Other built-ins may be more difficult/impossible to emulate in perl, so the question is still more or less valid as a general question, just not so much for this particular example.

      Thanks. Now it works fine after your update. Here a sample code snippet:

      #!/usr/bin/perl use strict; use warnings; use SVN::Notify; my $command = 'perl'; my $exe = SVN::Notify->find_exe($command); if( defined $exe ) { print "Command \"$command\" exists!"; } else { print "Command \"$command\" does NOT exist!"; }

      Only problem is that it is not able to handle parameters. If I use the command "perl --version" then I receive the answer that this command does not exist. If I use only "perl" then I get the answer that it exists.

      Fazit: Thank you a lot. This is a very good method to find out whether a command exists or not. But it is necessary to cut away the parameters of the command before giving it to the SVN::Notify->find_exe function.

Re: Opening not-existing command - Error Handling
by BrowserUk (Patriarch) on Aug 16, 2011 at 09:21 UTC

    FWIW: The only way that you could try to run a non-existant command, is if the text of that command were accepted as input from outside the program. And that is a highly dubious practice without you perform some kind of pre-check that the command supplied as input is something that you are prepared to run on behalf of the user.


    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: Opening not-existing command - Error Handling
by raybies (Chaplain) on Aug 16, 2011 at 14:11 UTC
    In linux when I try to run a command that I'm not sure exists, I always use "which", but as you've noted that's OS dependent. I see that someone at CPAN wrote File::Which, you might have luck with that... as it appears to be OS independent. (I've never used it.)

      I tried File::Which. It is working fine and at the moment I use it to check if a command exists or not.