Check if a command is valid

K

Kenny Meyer

Hello,

I have to figure out if a string is callable on a Linux system. I'm
actually doing this:

def is_valid_command(command):
retcode = 100 # initialize
if command:
retcode = subprocess.call(command, shell=True)
if retcode is 0:
print "Valid command."
else:
print "Looks not so good..."

is_valid_command("ls")

Never mind the code, because this is not the original.
The side effect of subprocess.call() is that it *actually* executes
it, but I just need the return code. What are better ways of doing
this?
 
C

Chris Rebert

Hello,

I have to figure out if a string is callable on a Linux system. I'm

"callable" seems vague. Is a command string with invalid arguments but
a valid executable "callable"? If no, then there's no general way to
test "callability" without actually running the command.
actually doing this:

   def is_valid_command(command):
       retcode = 100 # initialize
       if command:
           retcode = subprocess.call(command, shell=True)
       if retcode is 0:

That should be `== 0`, not `is 0`. The fact that `is 0` just so
happens to work is an implementation detail.
           print "Valid command."
       else:
           print "Looks not so good..."

   is_valid_command("ls")

Never mind the code, because this is not the original.
The side effect of subprocess.call() is that it *actually* executes
it, but I just need the return code.

Well, you're not gonna be able to get the command's return code
without actually running it (unless perhaps you're referring to a
return code from the shell itself?).
What are better ways of doing this?

One idea:

from shlex import split as shell_tokenize
from subprocess import check_output

def is_valid_command(command):
try:
executable = shell_tokenize(command)[0]
except (ValueError, IndexError):# invalid shell syntax
return False
return bool(check_output(['which', executable]))# on the PATH?

Cheers,
Chris
 
J

Jean-Michel Pichavant

Kenny said:
Hello,

I have to figure out if a string is callable on a Linux system. I'm
actually doing this:

def is_valid_command(command):
retcode = 100 # initialize
if command:
retcode = subprocess.call(command, shell=True)
if retcode is 0:
print "Valid command."
else:
print "Looks not so good..."

is_valid_command("ls")

Never mind the code, because this is not the original.
The side effect of subprocess.call() is that it *actually* executes
it, but I just need the return code. What are better ways of doing
this?
I'm not sure I get exactly what you're searching for but here's
something that may help.

If you just whant to know if a command (without parameter) is a Linux
command (either a builtin, alias of file exe) you can use the "where"
command and inspect its return code, the command is not executed though.
>where ls
ls is an alias for ls --color=auto -F
ls is /bin/ls
>where apt-get apt-get is /usr/bin/apt-get
>where doesnotexists
doesnotexists not found
zsh: exit 1

retcode = subprocess.call(command, shell=True)

becomes

retcode = subprocess.call("where " + command)

JM

NB : this does not work with parameters appened to the command.
 
C

Chris Rebert

I'm not sure I get exactly what you're searching for but here's something
that may help.

If you just whant to know if a command (without parameter) is a Linux
command (either a builtin, alias of file exe) you can use the "where"
command and inspect its return code, the command is not executed though.

ls is an alias for ls --color=auto -F
ls is /bin/ls
doesnotexists not found
zsh: exit 1

`where` seems to be a zsh built-in:
$ # I'm in UR bash
$ nonexistent
-bash: nonexistent: command not found
$ where bash
-bash: where: command not found

And not everyone has zsh installed, so...
I don't see why one shouldn't use the standard `which` *nix command instead..

Also, in retrospect, my suggestion should probably have checked the
return code rather than the output; more efficient and simple that
way.

Cheers,
Chris
 
H

Hans Mulder

Chris said:
`where` seems to be a zsh built-in:
$ # I'm in UR bash
$ nonexistent
-bash: nonexistent: command not found
$ where bash
-bash: where: command not found

And not everyone has zsh installed, so...
I don't see why one shouldn't use the standard `which` *nix command instead.

Because `which` ia a C shell script. It reads your .cshrc, to see which
aliases would be defined if you were to use the C shell, but it doesn't
look at your .bashrc.

You're probably better off using `type`: it knows about built-ins and
shell functions and that sort of stuff:

$ which type
/usr/bin/type
$ type type
type is a shell builtin
$

Guess which answer is more relevant to you .....

HTH,

-- HansM
 
K

Kenny Meyer

Chris said:
"callable" seems vague. Is a command string with invalid arguments but
a valid executable "callable"? If no, then there's no general way to
test "callability" without actually running the command.

I'm glad you pointed that out, because you're right. I subconciously
meant a file that is in the $PATH.

[snip]
Well, you're not gonna be able to get the command's return code
without actually running it (unless perhaps you're referring to a
return code from the shell itself?).
What are better ways of doing this?

One idea:

from shlex import split as shell_tokenize
from subprocess import check_output

def is_valid_command(command):
try:
executable = shell_tokenize(command)[0]
except (ValueError, IndexError):# invalid shell syntax
return False
return bool(check_output(['which', executable]))# on the PATH?
I have tried this and found some unexpected issues with Python 2.6 which I
though I should point out:

Firstly, the function `check_output` in the `subprocess` module only comes with
Python 2.7, but I have found a similar function called `check_call` [1] which
seems is similar, but not the same.

[1] http://docs.python.org/library/subprocess.html#subprocess.check_call

The code now looks like this:

from shlex import split as shell_tokenize
from subprocess import check_call, CalledProcessError

