我有一个Python脚本(popen.py
),它运行另一个Python脚本(counter.py
)作为子进程,输出重定向到/tmp/counter.log
。我正在使用的代码是:
/tmp/counter.py
#!/usr/bin/env python2
import time
i = 0
while True:
print i
i +=1
time.sleep(1)
/tmp/popen.py
#!/usr/bin/env python2
import subprocess
f = open("/tmp/counter.log", "a+")
p = subprocess.Popen("/tmp/counter.py", stdout=f, stderr=f, bufsize=1)
然而,当我运行popen.py
时,子进程被创建并保持运行,但是在输出达到4096字节之前没有任何内容写入/tmp/counter.log
,然后它似乎被刷新到文件。
有没有办法让我的子进程逐行写入日志文件而不修改counter.py
脚本本身?
我不想修改counter.py
的原因是子进程可能并不总是运行Python脚本。我尝试过运行一个小的可执行文件(用C编写)同样的问题,同样的问题也出现了。
我已尝试为该文件编写自我刷新包装,并按照here所述使用stdout
,但这也不起作用。
我已经使用lsof
和strace
进行了一些调试,这是我设法找到的:
手动运行/tmp/counter.py
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
python2 629 daniel 0u CHR 136,0 0t0 3 /dev/pts/0
python2 629 daniel 1u CHR 136,0 0t0 3 /dev/pts/0
python2 629 daniel 2u CHR 136,0 0t0 3 /dev/pts/0
通过/tmp/popen.py
运行/tmp/counter.pyCOMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
python2 638 daniel 0u CHR 136,0 0t0 3 /dev/pts/0
python2 638 daniel 1u REG 202,0 0 768 /tmp/counter.log
python2 638 daniel 2u REG 202,0 0 768 /tmp/counter.log
手动运行/tmp/counter.py
select(0, NULL, NULL, NULL, {1, 0}) = 0 (Timeout)
write(1, "11\n", 3) = 3
select(0, NULL, NULL, NULL, {1, 0}) = 0 (Timeout)
write(1, "12\n", 3) = 3
select(0, NULL, NULL, NULL, {1, 0}) = 0 (Timeout)
write(1, "13\n", 3) = 3
select(0, NULL, NULL, NULL, {1, 0}) = 0 (Timeout)
write(1, "14\n", 3) = 3
select(0, NULL, NULL, NULL, {1, 0}) = 0 (Timeout)
write(1, "15\n", 3) = 3
通过/tmp/popen.py
运行/tmp/counter.pyselect(0, NULL, NULL, NULL, {1, 0}) = 0 (Timeout)
select(0, NULL, NULL, NULL, {1, 0}) = 0 (Timeout)
select(0, NULL, NULL, NULL, {1, 0}) = 0 (Timeout)
select(0, NULL, NULL, NULL, {1, 0}) = 0 (Timeout)
select(0, NULL, NULL, NULL, {1, 0}) = 0 (Timeout)
...
write(1, "11\n12\n13\n14\n15\n16\n17\n18\n"..., 4096) = 4096
答案 0 :(得分:1)
我最终使用的解决方案并没有完全解决问题,但此时最可接受的折衷方案是在生成子进程时设置PYTHONUNBUFFERED
环境变量:
#!/usr/bin/env python2
import subprocess
f = open("/tmp/counter.log", "a+")
p = subprocess.Popen("/tmp/counter.py", stdout=f, stderr=f, env={
"PYTHONUNBUFFERED": "Yes please"
})
这在额外代码和其他进程方面的开销最低,但仅在子进程是Python脚本时才有效。
答案 1 :(得分:0)
通常,您不能让进程逐行写入文件,除非进程定期刷新。但是你可以让调用过程看起来像一个终端。遵循CLIB规则的进程将切换到行模式并为您提供所需的内容。在这个例子中,我设置为伪终端并写入+刷新日志文件。
#!/usr/bin/env python2
import os
import subprocess
import pty
master,slave = pty.openpty()
f = open("/tmp/counter.log", "a+")
p = subprocess.Popen(["python", "counter.py"], stdout=slave, stderr=slave, close_fds=True)
os.close(slave)
reader = os.fdopen(master)
while True:
data = reader.readline()
if not data:
break
f.write(data)
f.flush()
print data.strip()
print 'done'
reader.close()
p.wait()
答案 2 :(得分:-1)
实际上,subprocess.Popen可用于Python脚本以外的可执行文件。这是一个片段,它创建了用户的cron时间表的副本:
import subprocess
import shlex
def getTempCrontabFile(argTmpFile='/tmp/tmpFile'):
# Create a file in r/w mode that will be the target for
# the crontab utility redirection.
try:
tmpFile = open(argTmpFile, 'a+')
except IOError as customErr:
print 'Failed to open or create temporary crontab file.'
print customErr
return customErr
# Define the command line to list the cron schedule.
cmdLine = 'crontab -l'
# Format the command line into an array of arguments. This is
# useful for proper formatting of spaces and quoted arguments
# especially when commands get complicated.
args = shlex.split(cmdLine)
# Make the call to Popen using the file we created for stdout.
result = subprocess.Popen(args, stdout=tmpFile)
return result