Python: Trouble with cross platform running of subprocesses - python

I have the following helper method which executes commands perfectly on OSX and only with some commands on Windows:
def exec_cmd(cmd):
"""Run a command and return the status, standard output and error."""
proc = Popen(shlex.split(cmd), stdout=PIPE, stderr=PIPE)
stdout, stderr = proc.communicate()
# I like to get True or False rather than 0 (True) or 1 (False)
# which is just backwards as usually 0 is False and 1 is True
status = not bool(proc.returncode)
return (status, stdout, stderr)
For example, the following sample commands all work perfectly on Mac using my exec_cmd helper:
osascript -e 'tell application "Microsoft PowerPoint" to activate
osascript -e 'tell application "Microsoft PowerPoint" to quit
For example, the following sample commands all work perfectly on Windows using my exec_cmd helper:
"C:\Program Files\Microsoft Office\Office15\Powerpnt.exe" /S "C:\Users\MyUser\example.pptx"
Taskkill /IM POWERPNT.EXE /F
However, the following does not work on Windows:
START "" "C:\Program Files\Microsoft Office\Office15\Powerpnt.exe"
It errors out with:
WindowsError: [Error 2] The system cannot find the file specified
Even this doesn't work:
p = Popen(["START", "", "C:\Program Files\Microsoft Office\Office15\Powerpnt.exe"], stdout=PIPE, stderr=PIPE)
However running that same command on the command line works fine, or even stranger just doing this works:
os.system('START "" "C:\Program Files\Microsoft Office\Office15\Powerpnt.exe"')
Why does os.system work, but not the Popen version? These are just simple open and close app examples, but I'd like to do more as I need to get the stdout output for some commands I plan on running.
Any help on sorting this out is appreciated. I can't seem to understand the underlying mechanic of os.system vs. subprocess.Popen.

You are seeing this issue because START isn't a program, its a shell command. According to the documentation, os.system() "Executes the command (a string) in a subshell", where as popen doesn't. os.system() effectively spawns a new cmd.exe instance, and passes the command to that, where as popen just spawns a new process.
You are getting the The system cannot find the file specified error because there isn't a program called START

Related

Executing PowerShell commands with Python 3.5

I have been working on an issue that requires a Python script to run via the PowerShell command line. The script should pass the command to the command line and save the output. However, I'm running into an issue where some command line arguments are not recognized.
import subprocess
try:
output = subprocess.check_output\
(["Write-Output 'Hello world'"], shell=True)
# (["dir"], shell=True)
except subprocess.CalledProcessError as e:
print(e.output)
print('^Error Output^')
If I use the current command with the check_output command, I get an error stating that:
'"Write-Output 'Hello world'"' is not recognized as an internal or external command,
operable program or batch file.
If I just use the "dir" line, the script runs just fine. I'm at odds here as to why this would be happening. This is not the exact script that I'm running, but it produces the same problem on my machine. If I just type the problem command into the command line, it would output "Hello world" onto the new line just as expected.
Any insight as to why this would be happening would be greatly appreciated. If it's of relevance, I would like to not use any sort of admin privilege workaround.
I believe this is because in Windows your default Shell is not PowerShell, you could Execute a Powershell command, calling the executable by executing Powershell with the arguments you need.
For Example
POWERSHELL_COMMAND = r'C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe'
subprocess.Popen([POWERSHELL_COMMAND,
'-ExecutionPolicy', 'Unrestricted',
'Write-Output', 'Hello World'],
stdout = subprocess.PIPE,
stderr = subprocess.PIPE)
if powershell is not in path you could use the full path for the executable
or if it's in path you could use just POWERSHELL_COMMAND = "powershell" as command, becareful, with the backslashed windows paths, to avoid errors you could use raw strings.
To verify that you have powershell in path, you could go to the configurations and check, or you could just open a cmd and type powershell and if It works, then you could assume that powershell is in path.
From the docs:
On Windows with shell=True, the COMSPEC environment variable specifies the default shell.
So set COMSPEC=powershell allows to make shell=True use powershell as default instead of cmd

Use python to run multiple commands inside the wine cmd?