def is_valid_command(command):
try:
executable = shell_tokenize(command)[0]
check_call(['which', executable]) # Raises CalledProcessError if
# something went wrong
return True
except (ValueError, IndexError, CalledProcessError): # Catch exception if there
# was an error calling the process
return False

The idea with `which` is really great one.

Thanks a lot, for your time and your input.

--
Onward and upwards,
Kenny Meyer

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)

iEYEARECAAYFAkw84gwACgkQ+/8gJfVrobKgrgCeMQ8WNJRAiiZABP5N+PSz/gHX
PmYAniP9otwBOF68h+npy0DXv51z8pTN
=8nta
-----END PGP SIGNATURE-----
 
K

Kenny Meyer

Because `which` ia a C shell script.  It reads your .cshrc, to see which
aliases would be defined if you were to use the C shell, but it doesn't
look at your .bashrc.

You're probably better off using `type`: it knows about built-ins and
shell functions and that sort of stuff:

$ which type
/usr/bin/type
$ type type
type is a shell builtin
$

Guess which answer is more relevant to you .....

HTH,

-- HansM

Oh thanks, Hans! `type` seems to a good alternative. Surely it can
also get the job (better) done.
 
S

Steven W. Orr

Hello,

I have to figure out if a string is callable on a Linux system. I'm
actually doing this:

def is_valid_command(command):
retcode = 100 # initialize
if command:
retcode = subprocess.call(command, shell=True)
if retcode is 0:
print "Valid command."
else:
print "Looks not so good..."

is_valid_command("ls")

Never mind the code, because this is not the original.
The side effect of subprocess.call() is that it *actually* executes
it, but I just need the return code. What are better ways of doing
this?

Luke! Use the force!

#! /usr/bin/python

import os
def is_valid_command(command):
looking_good = False
for ii in os.environ['PATH'].split(':'):
if os.access(ii + '/' + command, os.X_OK):
looking_good = True
break
print ["Looks not so good...", "Valid command."][looking_good]

is_valid_command('python')
is_valid_command('pythoon')

This way you don't start up any subprocesses and you are actually doing what
the shell would do for you.

THE ONLY DIFFERENCE is that a persistent bash would hash all of the contents
of what lives in PATH and so might have a slight shot of being faster under
somewhat obscure conditions.

I would strongly encourage you to not execute an arbitrary string to see if it
returns a pretty return code.

is_valid_command('{cd /; rm -rf /}')

Warning:
* It only checks if the command exists in PATH and is executable TO YOU:
* Do not make fun of is_valid_command. It will get angry.
* You might also want to beef it up a la
pp = ii + '/' + command
if os.access(pp, (os.X_OK) and not os.stat.isdir(p)):
so you are checking executable files and not directories etc...
* More warnings can be amplified upon over pitchers of beer.

--
Time flies like the wind. Fruit flies like a banana. Stranger things have .0.
happened but none stranger than this. Does your driver's license say Organ ..0
Donor?Black holes are where God divided by zero. Listen to me! We are all- 000
individuals! What if this weren't a hypothetical question?
steveo at syslang.net


-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.10 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org/

iEYEARECAAYFAkw+IRwACgkQRIVy4fC+NyT+EgCgg3fJy58fMnK9Y/HvBe6HzLtU
sRQAnAr1vr7cLsei1eDS6Yr6deolMLeL
=SnuZ
-----END PGP SIGNATURE-----
 
G

Grant Edwards

I have to figure out if a string is callable on a Linux system. I'm
actually doing this:

def is_valid_command(command):
retcode = 100 # initialize
if command:
retcode = subprocess.call(command, shell=True)
if retcode is 0:
print "Valid command."
else:
print "Looks not so good..."

is_valid_command("ls")

Never mind the code, because this is not the original. The side
effect of subprocess.call() is that it *actually* executes it, but I
just need the return code. What are better ways of doing this?

Luke! Use the force!

#! /usr/bin/python

import os
def is_valid_command(command):
looking_good = False
for ii in os.environ['PATH'].split(':'):
if os.access(ii + '/' + command, os.X_OK):
looking_good = True
break
print ["Looks not so good...", "Valid command."][looking_good]

is_valid_command('python')
is_valid_command('pythoon')

Just to be clear, that's not the same as the OP's code in two
respects:

1) It doesn't handle shell builtins or aliases.

2) It determines not whether a command is valid (returns 0), but
whether a command exists as an executable. "Valid" is a rather
small subset of "exists".

Of course the OP didn't explain what he meant by "callable", so all we
have to go on is his posted code.
This way you don't start up any subprocesses and you are actually
doing what the shell would do for you.

THE ONLY DIFFERENCE is that a persistent bash would hash all of the
contents of what lives in PATH and so might have a slight shot of
being faster under somewhat obscure conditions.

No, there are other differences. See above.
I would strongly encourage you to not execute an arbitrary string to
see if it returns a pretty return code.

is_valid_command('{cd /; rm -rf /}')

Warning:
* It only checks if the command exists in PATH and is executable TO
YOU:

Which is different than determining whether a command (including
arguments) is valid (callable and returns 0). However, running a
command to determine if it's valid is going to cause problems sooner
or later due to side-effects of that command.

For example, the first time you the command "rm /path/to/a/file" it
may be valid, but the second time it won't be.
* Do not make fun of is_valid_command. It will get angry.

And don't taunt happy fun ball!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top