12

I'm trying to write a cross-platform tool that runs specific commands, expects certain output for verification, and sends certain output (like username/password) for authentication.

On Unix, I have been successful in programming a Python tool that uses the pexpect library (via pip install pexpect). This code works perfectly and is exactly what I am trying to do. I've provided a small excerpt of my code for proof-of-concept below:

self.process = pexpect.spawn('/usr/bin/ctf', env={'HOME':expanduser('~')}, timeout=5)
self.process.expect(self.PROMPT)
self.process.sendline('connect to %s' % server)
sw = self.process.expect(['ERROR', 'Username:', 'Connected to (.*) as (.*)'])
if sw == 0:
    pass
elif sw == 1:
    asked_for_pw = self.process.expect([pexpect.TIMEOUT, 'Password:'])
    if not asked_for_pw:
        self.process.sendline(user)
        self.process.expect('Password:')
    self.process.sendline(passwd)
    success = self.process.expect(['Password:', self.PROMPT])
    if not success:
        self.process.close()
        raise CTFError('Invalid password')
elif sw == 2:
    self.server = self.process.match.groups()[0]
    self.user = self.process.match.groups()[1].strip()
else:
    info('Could not match any strings, trying to get server and user')
    self.server = self.process.match.groups()[0]
    self.user = self.process.match.groups()[1].strip()
info('Connected to %s as %s' % (self.server, self.user))

I tried running the same source on Windows (changing /usr/bin/ctf to c:/ctf.exe) and I receive an error message:

Traceback (most recent call last):
  File ".git/hooks/commit-msg", line 49, in <module> with pyctf.CTFClient() as c:
  File "C:\git-hooktest\.git\hooks\pyctf.py", line 49, in __init__
    self.process = pexpect.spawn('c:/ctf.exe', env={'HOME':expanduser('~')}, timeout=5)
  AttributeError: 'module' object has no attribute 'spawn'

According to the pexpect documentation:

pexpect.spawn and pexpect.run() are not available on Windows, as they rely on Unix pseudoterminals (ptys). Cross platform code must not use these.

That led me on my search for a Windows equivalent. I have tried the popular winpexpect project here and even a more recent (forked) version here, but neither of these projects seem to work. I use the method:

self.process = winpexpect.winspawn('c:/ctf.exe', env={'HOME':expanduser('~')}, timeout=5)

