Search code examples
python-3.xpexpect

How do I collect ALL output from pexpect.spawn()


Currently the c.read() function only returns values from output of the program that I am calling and does not collect what happens during the interactive portion. (In other words: Waiting for user name and password). Since errors can happen during that section I need to collect all output after the cmd is called.

Using the run command works fine. I am trying to convert to using expect.spawn() since that looks very close to the ssh version of expect. Current the run() method works fine in that it works locally. I can't find getOutput() type function that would return the entire output.

import pexpect
class Expect( ):
    def Do( self, cmd, program: list = [], timeout: int = 20 ):
        # result = run( cmd, events=program, timeout=timeout).decode() 

        result = ''
        c = pexpect.spawn( cmd, encoding='utf-8', timeout=timeout )
        for curItem in program:
            # print( 'wait: ' + curItem[0] + ' resp: ' + curItem[1])
            c.expect( curItem[0] )
            c.sendline( curItem[1] )
            # result += c.read()
    
        result += c.read()

        print ( 'res: ' + str(result) )
        return result


cmd  = 'fmsadmin list files -s'
prog = [('username \\(.+\\):', 'yourUN\n'), ('password:', 'yourPW\n')]
res  = Expect().Do( cmd, prog )    # Returns everything
# use results to verify functionality

Results

ID File                  Clients Size   Status Enabled Extended Privileges                                                                   Encrypted 
1  FMServer_Sample.fmp12 0       905216 Normal fmapp fmxml fmphp fmwebdirect                                                                 No        

When I expect

username (yourUN):yourUN
password:
ID File                  Clients Size   Status Enabled Extended Privileges                                                                   Encrypted 
1  FMServer_Sample.fmp12 0       905216 Normal fmapp fmxml fmphp fmwebdirect                                                                 No        

Update

I added result += c.before + c.after just under c.sendline( curItem[1] ) which now returns: c.sendline( curItem[1] )

username (yourUN): yourUN

password:
ID File                  Clients Size   Status Enabled Extended Privileges                                                                   Encrypted 
1  FMServer_Sample.fmp12 0       905216 Normal fmapp fmxml fmphp fmwebdirect                                                                 No        

Unfortunately there are still two issues. 1 there are additional values such as returns in the output and if I provide an invalid password I get debugging details back without the apps returned values. I get:

before (last 100 chars): '\r\n/usr/local/bin/fmsadmin: Permission denied, please try again.\r\nusername (yourUN):'
after: <class 'pexpect.exceptions.TIMEOUT'>
match: None
match_index: None
etc

So in short, I'm looking for the full returned values from the CLI exactly as it would be returned if I did it by hand. Which is what run() does.


Solution

  • OK, I figured it out. Not quite expected but it makes sense. Several changes:

    1. Add result += c.before + c.after at end of for loop. This captures output from from the account and password and any error from the CLI application.
    2. Put the spawn, expect, send inside a try accept block
    3. Move result += c.read() inside Try before Except (quiet the exception)
    4. Finally call result += c.buffer during the exception.

    I'll probably remove the local section in next version (once I figure out remote case)

    from pexpect import *
    
    class Expect( ):
        def Do( self, cmd, program: list = [], timeout: int = 20 ):
            local   = False
            result  = ''
            if local:
                result = runu( cmd, events=program, timeout=timeout ) 
            else:
                c = ''
                try:
                    c = spawnu( cmd, timeout=timeout )
                    for curItem in program:
                        c.expect( curItem[0] )
                        c.sendline( curItem[1] )
                        result += c.before + c.after
                    result += c.read()
                except:
                    result += c.buffer
            return result
    
    cmd  = 'fmsadmin list files -s'
    prog = [('username \\(.+\\):', 'yourUN\n'), ('password:', 'yourPW\n')]
    res  = Expect().Do( cmd, prog, 5 )
    print ( '-' * 50 + '\n' + str(res))