【问题标题】:Can a program detect if it is launched via shell or not?程序可以检测它是否是通过 shell 启动的吗?
【发布时间】:2025-12-28 16:10:06
【问题描述】:

如果shell 设置为truels 命令会给我一个不同的stdout。这是为什么呢?

是否有一个基本概念允许程序(在这种情况下为ls)检测它是否通过 shell 启动?

我注意到p1p2在 Windows 上共享相同的 stdout,但在 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)

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

【问题讨论】:

  • args 是一个列表时,我认为shell=True 没有意义。 shell 将命令行作为单个字符串处理。
  • @Barmar 我通过将 args 作为字符串发送来检查,行为仍然取决于 ls 是否作为 shell 运行。
  • 我不确定到底发生了什么,但尝试更改为cmd=['echo', 'foo', 'bar'],您会看到一些有趣的结果。当您使用shell=True 时,executable 用作外壳。

标签: python python-3.x shell subprocess


【解决方案1】:

没有办法像你一样通过查看run() 调用的返回值来判断

返回值(p1,即CompletedProcess)不存储shell的属性:

>>> p1.__dict__
{'args': 'ls', 'returncode': 0, 'stdout': None, 'stderr': None}

【讨论】:

  • 如果我的问题不够详细,请原谅。我不是在寻找一种方法来确定是否使用 shell 属性的特定值运行 Python 子进程。我试图理解为什么 p1p2 有不同的标准输出。
  • 这个问题的答案是“因为有人在使用 shell,有人没有。”您还在寻找什么?
  • 当然是这个原因。但我的问题是ls 之类的程序如何检测它是否是通过 shell 运行的?并不是有一些特殊的值说“请作为 shell 程序运行”作为参数发送给它。这里面的工作原理是什么?
  • AFAIK ls 不在乎它是否通过 shell 运行。它会根据 stdout 是否为终端来更改其某些输出格式,仅此而已。
【解决方案2】:

Python 文档很模糊,但行为无法从strace 中隐藏。

您的 Python 代码:

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)

变成(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

这相当于运行这些 shell 命令,即使两者都是从 shell 执行的,您也可以确认它们会给出相同的结果。

ls -c -la           # Generally equivalent to: ls -lca
( exec -a -la ls )  # Generally equivalent to: ls

由此我们可以推断出行为。

如果shell=True,则调用executable。参数列表是executable,后跟shell 标准-c 标志,然后是args。这在executable='bash', args=['ls -la'] 的情况下更有意义。

如果shell=False,则调用可执行文件。参数列表是args。这模仿了execve

所以基本上没有,这里没有检测。这只是对ls 的两次不同调用,也不是您想要的。

【讨论】:

    最近更新 更多