Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python subprocess proc.stderr.read() introduce extra lines?

I want to run some command and grab whatever is output to stderr. I have two versions of function that does this version 1.

def Getstatusoutput(cmd):
    """Return (status, output) of executing cmd in a shell."""

    import sys
    mswindows = (sys.platform == "win32")

    import os
    if not mswindows:
        cmd = '{ ' + cmd + '; }'

    pipe = os.popen(cmd + ' 2>&1', 'r')
    text = pipe.read()
    sts = pipe.close()
    if sts is None: sts = 0
    if text[-1:] == '\n': text = text[:-1]
    return sts, text  

and version 2

def Getstatusoutput2(cmd):
    proc = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
    return_code = proc.wait()
    return return_code, proc.stdout.read(), proc.stderr.read()

The first version prints stderr output as I expect. The second version prints one blank line after every line. I suspect this is due to text[-1:] line in the version 1...but I can't seem to do something similar in second version. Can anybody explain what I need to do to make second function generate the same output as first one without extra lines in between (and at the very end) ?

Update: Here's how I am printing the output Here's how I am printing

      status, output, error = Getstatusoutput2(cmd)
      s, oldOutput = Getstatusoutput(cmd)
      print "oldOutput = <<%s>>" % (oldOutput)
      print "error = <<%s>>" % (error)
like image 577
user536012 Avatar asked Oct 10 '22 14:10

user536012


2 Answers

You can add .strip():

def Getstatusoutput2(cmd):
    proc = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
    return_code = proc.wait()
    return return_code, proc.stdout.read().strip(), proc.stderr.read().strip()

Python string Docs:

string.strip(s[, chars])

Return a copy of the string with leading and trailing characters removed. If chars is omitted or None, whitespace characters are removed. If given and not None, chars must be a string; the characters in the string will be stripped from the both ends of the string this method is called on.

string.whitespace

A string containing all characters that are considered whitespace. On most systems this includes the characters space, tab, linefeed, return, formfeed, and vertical tab.

like image 178
chown Avatar answered Oct 12 '22 11:10

chown


You could use subprocess.check_output([cmd], stderr=STDOUT) to capture all output.

To capture stdout, stderr separately you could use .communicate():

stdout, stderr = Popen([cmd], stdout=PIPE, stderr=PIPE).communicate()

To get all lines without a newline character at the end you could call stderr.splitlines().

To avoid printing additional newline if it is already present add ',' after the variable in a print statement:

print line,

Or if you use print() function:

print(line, end='')

Note

Your Getstatusoutput2() will block if the cmd produces enough output, use above solutions instead:

>>> len(Getstatusoutput2(['python', '-c',"""print "*"*2**6"""])[1])
65
>>> len(Getstatusoutput2(['python', '-c',"""print "*"*2**16"""])[1])

Popen.wait() documentation:

Wait for child process to terminate. Set and return returncode attribute.

Warning: This will deadlock when using stdout=PIPE and/or stderr=PIPE and the child process generates enough output to a pipe such that it blocks waiting for the OS pipe buffer to accept more data. Use communicate() to avoid that.

Related Use communicate() rather than stdin.write(), stdout.read() or stderr.read()

like image 43
jfs Avatar answered Oct 12 '22 10:10

jfs