sup*_*gra 6 python subprocess io-redirection
我有一个父Python脚本会启动子(其发射的孙子),并经过一段时间后,我终止子,但孙子继续泵到标准输出。在我杀死孩子之后,我想抑制/重定向孙子(及其所有后代)的 stdout 和 stderr。
这是家长:
import time
import subprocess
proc = subprocess.Popen('./child.sh')
print("Dad: I have begotten a son!")
time.sleep(1)
proc.kill()
proc.wait()
print("Dad: My son hath died!")
time.sleep(2)
print("Dad: Why does my grandson still speak?")
Run Code Online (Sandbox Code Playgroud)
这是我无法修改的子脚本。
#!/bin/bash
./grandchild.sh &
echo "Child: I had a son!"
for (( i = 0; i < 10; i++ )); do
echo "Child: Hi Dad, meet your grandson!"
sleep 1
done
exit 0
Run Code Online (Sandbox Code Playgroud)
这是一个吵闹的孙子,我无法修改。
#!/bin/bash
for (( i = 0; i < 10; i++ )); do
echo "Grandchild: Wahhh"
sleep 1
done
exit 0
Run Code Online (Sandbox Code Playgroud)
在杀死孩子之前,我尝试这样做:
import os
f = open(os.devnull,"w")
proc.stdout = proc.stderr = f
Run Code Online (Sandbox Code Playgroud)
但它似乎不起作用。输出是:
> ./parent.py
Dad: I have begotten a son!
Child: I had a son!
Child: Hi Dad, meet your grandson!
Grandchild: Wahhh
Dad: My son hath died!
Grandchild: Wahhh
Grandchild: Wahhh
Dad: My grandson still speaketh!
Grandchild: Wahhh
Grandchild: Wahhh
Grandchild: Wahhh
Grandchild: Wahhh
Grandchild: Wahhh
Grandchild: Wahhh
Grandchild: Wahhh
Run Code Online (Sandbox Code Playgroud)
当您调用时,subprocess.Popen您可以告诉它重定向stdout和/或stderr. 如果不这样做,它会允许操作系统从 Python 进程的实际STDOUT_FILENO和STDERR_FILENO(它们是固定常量 1 和 2)进行复制,从而使它们不被重定向。
这意味着,如果 Python 的 fd 1 和 2 将访问您的 tty 会话(可能在底层设备上,例如/dev/pts/0),则子项\xe2\x80\x94,在这种情况下,孙子也是\xe2\ x80\x94 直接与同一个会话对话(相同/dev/pts/0)。您在 Python 进程本身中所做的任何操作都无法改变这一点:这些是独立的进程,可以独立、直接访问会话。
您可以做的是./child.sh在适当的位置进行重定向来调用:
proc = subprocess.Popen(\'./child.sh\', stdout=subprocess.PIPE)\nRun Code Online (Sandbox Code Playgroud)\n\n快速旁注编辑:如果您想丢弃子级及其孙级的所有os.devnull输出,请打开(或者像您所做的那样,或者使用os.open()来获取原始整数文件描述符)并将 stdout 和 stderr 连接到底层文件描述符。如果您已将其作为 Python 流打开:
f = open(os.devnull, "w")\nRun Code Online (Sandbox Code Playgroud)\n\n那么底层文件描述符是f.fileno():
proc = subprocess.Popen(\'./child.sh\', stdout=f.fileno(), stderr=f.fileno())\nRun Code Online (Sandbox Code Playgroud)\n\n在这种情况下,您无法从所涉及的任何进程中获得任何输出。
\n\n现在子级中的文件描述符 1 连接到管道实体,而不是直接连接到会话。(由于没有stderr=上面的内容,子进程中的fd 2仍然直接连接到会话。)
存在于操作系统内部的管道实体只是从一端(管道的“写端”)复制到另一端(“读端”)。你的Python进程可以控制读端。您必须通常不直接调用操作系统read系统调用\xe2\x80\x94,而是在读取端参见下面的\xe2\x80\x94,以收集其输出。
一般来说,如果您停止从读端读取,管道将“填满”,并且任何尝试write在写端上进行操作系统级别的进程都会被“阻止”,直到有人有权访问读端(即您) ,再次)从中读取。
如果丢弃读端,使管道无处转储其输出,则写端开始返回EPIPE错误SIGPIPE并向任何尝试操作系统级调用的进程发送信号write。当您调用操作系统级close系统调用时,假设您没有将描述符交给其他进程,就会发生这种丢弃。当您的进程退出时也会发生这种情况(再次在相同的假设下)。
没有方便的方法可以将读取端连接到像这样的无限数据接收器/dev/null,至少在大多数类 Unix 系统中(有一些系统调用一些特殊的系统调用来完成这种“管道”)。但如果你打算杀死这个孩子并愿意让他的孙子死于SIGPIPE信号,你可以简单地关闭描述符(或退出),让筹码落到它们可能落下的地方。
子孙可以通过设置或阻止来保护自己SIGPIPE免于SIG_IGN死亡SIGPIPE。exec信号掩码在系统调用之间继承,因此在某些情况下,您可以阻止SIGPIPE子级(但有些子级会取消阻止信号)。
如果关闭描述符不合适,您可以创建一个新进程,仅读取并丢弃传入的管道数据。如果您使用fork系统调用,这很简单。另外,一些类 Unix 系统允许您通过套接字将文件描述符传递AF_UNIX给其他不相关的(父/子)进程,因此您可以有一个守护进程来执行此操作,并且可以通过套接字访问AF_UNIX。(这对于代码来说并不简单。)
如果您希望子进程将其 stderr 输出发送到同一管道,以便您可以读取其 stdout 和 stderr,只需添加stderr=subprocess.STDOUT到Popen()调用中即可。如果您希望子进程将其 stderr 输出发送到单独的管道,请添加stderr=subprocess.PIPE. 然而,如果你选择后者,事情可能会变得有点棘手。
为了防止子进程阻塞,如上所述,您必须调用操作系统read调用。如果只有一根管道,这很容易:
for line in proc.stdout:\n ...\nRun Code Online (Sandbox Code Playgroud)\n\n例如,或者:
\n\nline = proc.stdout.readline()\nRun Code Online (Sandbox Code Playgroud)\n\n将一次一行读取管道(Python 内的模缓冲)。您可以根据需要阅读任意多行或任意行。
\n\n但是,如果有两个管道,则必须读取“已满”的管道。Python 的subprocess模块定义了communicate()为您执行此操作的函数:
stdout, stderr = proc.communicate()\nRun Code Online (Sandbox Code Playgroud)\n\n这里的缺点是communicate()读取完成:它需要获取可以到达每个管道写入端的所有输出。这意味着它会重复调用操作系统级read操作,直到read指示数据结束。仅当所有在某个时刻对相应管道的写入端具有写入访问权限的进程都关闭了管道的该端时,才会发生这种情况。换句话说,它等待close连接到管道写入端的描述符的子级和任何孙级。
一般来说,只使用一个管道要简单得多,根据需要读取尽可能多的内容(但仅读取尽可能多的内容),然后只需关闭该管道即可:
\n\nproc = subprocess.Popen(\'./child.sh\', stdout=subprocess.PIPE)\nline1 = proc.stdout.readline()\nline2 = proc.stdout.readline()\n# that\'s all we care about\nproc.stdout.close()\nproc.kill()\nstatus = proc.wait()\nRun Code Online (Sandbox Code Playgroud)\n\n这是否足够取决于您的具体问题。
\n| 归档时间: |
|
| 查看次数: |
1528 次 |
| 最近记录: |