subprocess.run(shell, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
时间: 2023-11-25 09:36:10 浏览: 367
`subprocess.run()` 是 Python 中一个用于执行外部命令的函数。它可以接受一个字符串形式的 shell 命令作为参数,并返回一个 `CompletedProcess` 对象,其中包含了命令执行的结果等信息。
在这个函数中,`stdout=subprocess.DEVNULL` 和 `stderr=subprocess.DEVNULL` 都是用来将标准输出和标准错误输出重定向到空设备上,从而避免在命令执行过程中在终端或日志中看到这些输出。这样做的好处是可以让命令在后台默默执行,提高程序的执行效率。
相关问题
逐条解释 import subprocess new_pass = "your_new_password" command = f'echo {new_pass} | sudo passwd --stdin root' subprocess.run(command, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
这段代码使用Python的`subprocess`模块来执行命令行命令。让我们逐条解释这段代码的每一部分:
1. `import subprocess`: 导入`subprocess`模块,该模块提供了执行外部命令的功能。
2. `new_pass = "your_new_password"`: 定义一个变量`new_pass`,并将其设置为你想要使用的新密码。你可以将其替换为你希望设置的实际密码。
3. `command = f'echo {new_pass} | sudo passwd --stdin root'`: 构建一个命令字符串,其中使用了前一步定义的新密码。这个命令字符串使用`echo`命令将新密码作为输入,并通过管道符号`|`将其传递给`sudo passwd --stdin root`命令。`sudo passwd --stdin root`命令将从标准输入中读取密码,并将其应用于`root`用户。
4. `subprocess.run(command, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)`: 使用`subprocess.run()`函数来执行命令行命令。这个函数接受一个命令字符串作为参数,并通过`shell=True`参数告诉系统使用shell来解释执行命令。`stdout=subprocess.DEVNULL`和`stderr=subprocess.DEVNULL`参数将命令的标准输出和标准错误输出重定向到空设备,以防止输出显示在终端上。
总体而言,这段代码的作用是使用`subprocess`模块执行命令行命令,将新密码作为输入传递给`sudo passwd --stdin root`命令,并将其应用于`root`用户。在执行过程中,输出被重定向到空设备以避免显示在终端上。
Python 代码: p1 = subprocess.Popen( ffmpeg_cmd1, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True ) # 执行第二个命令处理视频 p2 = subprocess.Popen( ffmpeg_cmd2, stdin=p1.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) # 等待两个进程完成 p1.stdout.close() #p1.kill() print('p1done') p2.communicate() print('p2done') p2.communicate() 卡死问题及解决方案
<think>我们之前已经讨论过`subprocess.Popen.communicate()`卡死的问题,并给出了常见的几个原因和解决方案。现在,用户再次提出这个问题,并引用了一些站内资料,其中涉及在Windows上作为不同用户运行、使用`shell=True`导致挂起以及读取阻塞问题。结合用户的问题和之前的回答,我们需要更具体地针对这些引用内容进行解答。
用户引用内容分析:
[1] 提到在Windows上作为不同用户运行子进程。这可能会影响权限,进而导致卡死。
[2] 提到使用`shell=True`时挂起,这可能是由于缓冲区问题或shell启动方式引起。
[3] 提到读取阻塞问题,这与我们之前说的缓冲区死锁一致。
因此,我们将针对这些点,结合之前的解决方案,给出更具体的解决方法。
### 解决`subprocess.Popen.communicate()`卡死的综合方案
#### 1. **缓冲区死锁(最常见)**
**问题**:当子进程的输出量超过操作系统的管道缓冲区大小时,父进程等待子进程结束,而子进程在等待父进程读取输出,造成死锁。
**解决方案**:
- 使用线程或异步读取输出流(适用于大量输出)
- 使用文件替代管道(适用于输出量极大)
**示例代码(使用线程读取)**:
```python
import subprocess
from threading import Thread
def read_stream(stream, buffer):
# 逐行读取流直到结束
for line in iter(stream.readline, b''):
buffer.append(line)
stream.close()
proc = subprocess.Popen(['your_command'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
bufsize=1, # 行缓冲
universal_newlines=True) # 文本模式
stdout_buffer, stderr_buffer = [], []
t1 = Thread(target=read_stream, args=(proc.stdout, stdout_buffer))
t2 = Thread(target=read_stream, args=(proc.stderr, stderr_buffer))
t1.start(); t2.start()
proc.wait() # 等待子进程结束
t1.join(); t2.join() # 等待线程结束
stdout = ''.join(stdout_buffer)
stderr = ''.join(stderr_buffer)
```
#### 2. **子进程等待输入(未关闭标准输入)**
**问题**:子进程可能等待标准输入,而父进程没有提供任何输入。
**解决方案**:
- 如果不需要输入,显式关闭标准输入:
```python
proc = subprocess.Popen(['command'],
stdin=subprocess.DEVNULL, # 关闭输入
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = proc.communicate() # 此时不会等待输入
```
#### 3. **超时处理(防止进程长时间运行)**
**问题**:子进程可能执行时间过长或无响应。
**解决方案(Python 3.3+)**:
```python
try:
proc = subprocess.Popen(['command'], stdout=subprocess.PIPE)
stdout, stderr = proc.communicate(timeout=30) # 设置超时时间
except subprocess.TimeoutExpired:
proc.kill() # 终止子进程
stdout, stderr = proc.communicate() # 清理资源
```
#### 4. **在Windows上作为不同用户运行(引用[1])**
**问题**:在Windows上以不同用户身份运行子进程时,可能需要特殊权限或配置,否则可能导致进程无法启动或挂起。
**解决方案**:
- 使用`runas`命令或Windows API(需要提供用户名和密码)
- 注意:在Windows上以不同用户启动进程通常需要提供密码。
**示例(使用`runas`命令)**:
```python
# 注意:这种方法需要用户交互输入密码,不适用于自动化
# 如果必须自动化,考虑使用Windows服务或任务计划程序
proc = subprocess.Popen(['runas', '/user:OtherUser', 'command'],
stdin=subprocess.PIPE)
# 需要手动输入密码,因此不推荐
```
**替代方案(使用第三方库)**:
使用`pywin32`库可以以指定用户运行进程(需要提供密码):
```python
import win32api, win32process, win32security
from win32con import LOGON_WITH_PROFILE
user = "domain\\username"
password = "password"
command = "path/to/executable"
# 创建进程
handle = win32security.LogonUser(user, None, password,
win32security.LOGON32_LOGON_INTERACTIVE,
win32security.LOGON32_PROVIDER_DEFAULT)
win32process.CreateProcessAsUser(handle, None, command, None, None, False, 0, None, None, win32process.STARTUPINFO())
```
> **注意**:在代码中存储明文密码存在安全风险,应谨慎使用。
#### 5. **避免使用`shell=True`(引用[2])**
**问题**:使用`shell=True`可能引起额外的shell进程,增加死锁风险,且存在安全风险(命令注入)。
**解决方案**:
- 尽可能避免使用`shell=True`,直接传递命令和参数列表。
- 如果必须使用(例如需要shell特性),确保正确处理引号和转义。
**示例**:
```python
# 不推荐:使用shell=True
proc = subprocess.Popen('ls -l', shell=True, stdout=subprocess.PIPE) # 可能挂起
# 推荐:避免shell=True
proc = subprocess.Popen(['ls', '-l'], stdout=subprocess.PIPE) # 直接执行
```
#### 6. **读取阻塞问题(引用[3])**
**问题**:直接使用`stdout.read()`或`stderr.read()`可能阻塞,直到流关闭。
**解决方案**:
- 使用非阻塞读取(如`select`模块)或线程(如前所述)。
- 使用`communicate()`时,它本身会读取所有数据直到结束,但如果输出过大可能死锁,所以建议使用超时或线程。
### 总结
`communicate()`卡死通常由缓冲区死锁、子进程等待输入、超时未处理或权限问题引起。针对不同情况,我们可以:
1. 使用线程读取输出流
2. 关闭不需要的标准输入
3. 设置超时并处理超时异常
4. 在Windows上以不同用户运行时使用适当的方法
5. 避免不必要的`shell=True`
6. 对于输出量极大的情况,考虑使用文件
> **特别提示**:在Windows上,管道行为可能与Unix-like系统不同,建议在Windows环境中测试解决方案[^1]。
§§相关问题§§
1. 如何在Python中安全地执行外部命令(避免命令注入)?
2. 在Windows上如何以管理员身份运行Python子进程?
3. 如何实时获取子进程的输出而不发生阻塞?
--相关问题--:
1.如何在Python中安全地执行外部命令(避免命令注入)?
2.在Windows上如何以管理员身份运行Python子进程?
3.如何实时获取子进程的输出而不发生阻塞?</think>### Python subprocess.Popen.communicate() 卡死问题深度解析
`subprocess.Popen.communicate()` 卡死是常见问题,通常由以下原因引起:
#### 1. **缓冲区死锁(最常见)**
当子进程输出超过操作系统管道缓冲区(通常 64KB)时,父进程等待子进程结束,而子进程在等待父进程读取输出,造成死锁[^2][^3]。
**解决方案**:
```python
import subprocess
from threading import Thread
def stream_reader(stream, buffer):
while True:
chunk = stream.read(4096) # 分块读取
if not chunk: break
buffer.append(chunk)
proc = subprocess.Popen(['your_command'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
bufsize=0) # 无缓冲模式
stdout_buf, stderr_buf = [], []
t1 = Thread(target=stream_reader, args=(proc.stdout, stdout_buf))
t2 = Thread(target=stream_reader, args=(proc.stderr, stderr_buf))
t1.start(); t2.start()
proc.wait() # 等待进程结束
t1.join(); t2.join() # 等待读取线程结束
stdout = b''.join(stdout_buf).decode()
stderr = b''.join(stderr_buf).decode()
```
#### 2. **子进程等待输入**
当子进程需要 STDIN 输入但未收到时[^3]:
```python
# 明确关闭输入流
proc = subprocess.Popen(['cmd'],
stdin=subprocess.DEVNULL, # 显式关闭输入
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out, err = proc.communicate()
```
#### 3. **Windows 特定问题**
在 Windows 上作为不同用户运行时[^1]:
```python
import win32api, win32process, win32security
user = "domain\\username"
password = "password"
command_line = "your_command"
# 创建安全令牌
handle = win32security.LogonUser(
user, None, password,
win32security.LOGON32_LOGON_INTERACTIVE,
win32security.LOGON32_PROVIDER_DEFAULT
)
# 创建进程
startup = win32process.STARTUPINFO()
proc_info = win32process.CreateProcessAsUser(
handle, None, command_line, None, None, False,
0, None, None, startup
)
win32api.CloseHandle(handle)
```
#### 4. **超时处理(Python 3.3+)**
```python
try:
proc = subprocess.Popen(['slow_command'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
outs, errs = proc.communicate(timeout=30) # 设置超时
except subprocess.TimeoutExpired:
proc.kill() # 强制终止
outs, errs = proc.communicate() # 清理资源
```
#### 5. **输出量极大时的优化**
使用临时文件避免管道阻塞:
```python
import tempfile
with tempfile.TemporaryFile() as outfile, \
tempfile.TemporaryFile() as errfile:
proc = subprocess.Popen(['high_output_command'],
stdout=outfile,
stderr=errfile)
proc.wait()
outfile.seek(0)
errfile.seek(0)
stdout = outfile.read().decode()
stderr = errfile.read().decode()
```
### 关键预防措施
1. **避免 `shell=True`**:除非必要,否则显式传递参数列表[^2]
```python
# 危险做法
Popen('ls -l | grep py', shell=True)
# 安全做法
Popen(['ls', '-l'], stdout=PIPE)
```
2. **实时读取输出**:对于长时间进程,使用增量读取
```python
proc = Popen(['cmd'], stdout=PIPE, text=True)
while proc.poll() is None:
print(proc.stdout.readline(), end='')
```
3. **检查进程状态**:
```python
if proc.poll() is None: # 仍在运行
print("Process is hanging")
```
> **诊断工具**:在 Linux 使用 `strace -p <PID>` 分析系统调用,Windows 使用 Process Monitor[^1]。
阅读全文
相关推荐














