Call to several batch files through CMD doesn't block - python

I'm trying to call several install.bat files one after another with Python trough CMD.
It is necessary that each bat file be displayed in an interactive console window because it asks for some users instructions and that the python program only resume after each CMD process is resolved
Each install.bat file can take a pretty long time to finish its process.
My code is the following :
for game in games :
print("----------- Starting conversion for %s -----------" %game)
subprocess.call("start cmd /C " + "Install.bat", cwd=os.path.join(gamesDosDir,game), shell=True)
print("end")
But the console windows inside the shell are launched all at once and the "end" message appears event before any of them is finished, whereas I would like them appearing one by one and not go to the n+1 one until the n one is finished and the console window closed (either by user or automatically /K or /C then).
I understand this is some problems using CMD as call should be blocking. How to resolve that? Additionally, if possible how to keep it exactly the same and add 'Y' and 'Y' as default user input?

The most common way to start a batch file (or more generally a CLI command) if to pass it as an argument to cmd /c. After you comment I can assume that you need to use start to force the creation of a (new) command window.
In that case the correct way is to add the /wait option to the start command: it will force the start command to wait the end of its subprocess:
subprocess.call("start /W cmd /C " + "Install.bat", cwd=os.path.join(gamesDosDir,game),
shell=True)
But #eryksun proposed a far cleaner way. On Windows, .bat files can be executed without shell = True, and creationflags=CREATE_NEW_CONSOLE is enough to ensure a new console is created. So above line could simply become:
subprocess.call("Install.bat", cwd=os.path.join(gamesDosDir,game),
creationflags = subprocess.CREATE_NEW_CONSOLE)

Related

Interactively communicating with a FORTRAN shell program