only to sit and watch the Command Prompt do nothing (it seems as though it's trapped inside the winspawn method). I was wondering what other means I could go about programming a Python script to interact with the command line to achieve the same effect as I have been able to in Unix? If a suitable working Windows-version pexpect script does not exist, what other means could I use to go about this?

Dylan Wheeler
  • 6,928
  • 14
  • 56
  • 80
  • I suspect this is the password prompt which causes your problems on Windows. I had a lot of trouble sending password to a `PLINK` (ssh) command, and finally gave it up. I'd personally use `subprocess.Popen` with line scanning (manual code) to do that. Can't you pass user/password to your command at least? – Jean-François Fabre Aug 16 '16 at 14:00
  • winpexpect uses pipes for the standard handles. In this case most command-line programs switch to fully buffering stdout, so you'll only see output when the buffer fills up and flushes. Typically the buffer is 4 KiB. – Eryk Sun Aug 17 '16 at 07:12
  • If you're attached to the same console as the child process, then you can use the Windows console API to directly write to its input buffer and read from the screen buffer. You can even create a new, empty screen buffer that can either be temporarily set as the active screen buffer or referenced by a file descriptor and passed as `stdout` to `subprocess.Popen`. – Eryk Sun Aug 17 '16 at 07:16

3 Answers3

5

You can use wexpect ("Windows alternative of pexpect", Python Software Foundation). It has the same functions, and it works on Windows .

ejderuby
  • 710
  • 5
  • 21
  • There are actually two windows versions to pexpect - both are not (well) maintained or developed anymore - [winpexpect](https://pypi.org/project/winpexpect/) and [wexpect](https://pypi.org/project/wexpect/). neither of them worked for me (win 10, python 3.7.3). nor did the latest version of pexpect, which claims to have [minor support for windows](https://pexpect.readthedocs.io/en/stable/overview.html#pexpect-on-windows). – DarkLight Sep 16 '19 at 12:01
  • 2
    Contrary to the 4.8 pexpect docs, wexpect is alive and kicking as of August 2020. – Leo Aug 02 '20 at 13:22
  • @Leo Not really, with wexpect every time I run `child.expect('anything')` I get `wexpect.wexpect_util.EOF: broken pipe, bye bye` – IDK Jul 04 '21 at 15:16
  • I can confirm that wexpect worked almost exactly on windows as pexpect did on linux. I had to port a linux/os x app to windows and found wexpect. The only minor difference is if doing `child = wexpect.spawn(COMMAND)` there is no `child.logfile = fd` attribute and so the workaround was to `fd.write(str(child.after))` to get a logfile of the output and input. – bubonic Jan 11 '23 at 06:23
1

Better solution is to move DOCKER. It will solve the entire Linux <-> Windows Dependecies in all kind of process. Very simple steps are there to convert a any .py code into a Docker-image. This will be the Futreistic an doptimised solution to use pexpect in windows.

The Example for Encapsulating Python in container: In Linux : Step 1: https://docs.docker.com/engine/install/centos/ After the installation ,one can get docker as a sytem command in linux. Step 2 : Create a directory , and copy the python in that directory and create a ne file called "Dockerfile" and also have the requirements.txt

root@host_name~#cd Demo_docker Demo_code.py DOckerfile requirements.txt

vi Demo_code.py
import pexpect
a=2+2
print(a)

vi requirements.txt pexpect==4.8.0

vi Dockerfile python:latest COPY . . COPY . reqitrements.txt RUN pip3 install -- requirement requirements.txt CMD ["python","Demo.py"]

Step 3: Use Docker build command to build and run and you can upload this to "dockerHUB" and then pull to any where in the world.

After you pushed the image to Docker-Hub , in windows one need to download "Docker Desktop Appliaction" and the use "Docker pull command to pull the repo which you pushed to Docker-hub

  • Could you provide an example of DockerFile or docker-compose.yml showing how someone can encapsulate a Python script in docker? Also could you mention what are required for docker to be able to run Linux container in Windows to provide a better, more complete answer to the question ? – Sylvaus Nov 22 '20 at 21:02
  • The Example for Encapsulating Python in container: In Linux : Step 1: https://docs.docker.com/engine/install/centos/ After the installation ,one can get docker as a sytem command in linux. Step 2 : Create a directory , and copy the python in that directory and create a ne file called "Dockerfile" and also have the requirements.txt – katheravan arumugam Nov 24 '20 at 01:54
0

Instead of using pexpect.spawn you can use pexpect.popen_spawn.PopenSpawn for windows.

child = pexpect.popen_spawn.PopenSpawn('cmd', timeout=1)
child.send('ipconfig')
child.expect('Wireless', timeout=None)
Alan Kavanagh
  • 9,425
  • 7
  • 41
  • 65
  • 2
    Please note that PopenSpawn [is not](https://pexpect.readthedocs.io/en/stable/overview.html#pexpect-on-windows) a direct replacement for spawn, and this solution may not work with interactive terminal programs. – Mario Román Sep 04 '18 at 19:51
  • 2
    I had to do an extra import to get this working. See https://github.com/pexpect/pexpect/issues/328 – ChrisCantrell Sep 19 '18 at 21:06
  • no, it does not work. getting the same issue as in https://stackoverflow.com/questions/47582707/pexpect-telnet-on-windows – etwas77 May 21 '19 at 10:59
  • @etwas77 Telnet will not work with PopenSpawn in windows. You have to use plink.exe as mentioned in answer of https://stackoverflow.com/questions/47582707/pexpect-telnet-on-windows – Hemang Bhimani May 22 '19 at 09:47
  • i actually tried ftp or ssh, same issue, no example of any pexpect tutorial work for me.. – etwas77 May 22 '19 at 09:56
  • @etwas77 I have observed this error when not using the timeout flag with PopenSpawn or expect. Try with timeout=None. – Hemang Bhimani May 25 '19 at 07:44