5

I'm new to python and would like to open a windows cmd prompt, start a process, leave the process running and then issue commands to the same running process.

The commands will change so i cant just include these commands in the cmdline variable below. Also, the process takes 10-15 seconds to start so i dont want to waste time waiting for the process to start and run commands each time. just want to start process once. and run quick commands as needed in the same process

I was hoping to use subprocess.Popen to make this work, though i am open to better methods. Note that my process to run is not cmd, but im just using this as example

import subprocess
cmdline = ['cmd', '/k']
cmd = subprocess.Popen(cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE)

cmd.stdin.write("echo hi")       #would like this to be written to the cmd prompt
print cmd.stdout.readline()      #would like to see 'hi' readback

cmd.stdin.write("echo hi again") #would like this to be written to the cmd prompt
print cmd.stdout.readline()      #would like to see 'hi again' readback

The results arent what i expect. Seems as though the stdin.write commands arent actually getting in and the readline freezes up with nothing to read.

I have tried the popen.communicate() instead of write/readline, but it kills the process. I have tried setting bufsize in the Popen line, but that didn't make too much difference

Evan Carroll
  • 78,363
  • 46
  • 261
  • 468
pyNewbie
  • 155
  • 1
  • 2
  • 10
  • So ive made some headway, but still not getting exactly what i want. – pyNewbie May 09 '15 at 04:30
  • So ive made some headway... make it cmd.stdin.write("...\n") with the newline gets the input in and lets the process handle multiple commands in. Now my problem is automating the actual steps needed. To run manually, i run a .bat file with opens up a cygwin terminal (with a $ prompt). From that, i call a program that is more or less a tcl shell (with a % prompt). What i need to do now is somehow get the cmd.stdin,write to mimic me typing right at the tcl % prompt. thats where im at now... any ideas? – pyNewbie May 09 '15 at 04:45
  • don't comment on your own question, update it or ask a new one instead (your question should be answerable without reading any comments). – jfs May 09 '15 at 14:28
  • I don't think it takes 10-15 seconds to start a new cmd on a modern computer. If you actual command is not `cmd`; say so. The buffering behavior depends greatly on a particular subprocess (how it interacts with its stdin/stdout or whether it uses them at all e.g., a subprocess could write directly to Windows console using `WriteConsoleW` (though if the output is redirected; it shouldn't). – jfs May 09 '15 at 14:33
  • ok, heres what im trying to do.... I have some some code running on an Altera FPGA that talks to my DUT. I need to offload a big chunk of data from the FPGA after its finished capturing from my DUT. Ive found a nice function within Altera "system console" that allows me to quickly grab this big chunk and write to a binary file for post processing. So this Altera "System console" runs tcl scripts. its got a % prompt. Im trying to use popen or pexpect to get to that % prompt. – pyNewbie May 11 '15 at 05:46
  • I can use SC= subprocess.Popen('C:/altera/15.0/quartus/sopc_builder/bin/system-console.exe -cli -script=C:/test/capture.tcl',stdin=subprocess.PIPE,stdout=subprocess.PIPE) just fine but it takes too long. What i would like to do is call the popen(...system-console.exe -cli') once and then whenever i need to dump the data, send a SC.stdin.write('script=capture.tcl\n') and get my data. the tcl script itself is fast. – pyNewbie May 11 '15 at 05:51
  • But sending that tcl script with a separate SC.stdin.write is taken, but ignored. If i were to type the command right at the % prompt, it works perfectly and fast. without the overhead of starting system console. Does that make sense? – pyNewbie May 11 '15 at 05:55
  • As I said: don't put it in the comments, add it to the question (with proper formatting). – jfs May 11 '15 at 10:05

3 Answers3

2

Your comments suggest that you are confusing command-line arguments with input via stdin. Namely, the fact that system-console.exe program accepts script=filename parameter does not imply that you can send it the same string as a command via stdin e.g., python executable accepts -c "print(1)" command-line arguments but it is a SyntaxError if you pass it as a command to Python shell.

Therefore, the first step is to use the correct syntax. Suppose the system-console.exe accepts a filename by itself:

#!/usr/bin/env python3
import time
from subprocess import Popen, PIPE

with Popen(r'C:\full\path\to\system-console.exe -cli -', 
           stdin=PIPE, bufsize=1, universal_newlines=True) as shell:
    for _ in range(10):
        print('capture.tcl', file=shell.stdin, flush=True)
        time.sleep(5)

Note: if you've redirected more than one stream e.g., stdin, stdout then you should read/write both streams concurrently (e.g., using multiple threads) otherwise it is very easy to deadlock your program.

Related:

The second and the following steps might have to deal with buffering issues on the side of the child process (out of your hands on Windows), whether system-console allows to redirect its stdin/stdout or whether it works with a console directly, and character encoding issues (how various commands in the pipeline encode text).

Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • ok, i tried your example. im using python 2.7.9, so had to use from __future__ import print_function to get it to work. but i get the same sort of issue. command is taken, but nothing happens. You being up a good point about the system console program itself. it may very well be issue. how would i go about verifying stdin redirecting and whether it works with a console directly? thanks – pyNewbie May 11 '15 at 19:39
  • @pyNewbie: you should use `shell.stdin.flush()` on Python 2 (notice: `flush=True` above). Also, `stdout` is not redirected (you should see the output in Windows console). To check whether it redirects: 1. Redirect 2. Get the result (e.g., using `output, err = p.communicate(input)`) -- if you still see the output or it hangs waiting for input then the redirect has failed. – jfs May 11 '15 at 20:15
  • @ J.F. Sebastian: I added in the stdin.flush(). no difference in execution. In regards to the redirection, i dont get any output to the screen. If i use neither the stdin=PIPE nor the stdout=PIPE, then i do get output to the screen. I tried using only the stdin=PIPE (no stdout=PIPE) a couple days ago thinking that it would allow stdin input and still output to screen. But that has never worked. – pyNewbie May 11 '15 at 22:59
  • 1
    so i figured out that system console needed an extra argument passed in to put it in a certain mode (--disable_readline) to run the capture scripts. So after all that, the python code was working as intended (after the \n chars) Thanks for all your help guys!! – pyNewbie May 12 '15 at 04:52
  • So apparently you accidentally solved my different problem of not using `universal_newlines`. Thanks! – Robert Moore Jun 09 '17 at 21:26
1

Here is some code that I tested and is working on Windows 10, Quartus Prime 15.1 and Python 3.5

import subprocess

class altera_system_console:
    def __init__(self):
        sc_path = r'C:\altera_lite\15.1\quartus\sopc_builder\bin\system-console.exe --cli --disable_readline'
        self.console = subprocess.Popen(sc_path, stdin=subprocess.PIPE, stdout=subprocess.PIPE)

    def read_output(self):
        rtn = ""
        loop = True
        i = 0
        match = '% '
        while loop:
            out = self.console.stdout.read1(1)
            if bytes(match[i],'utf-8') == out:
                i = i+1
                if i==len(match):
                    loop=False
            else:
                rtn = rtn + out.decode('utf-8')
        return rtn

    def cmd(self,cmd_string):
        self.console.stdin.write(bytes(cmd_string+'\n','utf-8'))
        self.console.stdin.flush()

c = altera_system_console()
print(c.read_output())
c.cmd('set jtag_master [lindex [get_service_paths master] 0]')
print(c.read_output())
c.cmd('open_service master $jtag_master')
print(c.read_output())
c.cmd('master_write_8 $jtag_master 0x00 0xFF')
print(c.read_output())
Luke Beno
  • 11
  • 1
0

You need to use iter if you want to see the output in real time:

import subprocess
cmdline = ['cmd', '/k']
cmd = subprocess.Popen(cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE)


cmd.stdin.write("echo hi\n")#would like this to be written to the cmd prompt
for line in iter(cmd.stdout.readline,""):
    print line
    cmd.stdin.write("echo hi again\n")#would like this to be written to the cmd prompt

Not sure exactly what you are trying to do but if you want to input certain data when you get certain output then I would recommend using pexpect

Padraic Cunningham
  • 176,452
  • 29
  • 245
  • 321
  • Thanks for replying. I tried your code, but got similar hang ups. Im actually trying to call an Altera tool. Pexpect looks cool, but im on windows. i found a windows hack of it, but am finding issues. – pyNewbie May 09 '15 at 00:05
  • i tried http://techi2teach.blogspot.com/2011/01/how-to-get-python-pexpect-module-on.html but got similar errors that YingWang did. read later that this install was verified on XP. im running Win7. If you know how to install pexpect with cygwin on Win7, im game for using it. it looks very nice, but as of now, im not sure it will be viable because of windows. Any ideas why i cant get subprocess to read in multiple stdin writes? I dont care about the readbacks as much – pyNewbie May 09 '15 at 01:43
  • @pyNewbie. so you installed cygwin successfully or not? Also are you opening multiple shells? – Padraic Cunningham May 09 '15 at 10:00
  • yes, cygwin is installed, working ok. its just a matter of getting pexpect to work with win7. sounds like a great tool, but i run into issues because i dont have termios and i dont have something called resources. Ill look again for pexpect install help on win 7. I am only opening 1 shell. but i am calling process A, then process B and wanting to send commands to process B. I need to simulate typing at this % prompt. Its Altera System Console, in case anyone has specific experience thanks – pyNewbie May 09 '15 at 13:49
  • @pyNewbie. https://gist.github.com/anthonyeden/8488763 http://mediarealm.com.au/articles/2014/01/python-pexpect-windows-wexpect/ – Padraic Cunningham May 09 '15 at 14:35
  • i did get winpexpect working (at least functioning) enough to verify it can do what popen can do. im still having issues getting to my ultimate goal. see comments above. any ideas? Thanks!!! – pyNewbie May 11 '15 at 05:59
  • also, im trying this with pexpect, and the it just hangs after the sendline: from winpexpect import winspawn SC=winspawn('C:/altera/15.0/quartus/sopc_builder/bin/system-console.exe -cli') SC.expect('%') SC.sendline('source C:/test/capture.tcl'). it hangs, so at least its not being ignored. any ideas? thanks! – pyNewbie May 11 '15 at 06:25