DAOPHOT is a FORTRAN-written software for performing astronomy tasks in images. A typical flow of its usage is:
Open a terminal (gnome-terminal in my case) and run ./daophot. I'm now within DAOPHOT's shell.
Prompts the user for a command, let's say ATTACH to input an image file. DAOPHOT runs and prompts the user again for more commands.
User gives another command, let's say PHOTOMETRY. DAOPHOT runs and prompts the user again.
For every command the user gives, DAOPHOT runs and prompts again and again until exit is typed. For my case, I have three specific commands that will run one after another, without variation (ATTACH, PHOTOMETRY and PSF, with the latter maybe run more than once).
Right now I'm simply trying to ATTACH a file. What I have tried:
Using subprocess, as seen/asked here and here:
import subprocess
p = subprocess.Popen(["gnome-terminal","--disable-factory","--","./daophot"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.stdin.write(input("ATTACH file.fits"))
For this case, DAOPHOT's shell opens but the ATTACH command is not executed. I close the shell and the string "ATTACH file.fits" appears in the IPython terminal, ending the subprocess. I've tried also to use p.communicate(input=input("ATTACH file.fits")), but got the same result.
Using pexpect, as seen/asked here and here:
import pexpect
p = pexpect.spawn("gnome-terminal --disable factory -- ./daophot")
p.expect(pexpect.EOF)
p.sendline("ATTACH file.fits")
In this case, DAOPHOT's shell opens but the ATTACH command is not accounted for as an input.
Finally, a DAOPHOT wrapper already exists, but the idea is to have this automatically and interactive Python version in our lab, so that we can change later if needed.
From what I understand in terms of pipelines, ./daophot is a subsubprocess runnning inside gnome-terminal, so when I use e.g. p.stdin.write(input("ATTACH file.fits") I am actually inputing this command into gnome-terminal, and not into ./daophot.
Any help is much appreciated.

How can I echo Python loop processing info to the command line?

I am trying to set up a (iron)python script that shows some processing information to the command line. Something like:
list = ["a", "b", "c"]
for i in list:
cmd /k echo str(list.index(i)) #just echo index and leave window open so user can see progression
The challenge is to send multiple echo commands to the same instance of the command line.
Note: I can't just use print(i) in this case, because this ironpython script will be running inside a visual programming application, that does not allow printing to the command line. The goal of the script is that users can see progression during heavy operations.
I have come across multiple threads that suggest subprocess for this. I've seen and tested a lot of examples but I can get none of them to work. I could really use some help with that.
For example, when I do this...
import subprocess
proc=subprocess.Popen("echo hello world", shell=True, stdout=subprocess.PIPE)
.... then I can see that 'hello world' is being returned, so the echo was probably successful. But I never see a shell window pop up.
Edit 1
Some more test results: method 1 gives me a good result (command is executed and screen is left open for reading); method 2: adding stdout opens the shell and leaves the screen open but without a visible result - theres nothing to read; method 3: adding stdin flashes the screen (closes it too soon)
method 1:
process = Popen("cmd /k dir")
method2:
process = Popen("cmd /k dir",stdout=subprocess.PIPE)
method 3:
process = Popen('cmd /k dir',stdin=subprocess.PIPE)
Edit 2
Method 4 - creates two separate cmd windows, one showing a, one showing b. Not it.
process = Popen("cmd /k echo a")
process = Popen("cmd /k echo b")
Method 5 - adding process.stdin.write after stdin - just flashes
process = Popen('cmd /k echo a',stdin=subprocess.PIPE)
process.stdin.write("cmd /k echo b")
It seems so simple what I'm looking for but its giving me a headache...
What you are trying to do is not as simple as you think. Because the CMD window is a different process from your IronPython script, to do anything interactive (like showing the progress of a script), you need to use some form of inter-process communication.
For example, you could write a server script to run in the CMD window and then send messages to it from your IronPython script. But then you have to worry about how to start the server, how to stop it, and how to synchronise with the IronPython client script. You can see an example of this kind of inter-process communication in this StackOverflow answer.
Before trying something like that I would make sure that there's no way to display progress in your visual programming application, as staying inside the same process is likely to be easier.

subprocess window disappear quickly

In python, I use subprocess.Popen() to launch several processes, I want to debug those processes, but the windows of those processes disappeared quickly and I got no chance to see the error message. I would like to know whether there is any way I can stop the window from disappearing or write the contents in the windows to a file so that I can see the error message later.
Thanks in advance!
you can use the stdout and stderr arguments to write the outputs in a file.
example:
with open("log.txt", 'a') as log:
proc = subprocess.Popen(['cmd', 'args'], stdout=log, stderr=log)
In windows, the common way of keeping cmd windows opened after the end of a console process is to use cmd /k
Example : in a cmd window, typing start cmd /k echo foo
opens a new window (per start)
displays the output foo
leave the command window opened

Sequentially running multiple python programs "with sys" using batch

I am using Windows based PC.
I have multiple python programs with "sys". I mean the programs have these lines.
import sys
input_file = sys.argv[1]
output_file = sys.argv[2]
So I run these programs by running
python program1.py input1 output1
However, there are series of python programs so it will be convenient if I can run all these by double-clicking only one file.
How can I run them all sequentially, not simultaneously?
I tried things like
start C:\python27\python.exe program1.py input1 output1
start C:\python27\python.exe program2.py input2 output2
But it did not work..
Call is mainly for calling a batch file from within a batch file or running a block in current batch file as subroutine.
Start is the command to start any application as a separate process which means for a console applications to run it in a separate command prompt (console) window. A GUI application executed from within a batch file is always started as a separate process even if command start is not used.
Running an application results in halting the execution of the batch file until the application terminates itself for most applications. (There are applications with a different behavior caused by the application itself.) But a console or GUI application started using command start results in immediate continuation of batch file execution.
With using start /wait ... it is possible to start a console or GUI application as a separate process and halt execution of the batch file until the application terminates itself.
start /wait C:\python27\python.exe program1.py input1 output1
start /wait C:\python27\python.exe program2.py input2 output2
On running start /? in a command prompt window, the help for this command is printed into the output window.
["title"] means that optionally a title can be set for the new command prompt window (used only on starting a console application). I mention this here because command start can interpret any string in double quotes anywhere on the command line also as window title. Therefore if the application to start or one of its parameters must be enclosed in double quotes because of a space character or one of these characters &()[]{}^=;!'+,`~ in path or file name, it is better to explicitly specify a title string in double quotes immediately after command start as first parameter which can be even an empty string like "" (best for GUI applications).
start "Python Task 1" /wait "C:\python27\python.exe" program1.py input1 output1
start "Python Task 2" /wait "C:\python27\python.exe" program2.py input2 output2
import os
os.system('python' + ' ' + 'python_1.py')
os.system('python' + ' ' + 'python_2.py')
This solution is itself a python program. You can also construct the program names and have this in a loop. You can also add a parameter after the program name.

Closing console application after completion from Python

I have an exe file that I have to call with several parameters, and for this purpose I use a bat file. After I execute bat file command prompt does not close, but wait for me to press a key. Now I have to run this exe several times, and for this I want to run a script that will do it for me.
i = 0
for path in Paths
outout = codecs.open('runExe.bat', 'w')
output.write(PathToExe + " -param1" + " -param2 " + param2Val[0] + " -param3 " + param3Val[0] + " -param4 " + param4Val[0] + " -param5 param5Val")
output.close()
subprocess.call(["regsvr32.exe", path, "-u", "-s"])
subprocess.call(["regsvr32.exe", path, "-s"])
subprocess.call("runExe.bat")
i + = 1
where param3Val, param4Val, param5Val are lists with values for related command prompt parameters.
When I call this bat file, everything works perfectly for the first fun of exe, but after it executes, command promt waits for my respond. When I press any key, it closes and then exe file starts with different parameters.
So I want to eliminate with key-pressing thing. I tried to put "exit" to the end of the bat file, but it did not work. How can I close command prompt window from script, when exe finishes working?
Thanks in advance!
Upd1: sarmold's way of doing thing works fine, but I think this it is exe (console application) that is waiting for my response. Smth in exe file prevents console window from closing, but I do not have access to sources. How can I close it's window after it executes?
Upd2: I have tried to add "shell" call after subprocess.call, but this does not seem to work either, still have to respond to the console manually :(
shell = win32com.client.Dispatch("WScript.Shell")
shell.AppActivate("Command Prompt")
shell.SendKeys("cls(ENTER)")
There are two possible approaches here:
Make only a single bat file that contains all your commands, including the regsvr32.exe commands, and execute that.
Have the Python script do everything for you.
For the first approach, use the "a" open mode to append to the batch file. (Perhaps delete it at script start.) Write the contents of your three commands to the batch file within the loop -- so you wind up with a long batch file that includes all the commands you need.
Then call the subprocess.call() command once, outside the loop, at the end of the script, to run the entire thing.
For the second approach, remove all the batch-file writing and run your PathToExe using Python's subprocess.call(). It's almost as simple as deleting all lines that work with output, but change output.write() to subprocess.call() -- and obviously, fiddle with the contents a little bit so they work for subprocess.call() directly.
Are you running this in a way that launches a command prompt every time you run runExe.bat? It shouldn't necessarily wait for you to close the console, but since it does, try running your script with subprocess.call("cmd /C runExe.bat").
#Arnold is right, though: It's better to simplify your set-up and (imho) just let python handle everything.

Categories