Using python I can get either of these to work:
subprocess.call(['wine', 'cmd'])
os.system("wine cmd")
I'm using Ubuntu and python 3.5, Once I get into the wine cmd prompt I can no longer run commands, non of the ways to run multiple commands that I have seen online work, they don't error out, it just opens the cmd and pauses, I think it treats the cmd once open as a running command and is waiting to move on to the next command which it assumes is for the shell not the wine cmd, how can i then run commands inside the wine cmd once opened?
edit: Basically any time I run a command that requires further user input from within that command, how do I interact inside of that command?
You could build up from DOS through BASH to python as in the example code here. I cut and paste the code into python 2.7 and it worked, but you might like to confirm on 3.5
If you specifically need interaction rather than just running a DOS command then you could use subprocess.Popen.communicate to interact with your script which then interacts with wine/dos.
import subprocess, os, stat
from subprocess import Popen
from subprocess import PIPE
from subprocess import check_output
command_script="/tmp/temp_script.sh"
f1 = open(command_script,'w')
f1.write("#!/bin/bash\n")
#to run a dos command
#f1.write(r'WINEPREFIX=/path/tp/wine/prefix wine cmd /c #mydoscommand argval1'+'\n')
#for example
f1.write(r'wine cmd /c #echo Hello_world'+'\n')
#or to run a specifically pathed executable
#f1.write(r'WINEPREFIX=/path/tp/wine/prefix wine "c:\\Program Files (x86)\\path\\to\\executable.exe" additionalargs '+'\n')
f1.close()
st = os.stat(command_script)
os.chmod(command_script, st.st_mode | stat.S_IEXEC)
p = Popen(command_script, stdin=PIPE, stdout=PIPE, stderr=PIPE)
output, err = p.communicate(b"input data that is passed to subprocess' stdin")
rc = p.returncode
print output
os.remove(command_script)
Have a look at the answers where I nicked some of the code from Running windows shell commands with python and calling-an-external-command-in-python

Opening a terminal running the same program in Python

I am familiar with how to open a terminal from Python (os.system("gnome-terminal -e 'bash -c \"exec bash\"'")), but is there a way to open another terminal running the same program that opened the new terminal?
For instance, if I was running a program called foo.py and it opened another terminal, the new terminal would also be running foo.py.
See this question, it's pretty close. You want to add sys.argv as a parameter, though:
import sys
import subprocess
cmd = 'xterm -hold -e ./{0}'.format(' '.join(sys.argv))
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Be sure you somehow check how many processes/terminals you run already, otherwise it will hang your machine in a matter of seconds.

subprocess in python not excecuting command

I have a bit of code that I am designing to take a file, perform the dos2unix command on it, then copy that file to a file called INPUT, and then run a command that boots a program. From my code the first two tasks work flawlessly, however the script doesn't seem to excecute the command line which starts the program. However when I take the command line exactly as I have it written in the script, and pass it in terminal, it works fine.
here is the code:
import subprocess
import os
os.chdir('/home/mike/testing/crystal')
subprocess.Popen(['dos2unix mgo_input'], stdout=subprocess.PIPE, shell=True)
subprocess.call(['cp mgo_input INPUT'], shell=True)
subprocess.Popen(['mpirun -np 8 Pcrystal </dev/null &> mgo_singlepoint.out &'], stdout=subprocess.PIPE, shell=True)
it is the mpirun section of the code that seems to be getting hung up
Popen() returns before the called program finishes execution. Using call (or check_call, which checks return codes) can be a better solution. Better yet, use python for the conversion and the copy.
I'm not sure why you are piping stdout, but I'm going to assume you don't want dos2unix or mpirun to print to the screen, so I redirect them to /dev/null.
import subprocess
import shutil
import os
os.chdir('/home/mike/testing/crystal')
subprocess.check_call('dos2unix mgo_input', stdout=open('/dev/null','w'), shell=True)
shutil.copy2(mgo_input, 'INPUT')
subprocess.check_call('mpirun -np 8 Pcrystal', stdout=open('mgo_singlepoint.out', 'w'), stdstderr=subprocess.STDOUT, shell=True)

Python/CMD command

So I am running a command in my python py file
myNewShell = os.system('start "%s" /d "%s" cmd /f:on /t:0A /k "W:\\Desktop\\alias.bat"' % (myShot, myWorkDir))
This opens up a shell
How exactly would I input something into this shell directly from my python script, thus bypassing your actual cmd.exe. I have a bunch of DOSKEYs set up, such as maya which opens up the maya program. How would I add a line of code to my python script, so that it loads the shell with those aliases and inputs my command directly
Take a look at the powerful and useful subprocess module
You can then do code like this
import subprocess
pro = subprocess.Popen("cmd", stdout=subprocess.PIPE, stdin=subprocess.PIPE)
pro.stdin.write("mybat.bat\n")
pro.stdin.write("myother.bat\n")
pro.stdin.write("start mysillyprogram\n")
pro.stdin.flush()
pro.terminate() # kill the parent

Categories