Executing WSL Commands from Windows (Using Python 3) Draft

Posted by Geoff, Published: 2 years, 7 months ago (Updated: 2 years, 4 months ago)

Now that we have the Windows Subsystem for Linux (WSL) and MountainSort/MountainLab installed, I can discuss my method for interfacing with MountainSort from Windows. MountainSort commands are all executed through the Linux terminal. Therefore the initial problem was that I had to determine how to run commands through the WSL without having to necessarily open the WSL terminal manually. I know you can use the os module from Python to run commands in the Command Prompt on Windows (and the Terminal on Linux with a Linux machine), however I was unsure how to get the os module to interface with the WSL.

If you want to follow around with the examples I provide below, I have created a Jupyter Notebook with the relevant code and uploaded it to a GitHub repository1.

Executing WSL Terminal Commands from Windows (with Python 3)

After browsing GitHub for potential existing solutions I found the following repository2 that at an initial glance looked like it could solve my issue. I saw some WSL references in the source code, however when attempting to run the code, and looking at the issues reported3, it seemed like there was no Python 3 compatibility. To make the story short I cloned this repository, isolated only the WSL components, converted it to Python 3, and hosted it in my wsl_terminal repository on my GitHub4. If you import this code into Python (using a Python 3 kernel), we can execute WSL terminal commands from our Python shell/script. 

This code was strictly converted until it got to the point where it was working using the same coding patterns that the initial author provided. Therefore, I did not optimize the code. There are various inputs that are not really necessary for our applications that you will see. An example of what I will show you is running the 'dir' command in the WSL terminal, and then sleeping the terminal for 5 seconds so we can view the results before it closes. You can perform this using the following Python code:

# import the wsl_terminal code
from wsl_terminal import BashConfigure

profile = None
title = None 
cfg = BashConfigure()

commands = ['dir',  # dir commmand will tell us what files are within our current directory
           'sleep 5'  # will sleep for 5 seconds so that we can view the terminal before it closes
           ]

# here we will run the 'dir' command and then have terminal
# wait for 5 seconds before closing with 'sleep 5'.
cfg.win32_wsl_open_bash(title, commands, None)

We don't need to worry about the profile nor the title parameters. The title input is required, but isn't even used in this method, likely was just a legacy parameter that the original author left. I believe the profile variable will allow you to login under a specific username, however I do not use this functionality. Note: the WSL terminal closes after a commands finish, so if you are debugging you might want to end with a sleep X command so you can visualize the error.

Saving WSL Terminal Outputs

There might be a few cases where you want to read the WSL Terminal output. This is a good way to determine if a process has finished. If you were to run a command via Python using the method above, the Python code hands off the work to the WSL terminal, and does not wait for the WSL Terminal command to finish before proceeding with the following Python code. By saving the terminal outputs you can likely write a method that will determine when the step has finished so you can proceed with the following Python code. Another case might be to determine if there are any problems with the output so your workflow can handle errors appropriately.

A simple solution that I came up with is saving the terminal output to a .txt file. If you use the following command structure command1 >> command1_output.txt it will save the output (of command1 in this case) to the given filename (command1_output.txt in this example). 

An example of saving the same dir command output to a given filename is provided below:

# import the wsl_terminal code
from wsl_terminal import BashConfigure
from wsl_utils import get_ubuntu_path

profile = None
title = None 
cfg = BashConfigure()

windows_filename = r'D:\test_output.txt'  # the filename that we want to save
linux_filename = get_ubuntu_path(windows_filename)  # the linux version of the filename

commands = ['dir >> %s' % linux_filename] # >> filename will save the output to the filename
           
# here we will run the 'dir' command and then have terminal
# wait for 5 seconds before closing with 'sleep 5'.
cfg.win32_wsl_open_bash(title, commands, None)

Note that we also need to input the get_ubuntu_path() method. The get_ubuntu_path() method will convert the filepath value from Windows to Linux. Therefore we can predetermine a filename on our Windows machine that we want to save the output, and convert it to the appropriate Linux location within the WSL environment. If you open up the test_output.txt filename (in this case), we will have the matching values as what you saw in the previous example (with the sleep command).

References

  1. Relevant Jupyter Notebook: https://github.com/GeoffBarrett/MountainSortWindows/blob/master/notebooks/ExecutingWSLTerminalCommands.ipynb
  2. Skywind300 "terminal" GitHub Repository: https://github.com/skywind3000/terminal
  3. Skywind300 "terminal" GitHub Repository Python 3 Compatibility Issue: https://github.com/skywind3000/terminal/issues/2
  4. GeoffBarrett "wsl_terminal" GitHub Repository: https://github.com/GeoffBarrett/wsl_terminal

Comments

Post Comment