'Execute bash script from Python on Windows

I am trying to write a python script that will execute a bash script I have on my Windows machine. Up until now I have been using the Cygwin terminal so executing the bash script RunModels.scr has been as easy as ./RunModels.scr. Now I want to be able to utilize subprocess of Python, but because Windows doesn't have the built in functionality to handle bash I'm not sure what to do.

I am trying to emulate ./RunModels.scr < validationInput > validationOutput

I originally wrote this:

os.chdir(atm)
vin = open("validationInput", 'r')
vout = open("validationOutput", 'w')
subprocess.call(['./RunModels.scr'], stdin=vin, stdout=vout, shell=True)
vin.close()
vout.close()
os.chdir(home)

But after spending a while trying to figure out why my access was denied, I realized my issue wasn't the file permissions but the fact that I was trying to execute a bash file on Windows in general. Can someone please explain how to execute a bash script with directed input/output on windows using a python script?

Edit (Follow up Question):

Thanks for the responses, I needed the full path to my bash.exe as the first param. Now however, command line calls from within RunModels.scr come back in the python output as command not found. For example, ls, cp, make. Any suggestions for this?

Follow up #2: I updated my call to this:

subprocess.call(['C:\\cygwin64\\bin\\bash.exe', '-l', 'RunModels.scr'], stdin=vin, stdout=vout, cwd='C:\\path\\dir_where_RunModels\\')

The error I now get is /usr/bin/bash: RunModels.scr: No such file or directory. Using cwd does not seem to have any effect on this error, either way the subprocess is looking in /usr/bin/bash for RunModels.scr.

SELF-ANSWERED I needed to specify the path to RunModels.scr in the call as well as using cwd.

subprocess.call(['C:\\cygwin64\\bin\\bash.exe', '-l', 'C:\\path\\dir_where_RunModels\\RunModels.scr'], stdin=vin, stdout=vout, cwd='C:\\path\\dir_where_RunModels\\')

But another problem...

Regardless of specifying cwd, the commands executed by RunModels.scr are throwing errors as if RunModels.scr is in the wrong directory. The script executes, but cp and cd throw the error no such file or directory. If I navigate to where RunModels.scr is through the command line and execute it the old fashioned way I don't get these errors.



Solution 1:[1]

Python 3.4 and below

Just put bash.exe in first place in your list of subprocess.call arguments. You can remove shell=True, that's not necessary in this case.

subprocess.call(['C:\\cygwin64\\bin\\bash.exe', '-l', 'RunModels.scr'], 
                 stdin=vin, stdout=vout,
                 cwd='C:\\path\\dir_where_RunModels\\')

Depending on how bash is installed (is it in the PATH or not), you might have to use the full path to the bash executable.

Python 3.5 and above

subprocess.call() has been effectively replaced by subprocess.run().

subprocess.run(['C:\\cygwin64\\bin\\bash.exe', '-l', 'RunModels.scr'], 
                 stdin=vin, stdout=vout,
                 cwd='C:\\path\\dir_where_RunModels\\')

Edit:

With regard to the second question, you might need to add the -l option to the shell invocation to make sure it reads all the restart command files like /etc/profile. I presume these files contain settings for the $PATH in bash.

Edit 2:

Add something like pwd to the beginning of RunModels.scr so you can verify that you are really in the right directory. Check that there is no cd command in the rc-files!

Edit 3:

The error /usr/bin/bash: RunModels.scr: No such file or directory can also be generated if bash cannot find one of the commands that are called in the script. Try adding the -v option to bash to see if that gives more info.

Solution 2:[2]

A better solution than the accepted answer is to use the executable keyword argument to specify the path to your shell. Behind the curtain, Python does something like

exec([executable, '-c`, subprocess_arg_string])

So, concretely, in this case,

subprocess.call(
    './RunModels.scr',
    stdin=vin, stdout=vout,
    shell=True,
    executable="C:/cygwin64/bin/bash.exe")

(Windows thankfully lets you use forward slashes instead of backslashes, so you can avoid the requirement to double the backslashes or use a raw string.)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 GaidinD
Solution 2 tripleee