How to intercept transparently stdin/out/err

南笙酒味 提交于 2021-02-19 09:27:07

问题


I would like to catch all the inputs and output of a commandline program (namely, GDB, but currently changed to ls or cat for simplicity) and redirect it into a file, for later analysis.

I couldn't get anything close to working, but I can't understand what's wrong. Here is my last attempt:

#!/usr/bin/env python2

import subprocess
import sys
import select
import os

def get_pipe():
  fd_r, fd_w = os.pipe()
  return os.fdopen(fd_r, "r"), os.fdopen(fd_w, "w")

out_r, out_w = get_pipe()
err_r, err_w = get_pipe()
in_r, in_w = get_pipe()

proc = subprocess.Popen(["ls"] + sys.argv[1:], stdin=in_r, stdout=out_w, stderr=err_w)

out_w.close()
err_w.close()
in_r.close()

running = True
while running:
  input_lst, output_lst, x_lst = select.select([sys.stdin],[out_r, err_r],  [], 0.5)

  if out_r in output_lst+input_lst:
    data = out_r.readlines()
    print "*",data,"*"
  if err_r in output_lst+input_lst:
    data = err_r.readlines()
    print "+",data,"+"
  if sys.stdin in input_lst:
    data = sys.stdin.readline()
    print "-", data, "-"
    in_w.write(data)

  # don't try to quit if we received some data
  if len(input_lst+output_lst+x_lst) != 0:
    continue
  retcode = proc.poll()
  if retcode is not None:
    running = False

out_r.close()
err_r.close()
in_w.close
exit(retcode)

I tried several other options, like - writing a file wrapper, which was supposed to write to an external file everything written into stdin / read from stdout-err - named pipes - ...

but the best I obtained was the very first lines of "ls".

Moreover, GDB relies on readline for CLI edition, and I feel like it won't be that easy to catch transparently!


回答1:


So after quite a while of research, I found a solution to this problem:

with non blocking reads and writes, we just have to wait for the input file to run out of data (and thrown an exception), and then operate on the same for the output (stdout and stderr):

#!/usr/bin/python2

import sys, os
import subprocess
import fcntl

dump = open("/tmp/dump", "w")
dump.write("### starting %s ###" % " ".join(sys.argv))

proc = subprocess.Popen(["<real app>"] + sys.argv[1:], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

def nonblocking(fd):
  fl = fcntl.fcntl(fd, fcntl.F_GETFL)
  fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)

nonblocking(proc.stdin)
nonblocking(proc.stdout)
nonblocking(proc.stderr)

nonblocking(sys.__stdin__)
nonblocking(sys.__stdout__)
nonblocking(sys.__stderr__)

def me_to_proc():
  x_to_y(sys.__stdin__, proc.stdin, "~in> ")

def proc_to_me():
  x_to_y(proc.stdout, sys.__stdout__, "<out~ ")

def proc_to_me_err():
  x_to_y(proc.stderr, sys.__stderr__, "<err~ ")

def x_to_y(x, y, prefix=""):
  try:
    while True:
       line = x.readline()
       to_dump = "%s%s" % (prefix, line)
       print >> dump, to_dump
       print to_dump
       y.write(line)
       y.flush()
       dump.flush()
  except:
    pass

recode = None
while recode is None:
  proc_to_me()
  #proc_to_me_err()
  me_to_proc()

  retcode = proc.poll()

exit(retcode)

just replace your original binary with this script, and change <real app> to create the actual process. In- and out-put information will be written on screen and dumpted into /tmp/dump.

(I'm not sure however about the termination criteria, I didn't check that in details)



来源:https://stackoverflow.com/questions/10794894/how-to-intercept-transparently-stdin-out-err

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!