os.system() or os.execv() w/stdout redirect and PID tracking

Michael P. Reilly arcege at shore.net
Thu May 6 14:20:51 EDT 1999


Michael Olivier <dmtech at iname.com> wrote:
: Hi folks,

: What's the best way from a python program to start another program in
: the background and:
: - redirect stdout/stderr to a file
: - get the process ID of the started program.

If you put it in the background, then I guess you don't want the PID
(backgrounding the process means that you are ignoring when it completes).
I will assume that you want to be notified when it completes and continue
processing.

: It seems that:

: - os.system() call can start a program in the background, but doesn't
: return the pid (process ID) of the started program -- returns 0

:     os.system("find / -name mic* -print > /tmp/find-cmd 2>&1 &")

:     This does what I want but don't get the PID back for it

This calls "/bin/sh -c 'find / -name mic* -print > /tmp/find-cmd 2>&1 &'"
Which spawns two processes, the `find' and the '/bin/sh', of which only
the '/bin/sh' would be retrievable.  Besides which, you are placing the
find in the background.

: - if I call os.fork() and then os.execv(), the pid is trackable,
: but I don't know a way to redirect stdout/stderr

You must replace fd1 and fd2 (stdout and stderr respectively) with the
output file using the POSIX functions in the os module.


For much of what you seem to want, you could use the Popen3 class in
the popen2 module.

  import popen2
  # this has the same semantics as os.system(), which calls /bin/sh and
  # attempts to evaluate the wildcard
  finder = popen2.Popen3("find / -name 'mic*' -print >/tmp/find-cmd 2>&1")
  pid = finder.pid
  while finder.poll() < -1:
    do_something_else_for_a_short_time()
  return_code = finder.sts  # status value from wait

or
  # it might be good to put the read/write loop in a thread
  import popen2
  finder = popen2.Popen3("find / -name 'mic*' -print")
  file = open('/tmp/find-cmd', 'w')
  # if you didn't want the pid, you could use os.popen()
  pid = finder.pid
  line = finger.fromchild.readline()
  while line:
    file.write(line)
    line = finder.fromchild.readline()
  file.close()
  return_code = finder.wait()

Or the old-fashioned method:
  import os, signal

  pid = os.fork()
  return_code = None
  if pid == 0:
    outfile = os.open('/tmp/find-cmd', os.WRONLY)
    # redirect stdout and stderr to '/tmp/find-cmd'
    os.close(1)
    os.dup(outfile)
    os.close(2)
    os.dup(outfile)
    os.close(outfile)
    # note that this does NOT evaluate the wildcard
    os.execlp('find', 'find', '/', '-name', 'mic*' -print')
    os._exit(255)
  def process_completion(signum, frame, pid=pid):
    global return_code
    wpid, rc = os.waitpid(pid, os.WNOHANG)
    if wpid == pid:
      return_code = rc
  signal.signal(signal.SIGCHLD, process_completion)
  do_something_else_for_a_short_time()

Using the raw POSIX calls is the most flexible, but not the easiest or
most portable; the functions in popen2 are generally better to use.
But then it all depends on what you want to do.

  -Arcege





More information about the Python-list mailing list