为什么Popen.stdout只包含部分输出?

时间:2015-08-06 10:13:17

标签: python subprocess stdout popen ros

我使用子进程模块在python中同时运行两个进程:

p_topic = subprocess.Popen(['rostopic','echo','/msg/address'], stdout=PIPE)
p_play = subprocess.Popen(['rosbag','play',bagfile_path])

这些是ROS进程:p_topic侦听要播放的.bag文件,并将该.bag文件中的某些信息输出到stdout流;我想使用p_topic.stdout对象(表现为文件)访问此输出。

然而,我发现的事情是p_topic.stdout对象只包含它应该具有的第一个〜1/3的输出行 - 也就是说,与手动运行这两个命令相比,同时在两个shell中并排。

我已经尝试等待很长时间才能完成输出,但这并没有改变任何东西,它每次都被p_topic.stdout捕获的线的比例大致相同。任何关于这可能是什么的提示将不胜感激!

编辑:

以下是阅读代码:

#wait for playing to stop
while p_play.poll() == None:
    time.sleep(.1)

time.sleep(X)#wait for some time for the p_topic to finish
p_topic.terminate()

output=[]
for line in p_topic.stdout:
    output.append(line)

请注意,time.sleep(X)中的值X没有任何区别

2 个答案:

答案 0 :(得分:1)

默认情况下,当进程的stdout未连接到终端时,输出将被块缓冲。连接到终端时,它的线路缓冲。您希望获得完整的行,但除非rostopic取消缓冲或显式行缓冲区stdout(如果它是C程序,您可以使用setvbuf自动执行此操作)。

另一种(可能是重叠的)可能性是管道缓冲区本身正在填充(管道缓冲区通常相当小),并且因为你永远不会消耗它,rostopic填充管道缓冲区然后无限制地阻塞直到你杀死它,当你阅读过程的stdout时,只留下管道中适合排水的东西。在这种情况下,您需要生成一个线程以防止管道从Python中耗尽,或者让主线程使用select模块组件来监视和排空管道(与轮询其他进程混合)。该线程通常更容易,但您需要小心避免线程安全问题。

答案 1 :(得分:0)

是否值得尝试流程沟通/等待?而不是睡觉,这会解决你的问题吗?

我将此作为一般用途,因此不确定您是否可以将其更改为您需要的内容?

    executable_Params = "{0} {1} {2} {3} {4}".format(my_Binary, 
                                                       arg1, 
                                                       arg2, 
                                                       arg3, 
                                                       arg4)

    # execute the process
    process = subprocess.Popen(shlex.split(executable_Params), 
                               shell=False, 
                               stderr=subprocess.PIPE, 
                               stdout=subprocess.PIPE)


    stdout, stderr = process.communicate()
    ret_code = process.wait()

    if ret_code == 0:
        return 0
    else:
        #get the correct message from my enum method
        error_msg = Process_Error_Codes(ret_code).name
        raise subprocess.CalledProcessError(returncode=ret_code, 
                                            cmd=executable_Params)