3

I have a program that gets output from another program which runs on the new windows subsystem for linux. I have written a python program that runs from the windows system, but will execute the linux program using python subprocess module. If this is confusing see the example below.

However, when I do this I find that when called through python subprocess, windows cannot find bash program.

example on the commandline or powershell in windows:

C:\>bash -c "echo hello world!"
hello world!

C:\>python
Python 2.7.5 (default, May 15 2013, 22:43:36) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import subprocess as s
>>> s.call('bash -c "echo hello world"'.split())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "c:\Python27-32\lib\subprocess.py", line 524, in call
    return Popen(*popenargs, **kwargs).wait()
  File "c:\Python27-32\lib\subprocess.py", line 711, in __init__
    errread, errwrite)
  File "c:\Python27-32\lib\subprocess.py", line 948, in _execute_child
    startupinfo)
WindowsError: [Error 2] The system cannot find the file specified

>>> s.call('bash -c "echo hello world"'.split(),shell=True)
    'bash' is not recognized as an internal or external command,
    operable program or batch file.
    1

I thought maybe it wasn't loading my path settings somehow so I put in the full address of the bash program.

>>> s.call(b,shell=True)
'C:\Windows\System32\bash.exe' is not recognized as an internal or external command,
operable program or batch file.
1

EDIT : I realize my command might be giving a problem since o am splitting on spaces and the "echo hello world" is one argument, but trying the same thing with bash -c ls also gives the same error

jeffpkamp
  • 2,732
  • 2
  • 27
  • 51
  • @eryksun Thank you, I knew bash was 64 bit only, but didn't think it would result in that type of error if there was a compatibility issue. I will just run my program with the 64 bit version of python. Please put your put this as an answer so I can accept it! – jeffpkamp Oct 02 '16 at 15:26
  • 1
    Side-note: `s.call('bash -c "echo hello world"'.split())` is _wrong_. You just called the program as if `"echo`, `hello` and `world"` were passed as three separate arguments. If you absolutely must split a single string literal, use a function like `shlex.split` that understands quoting rules. – ShadowRanger Oct 22 '16 at 01:01

1 Answers1

8

For 32-bit programs running in the WOW64 subsystem, the "System32" directory gets redirected to "SysWOW64". The WSL bash.exe loader is distributed as a 64-bit executable, so from 32-bit Python you need to use the virtual "SysNative" directory. For example:

import os
import platform
import subprocess

is32bit = (platform.architecture()[0] == '32bit')
system32 = os.path.join(os.environ['SystemRoot'], 
                        'SysNative' if is32bit else 'System32')
bash = os.path.join(system32, 'bash.exe')

subprocess.check_call('"%s" -c "echo \'hello world\'"' % bash)

Note that currently Windows pipes aren't bridged to WSL pipes, so if you try to use stdout=PIPE or subprocess.check_output, the WSL bash loader will fail. You could read the console output directly via ReadConsoleOutputCharacter (e.g. see this answer). Or, more simply, you can redirect output to a temporary file, passing the temporary file's path translated as a WSL path. For example:

import tempfile

def wintolin(path):
    path = os.path.abspath(path)
    if path[1:2] == ':':
        drive = path[:1].lower()
        return '/mnt/' + drive + path[2:].replace('\\', '/')

cmd = '"%s" -c "echo \'hello world\' > \'%s\'"'

with tempfile.NamedTemporaryFile(mode='r', encoding='utf-8') as f:
    subprocess.check_call(cmd % (bash, wintolin(f.name)))
    out = f.read()

Edit: As of Windows build 14951, you should be able to use stdout=PIPE. See the WSL blog post Windows and Ubuntu Interoperability.

Community
  • 1
  • 1
Eryk Sun
  • 33,190
  • 5
  • 92
  • 111