Reputation: 12452
is_exec = lambda x: subprocess.call("type " + x, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0 and (os.path.isfile(x) and os.access(x, os.X_OK))
I came across this code and it works fine but is it redundant?
Isnt is_exec = lambda x: os.access(x, os.X_OK)
sufficient?
Question: Is there a case where is_exec = lambda x: os.access(x, os.X_OK)
isnt catching but the first one does?
Upvotes: 0
Views: 141
Reputation: 54551
There is a subtle difference here -- in the first call, this will also detect shell built-ins, for example:
$ type cd
cd is a shell builtin
$ echo $?
0
But there is no actual cd
executable as such, so you can't check it explicitly with os.access()
. That said, I believe it should actually be
is_exec = lambda x: subprocess.call("type " + x, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0 or (os.path.isfile(x) and os.access(x, os.X_OK))
Note that the middle operator is now or
. Also, the subprocess pipes seem a bit superfluous here, and there is the caveat that it can be used as a shell injection to consider too.
All in all, if you only care about verifying exeutable files, then ditching the first bit is fine.
Upvotes: 1
Reputation: 530970
Both require os.access(x, os.X_OK)
to return True. However, in addition to performing some unnecessary tests before calling os.access
, the first opens you up to a shell injection attack unless you carefully screen the value of x
before using it. Using shell=True
with subprocess.call
simply passes a string to the shell for execution. If the value of x
is carefully constructed, you end up executing more than just the type
command. For example:
x = "somefile.txt; rm foo.txt"
subprocess.call("type " + x, shell=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
will pass the string "type somefile.txt; rm foo.txt" to the shell, resulting in two commands, not just one, in being executed.
Upvotes: 1