Python code to run bash commands in Windows [duplicate] - python

I'm on Windows using PowerShell and WSL 'Ubuntu 20.04 LTS'. I have no native Linux Distro, and I cant use virtualisation because of nested device reasons.
My purpose is to use a Windows Python script in PowerShell to call WSL to decrypt some avd-snapshots into raw-images. I already tried os.popen, subprocess.Popen/run/call, win32com.client, multiprocessing, etc.
I can boot the WSL shell, but no further commands are getting passed to it. Does somebody know how to get the shell into focus and prepared for more instructions?
Code Example:
from multiprocessing import Process
import win32com.client
import time, os, subprocess
def wsl_shell():
shell = win32com.client.Dispatch("wscript.shell")
shell.SendKeys("Start-Process -FilePath C:\\Programme\\WindowsApps\\CanonicalGroupLimited.Ubuntu20.04onWindows_2004.2021.825.0_x64__79rhkp1fndgsc\\ubuntu2004.exe {ENTER}")
time.sleep(5)
os.popen("ls -l")
if __name__ == '__main__':
ps = Process(target = wsl_shell)
ps.start()

There are a few ways of running WSL scripts/commands from Windows Python, but a SendKeys-based approach is usually the last resort, IMHO, since it's:
Often non-deterministic
Lacks any control logic
Also, avoid the ubuntu2004.exe (or, for other users who find this, the deprecated bash.exe command). The much more capable wsl.exe command is what you are looking for. It has a lot of options for running commands that the <distroname>.exe versions lack.
With that in mind, here are a few simplified examples:
Using os.system
import os
os.system('wsl ~ -e sh -c "ls -l > filelist.txt"')
After running this code in Windows Python, go into your Ubuntu WSL instance and you should find filelist.txt in your home directory.
This works because:
os.system can be used to launch the wsl command
The ~ tells WSL to start in the user's home directory (more deterministic, while being able to avoid specifying each path in this case)
wsl -e sh runs the POSIX shell in WSL (you could also use bash for this)
Passing -c "<command(s)>" to the shell runs those commands in the WSL shell
Given that, you can pretty much run any Linux command(s) from Windows Python. For multiple commands:
Either separate them with a semicolon. E.g.:
os.system('wsl ~ -e sh -c "ls -l > filelist.txt; gzip filelist.txt')
Or better, just put them all in a script in WSL (with a shebang line), set it executable, and run the script via:
wsl -e /path/to/script.sh
That could even by a Linux Python script (assuming the correct shebang line in the script):
wsl -e /path/to/script.py
So if needed, you can even call Linux Python from Windows Python this way.
Using subprocess.run
The os.system syntax is great for "fire and forget" scripts where you don't need to process the results in Python, but often you'll want to capture the output of the WSL/Linux commands for processing in Python.
For that, use subprocess.run:
import subprocess
cp = subprocess.run(["wsl", "~", "-e", "ls", "-l"], capture_output=True)
print(cp.stdout)
As before, the -e argument can be any type of Linux script you want.
Note that subprocess.run also gives you the exit status of the command.

Related

How to run python code in the currently activated virtualenv with subprocess?

I have a virtualenv named 'venv' and it is activate:
(venv)>
and I wrote codes that I'll run it in the virtualenv (main.py):
import subprocess
result = subprocess.run('python other.py', stdout=subprocess.PIPE)
but when I run main.py file:
(venv)> python main.py
subprocess does not execute the command (python other.py) in the virtualenv i.e venv
How to run subprocess command in the current virtualenv session?
A child process can't run commands in its parent process without that process's involvement.
This is why ssh-agent requires usage as eval "$(ssh-agent -s)" to invoke the shell commands it emits on output, for example. Thus, the literal thing you're asking for here is impossible.
Fortunately, it's also unnecessary.
virtualenvs use environment variables inherited by child processes.
This means that you don't actually need to use the same shell that has a virtualenv activated to start a new Python interpreter intended to use the interpreter/libraries/etc. from that virtualenv.
subprocess.run must be passed a list, or shell=True must be used.
Either do this (which is better!)
import subprocess
result = subprocess.run(['python', 'other.py'], stdout=subprocess.PIPE)
Or this (which is worse!)
import subprocess
result = subprocess.run('python other.py', stdout=subprocess.PIPE, shell=True)
If you want to run a script with the same Python executable being used to run the current script, don't use python and rely on the path being set up properly, just use sys.executable:
A string giving the absolute path of the executable binary for the Python interpreter, on systems where this makes sense.
This works if you executed the script with python myscript.py relying on the active virtualenv's PATH. It also works if you executed the script with /usr/local/bin/python3.6 to ignore the PATH and test your script with a specific interpreter. Or if you executed the script with myscript.py, relying on a shbang line created at installation time by setuptools. Or if the script was run as a CGI depending on your Apache configuration. Or if you sudod the executable, or did something else that scraped down your environment. Or almost anything else imaginable.1
As explained in Charles Duffy's answer, you still need to use a list of arguments instead of a string (or use shell=True, but you rarely want to do that). So:
result = subprocess.run([sys.executable, 'other.py'], stdout=subprocess.PIPE)
1. Well, not quite… Examples of where it doesn't work include custom C programs that embed a CPython interpreter, some smartphone mini-Python environments, old-school Amiga Python, … The one most likely to affect you—and it's a pretty big stretch—is that on some *nix platforms, if you write a program that execs Python by passing incompatible names for the process and arg0, sys.executable can end up wrong.

