0

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?

Eryk Sun
  • 33,190
  • 5
  • 92
  • 111
Voljega
  • 156
  • 1
  • 13
  • You shouldn't need to use the `Start` command which, without it's `/wait` option, is the main issue. I also find it strange run `cmd.exe` with the script as an argument when the script should automatically run inside its default interpreter anyhow. – Compo Oct 09 '17 at 11:24
  • Try using full absolute paths to the batch files and put quotes around, and remove `start`: `'cmd /C "D:\Scripts\Install.bat"'`, for instance... – aschipfl Oct 09 '17 at 11:25
  • @Compo well I tried to do that but I don't know to – Voljega Oct 09 '17 at 11:29
  • @aschipfl If the bat file is not run from inside it's directory it doesn't work because it is likely using relative paths – Voljega Oct 09 '17 at 11:30
  • With "subprocess.call("cmd /C Install.bat", cwd=os.path.join(gamesDosDir,game), shell=True)" the command blocks but I can't see the cmd window and thus enter the input it's waiting for and it blocks forever – Voljega Oct 09 '17 at 11:33
  • You do not need `shell=True` to run a batch file or console-based executable. – Compo Oct 09 '17 at 11:37
  • @Compo If I remove Shell=True or change it to Shell=False in my command I get an error "FileNotFoundError: [WinError 2] Le fichier spécifié est introuvable" (file is not to be found). – Voljega Oct 09 '17 at 11:43
  • I'm executing the program directly through Spyder 3 is this can be an explanation – Voljega Oct 09 '17 at 11:43
  • What about: `cmd /C (echo Y&echo Y) | Install.bat`? – aschipfl Oct 09 '17 at 11:52
  • Have you tried running it without the `cmd /c` as I commented originally, `subprocess.call("Install.bat", args…)` – Compo Oct 09 '17 at 11:52
  • @Voljega Most likely the batch file is bad coded and expects that the current directory is the directory of `install.bat`. Edit the batch file and insert at top `cd /D "%~dp0"` to first set current directory to directory of the batch file. Even better for working also with batch file stored on a network share loaded with UNC path is to use at top of the batch file `pushd "%~dp0"` and use somewhere at bottom before exiting batch file `popd`. – Mofi Oct 09 '17 at 11:54
  • @Mofi the batch files come as-is, editing them is not possible as they are thousands of them – Voljega Oct 09 '17 at 11:59
  • Have you also tried, moving this to an earlier line, `cwd = os.path.join(gamesDosDir, game)` then using `subprocess.call("Install.bat", cwd)`? – Compo Oct 09 '17 at 12:05
  • Do not depend on the working directory to find the batch script. Checking the working directory is a configurable behavior, and secured systems might have it disabled. Use the fully-qualified path to the batch script, which is apparently `script_path = os.path.join(gamesDosDir, game, 'Install.bat')`. *Do not use* `shell=True` since that will hide the console window if one is created. Run the batch script directly: `subprocess.call(script_path)`. If the batch script is badly written, you may need the `cwd` option as well. – Eryk Sun Oct 09 '17 at 12:42
  • @aschifpl > `subprocess.call("cmd /C (echo Y&echo Y) | Install.bat", cwd=os.path.join(gamesDosDir,game), shell=False)` seems to work, well except I now realize it woud be better without entering the letter automatically `subprocess.call("cmd /C Install.bat", cwd=os.path.join(gamesDosDir,game), shell=False)` works also, congrats, do you want to propose the solution ? – Voljega Oct 09 '17 at 12:47
  • Thank you to everyone :) – Voljega Oct 09 '17 at 12:48
  • You never need `cmd /c` to run a batch script. That's redundant. – Eryk Sun Oct 09 '17 at 12:50
  • Use `subprocess.run` or `subprocess.Popen` and call `communicate` to write to stdin. – Eryk Sun Oct 09 '17 at 12:52

1 Answers1

0

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)
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • Since they're using double quotes in their arguments to the `Start` command, they'd need to at least use a blank title, `Start "" /W`. – Compo Oct 09 '17 at 13:20
  • @Compo: Of course they can. If they don't, they will just get the default title `C:\Windows\system32\cmd.exe`... – Serge Ballesta Oct 09 '17 at 13:42
  • well since @aschifpl gave the good solution before you but doesn't want to enter a response apparently I give you the green check – Voljega Oct 10 '17 at 16:48
  • You're running `'cmd /c "start cmd /c install.bat"'`, which creates an unnecessary cmd.exe process and two consoles, with one console hidden as the default behavior for `shell=True`. Instead, run the batch file directly using its fully-qualified path and without `shell=True`. If you're already attached to a console and want a new one, use `creationflags=CREATE_NEW_CONSOLE`. – Eryk Sun Oct 10 '17 at 21:59
  • @eryksun: Thank you! I had never used creationflags before... I have edited my answer because your solution is much cleaner. – Serge Ballesta Oct 11 '17 at 07:11