Reputation: 902
The ls
command is giving me a different stdout if shell
is set to true
. Why is that ?
Is there an underlying concept that allow a program (ls
in this case) to detect if it is launched via shell or not ?
I noticed that both p1
and p2
share the same stdout on Windows, but not on Linux.
import subprocess
cmd = ['ls', '-la']
# Using shell
p1 = subprocess.run(executable=cmd[0], args=cmd[1:], shell=True, text=True, capture_output=True)
# Without using shell
p2 = subprocess.run(executable=cmd[0], args=cmd[1:], shell=False, text=True, capture_output=True)
print(p1.stdout)
print(p2.stdout)
Output on Linux
total 12
drwxr-xr-x 2 root root 4096 Feb 20 18:25 .
drwx------ 10 root root 4096 Feb 20 18:51 ..
-rw-r--r-- 1 root root 269 Feb 20 18:57 test.py
test.py
Upvotes: 0
Views: 420
Reputation: 123600
The Python documentation is fuzzy, but the behavior can't hide from strace
.
Your Python code:
cmd = ['ls', '-la']
p1 = subprocess.run(executable=cmd[0], args=cmd[1:], shell=True, text=True, capture_output=True)
p2 = subprocess.run(executable=cmd[0], args=cmd[1:], shell=False, text=True, capture_output=True)
Turns into (strace -f -e execve python3 foo.py
):
[pid 143557] execve("/bin/ls", ["ls", "-c", "-la"], 0x7fffc1235340 /* 34 vars */) = 0
[pid 143558] execve("/bin/ls", ["-la"], 0x7fffc1235340 /* 34 vars */) = 0
Which is equivalent to running these shell commands, which you can confirm gives the same result even though both are being executed from a shell.
ls -c -la # Generally equivalent to: ls -lca
( exec -a -la ls ) # Generally equivalent to: ls
From this we can deduce the behavior.
If shell=True
, the executable
is invoked. The argument list is the executable
, followed by the shell standard -c
flag, followed by args
. This makes more sense in the case of executable='bash', args=['ls -la']
.
If shell=False
, the executable is invoked. The argument list is args
. This mimics execve
.
So essentially no, there is no detection going on here. It's simply two different invocations of ls
, and neither is what you wanted.
Upvotes: 2
Reputation: 8966
There is no way to tell by looking at the return value of the run()
call like you are.
The returned value (p1
, which is a CompletedProcess
) does not store an attribute for shell:
>>> p1.__dict__
{'args': 'ls', 'returncode': 0, 'stdout': None, 'stderr': None}
Upvotes: 1