asyncio DatagramProtocol does not print to console but does so in sublime text window [duplicate]

I'm using mingw with msys and mintty on windows. I have a problem that msys and mintty are somehow not flushing output until a command is finished. This means I can't really run any interactive programs.
For example, if I have in C:
printf("Test\n");
the output won't appear until the program has terminated. However, if I have:
printf("Test\n"); fflush(stdout);
then the output appears immediately. If I use msys without mintty or the windows console, then everything works normally.
So my question, what's going on with msys and mintty?
This can be an issue when msys uses the rxvt shell under a number of scenarios. In cases where I see this problem, I ask msys to use the native Windows shell for its console. For example:
C:\MinGW\msys\1.0\msys.bat --no-rxvt
I thought that modern MSYS installations default to using the native shell as MSYS developers seem to prefer it. I have other issues with the native shell that drive me to use the rxvt shell, so I do infrequently run into this issue.
C:\MinGW\msys\1.0\msys.bat --rxvt
I find that the rxvt shell usually works fine except for certain applications that are built as "console" utilities meant to run from a command-line.
The only thing that worked for me was to precede the command with winpty ...
$ winpty java ClassName
It causes unbuffered output to be 3x slower and buffered output to be 5x slower (in my case, with Java).
To always have a command invisibly invoked by winpty ...
$ cd ~
$ pwd -W
... add the following line to .bashrc ...
alias java="winpty java"
... then restart terminal and ignore the (one-time) warning message.

Launching a python script in a new terminal from another python script

I'm using LXDE, I would like to Launch a python script in a new terminal from another python script.
I would like the new python script to be totally independent.
I have tried a lot of things...
Calling xterm (or x-terminal-emulator) directly from python with the subprocess.call or subprocess.Popen with or without shell=True argument, it didn't work. It gives me an error about display not being set.
I have also created a sh file which calls the other python script and tried to call it, same results.
Any way to do this?
This works fine for me:
blocking:
import os
os.system("xterm -e \"python christmaskittens.py\"")
non blocking:
import os
os.system("xterm -e \"python christmaskittens.py\" &")

Ansicon doesn't install from python but only from cmd

I'm trying to install ansicon on Windows 8.1. I extracted the files and got to the level that I need to call ansicon -i. When I type this in my cmd and run python scripts that works great but when I call t from python by os.system('ansicon -i') that doesn't work and seems like it doesn't have any influence on the cmd.
Why os.system('ansicon -i') doesn't work and what alternative method can I use from within python?
First off, it’s not the -i flag that really does the work. -i only tells it to add itself to AutoRun. The -p flag that -i implies is what really does the work: -p tells it to inject a DLL into the parent process, and therein lies the problem: when you use os.system, you spawn a shell, which then runs the command you give it. But then you have Python running cmd running ansicon, and ansicon will inject into cmd, and then cmd, having finished its work, will exit.
Rather than using os.system, use the subprocess module, e.g.:
subprocess.check_call(['ansicon', '-p'])
The subprocess module (unlike os.system) will execute the command directly without a shell in-between (unless you pass shell=True). Then Python will spawn ansicon, and ansicon will inject into Python, as desired.
That said, rather than having ansicon inject itself into Python, Python could probably just load the DLL itself, avoiding some hardship:
import sys
import math
import ctypes
bitness = 1 << round(math.log2(round(math.log2(sys.maxsize + 1))))
ctypes.WinDLL('ANSI{}.DLL'.format(bitness))

os.system doesn't work in Python

I'm working on windows vista, but I'm running python from DOS command. I have this simple python program. (It's actually one py file named test.py)
import os
os.system('cd ..')
When I execute "python test.py" from a Dos command, it doesn't work.
For example, if the prompt Dos Command before execution was this:
C:\Directory>
After execution, must be this:
C:\>
Help Plz.
First, you generally don't want to use os.system - take a look at the subprocess module instead. But, that won't solve your immediate problem (just some you might have down the track) - the actual reason cd won't work is because it changes the working directory of the subprocess, and doesn't affect the process Python is running in - to do that, use os.chdir.
I don't really use Windows, but you can try cmd /k yourcommandhere. This executes the command and then returns to the CMD prompt.
So for example, maybe you can do what you want like this:
subprocess.call(['cmd', '/k', 'cd .. && prompt changed'])
As I said, I am not familiar with Windows, so the syntax could be wrong, but you should get the idea.
In case you don't know, this is a different CMD instance than the one you were in before you started your python script. So when you exit, your python script should continue execution, and after it's done, you'll be back to your original CMD.

Categories