I have been looking through some similar posts and through the ImageMagick page, but I cannot seem to find a reason for my issue:
Note I am using a windows machine.
I have a .ps image in a folder and it works when running with the command to convert it from the cmd: convert saved.ps newsaved.png
However when I try to execute it from my python script with the following code:
args = ["convert","saved.ps newsave.png"]
subprocess.Popen(args)
#or this call(args)
os.system("start newsave.png")
The cmd window says that newsave.png is an invalid parameter. (The error message being: Invalid parameter - newsave.png in the cmd window, which then closes instantly)
Having the everything seperated by a comma in args has also not helped. os.getcwd() returns the current work directory as well, so I know I'm in the right dir. The error happens when the subprocess is called.
Make each command-line argument a separate element of args. Also, use subprocess.call to ensure that the convert function has completed before you call os.system("start newsave.png"):
args = ["convert", "saved.ps", "newsave.png"]
rc = subprocess.call(args)
if rc != 0:
print "rc =", rc
In the end I had to add shell=True in order for the conversion to work properly.
args = ["convert", "saved.ps", "newsave.png"]
subprocess.call(args, shell=True)
Thanks to Warren for the help.
Related
My code is simply as follows:
file = 'C:\\Exe\\First Version\\filename.exe'
os.system(file)
When I run this program, a Windows error is raised: can't find the file specified.
I found out the problem has to with the whitespace in the middle of "First Version". How could I find a way to circumvent the problem?
P.S.: what if the variable 'file' is being passed as an argument into another function?
Putting quotes around the path will work:
file = 'C:\\Exe\\First Version\\filename.exe'
os.system('"' + file + '"')
but a better solution is to use the subprocess module instead:
import subprocess
file = 'C:\\Exe\\First Version\\filename.exe'
subprocess.call([file])
I used this:
import subprocess, shlex
mycmd='"C:\\Program Files\\7-Zip\\7z" x "D:\\my archive.7z" -o"D:\\extract folder" -aou'
subprocess.run(shlex.split(mycmd))
Try enclosing it with double quotes.
file = '"C:\\Exe\\First Version\\filename.exe"'
os.system(file)
It is true that os.system will launch a binary which has spaces in the path by wrapping that path in quotes. (That should be a pretty obvious solution if you are accustom to using a terminal.) By itself, however, that doesn't resolve the more painful problem with this function... Once you do that, you can then run into troubles adding arguments to your command! (Ahh!)
All current recommendations are to use the subprocess module now instead of this old, frowned upon function. One can also use shlx to convert flat strings into lists for those subprocess functions. I've encountered issues or pains with those methods too, which I won't ramble on about... Also, it's sometimes just easier use os.system when all you want is a thin wrapper over the shell, which implicitly displays the output streams on the console, works synchronously, etc. I sure wish there was a built-in function to execute a command on the shell like this, with absolutely 0 parsing, wrapping, abstracting...
Since there is no built-in without "filters", here's my solution patch for os.system. This is lifted from an open source library of mine. This has been tested on Windows, Mac, and Ubuntu Linux. I'm aware it's not 100% foolproof, and it's more involved than one would hop for, but it's not too bad.
When you call this _system() wrapper (passing a string to execute), just surround your long path in quotes and include any arguments that needs with or without quotes as well (i.e. exactly as you would enter the command in a terminal!). On the first "token" in the command, this will remove the quotes and escape spaces in the path on Mac or Linux. On Windows, it uses the "short name" by actually resolving what that is on the given environment. That part of the code is a bit tricky. Basically it uses a batch mechanism for the name resolution, and it sends the results back over stderr for the purpose of parsing what you'd get otherwise for the Popen() results on stdout.
I think I included all the imports and defines that you'll need. If I missed any (copying and pasting spinets of source), let me know.
from os import system, getcwd, chdir
from subprocess import Popen, PIPE
import platform
__plat = platform.system()
IS_WINDOWS = __plat == "Windows"
IS_LINUX = __plat == "Linux"
IS_MACOS = __plat == "Darwin"
__SCRUB_CMD_TMPL = "{0}{1}"
__DBL_QUOTE = '"'
__SPACE = ' '
__ESC_SPACE = '\\ '
if IS_WINDOWS :
__BATCH_RUN_AND_RETURN_CMD = ["cmd","/K"] # simply assuming cmd is on the system path...
__BATCH_ONE_LINER_TMPLT = "{0} 1>&2\n" # the newline triggers execution when piped in via stdin
__BATCH_ESCAPE_PATH_TMPLT = 'for %A in ("{0}") do #echo %~sA'
from subprocess import STARTUPINFO, STARTF_USESHOWWINDOW
__BATCH_ONE_LINER_STARTUPINFO = STARTUPINFO()
__BATCH_ONE_LINER_STARTUPINFO.dwFlags |= STARTF_USESHOWWINDOW
def _system( cmd, wrkDir=None ):
if wrkDir is not None:
initWrkDir = getcwd()
print( 'cd "%s"' % (wrkDir,) )
chdir( wrkDir )
cmd = __scrubSystemCmd( cmd )
print( cmd )
system( cmd )
print('')
if wrkDir is not None: chdir( initWrkDir )
def __scrubSystemCmd( cmd ):
"""
os.system is more convenient than the newer subprocess functions
when the intention is to act as very thin wrapper over the shell.
There is just one MAJOR problem with it:
If the first character in the command is a quote (to escape a long path
to the binary you are executing), then the limited (undesirable) parsing
built into the function can all fall apart. So, this scrub function
solves that...
"""
if not cmd.startswith( __DBL_QUOTE ): return cmd
cmdParts = cmd[1:].split( __DBL_QUOTE )
safeBinPath = _escapePath( cmdParts[0] )
args = __DBL_QUOTE.join( cmdParts[1:] ) # (the leading space will remain)
return __SCRUB_CMD_TMPL.format( safeBinPath, args )
def _escapePath( path ):
if not IS_WINDOWS: return path.replace(__SPACE, __ESC_SPACE)
return( path if __SPACE not in path else
__batchOneLinerOutput( __BATCH_ESCAPE_PATH_TMPLT.format(path) ) )
def __batchOneLinerOutput( batch ):
cmd = __BATCH_ONE_LINER_TMPLT.format( batch )
p = Popen( __BATCH_RUN_AND_RETURN_CMD, shell=False,
startupinfo=__BATCH_ONE_LINER_STARTUPINFO,
stdin=PIPE, stdout=PIPE, stderr=PIPE )
# pipe cmd to stdin, return stderr, minus a trailing newline
return p.communicate( cmd )[1].rstrip()
UPDATE
A better trick for the Windows context dawned on me recently. You don't need any of the conversion to short file name, or an escape sequence for a space. All you need to do is thwart the Python source which throws a monkey wrench into this by checking if the first character in the command is a double quote. Well, on Windows cmd / Batch, you can prefix any command with an # to indicate not to "echo" that line. So, if you simply slap that on the front of your command, there will no longer be a leading quote! You probably don't want the command echoed either, so it's an improvement in its self anyway.
Essentially, replace __scrubSystemCmd above with the following. If you want, you could drop all that code which gets the short file name (which is like half the code I orginally posted!)...
if IS_WINDOWS:
...
__NO_ECHO_PREFIX = "#"
def __scrubSystemCmd( cmd ):
if IS_WINDOWS:
if not cmd.startswith( __NO_ECHO_PREFIX ):
return __NO_ECHO_PREFIX + cmd
elif not cmd.startswith( __DBL_QUOTE ): return cmd
cmdParts = cmd[1:].split( __DBL_QUOTE )
safeBinPath = _escapePath( cmdParts[0] )
args = __DBL_QUOTE.join( cmdParts[1:] ) # (the leading space will remain)
return __SCRUB_CMD_TMPL.format( safeBinPath, args )
You can use the short name of the file which has spaces in its name.
file = 'C:\\Exe\\FirstV~1\\filename.exe'
os.system(file)
Hello I am trying to write a script to unzip bz2 files with 7zip and python.
First when I write
PS> & 'C:\Program Files\7-zip\7z.exe' e D:\path\example.bz2
with powershell it is working perfectly.
So I tried that with Python:
import glob
import subprocess
target = r"D:\path\*.bz2"
tab = glob.glob(target)
for i in range(len(tab)):
subprocess.call("& 'C:\\Program Files\\7-zip\\7z.exe' e %s" %tab[i], shell=True)
And I've got that error message: & was unexpected.
Does someone has an idea why ?
I am using Python 3.9.2
By using shell=True, you're opting to pass the command line to the platform-native shell, which on Windows is cmd.exe, not PowerShell, so your PowerShell command line fundamentally cannot be expected to work as-is.
If we take a step back: you don't need to involve the shell at all in your 7z.exe call, and not doing so also speeds up your operation.
By omitting shell=True, the target executable and its arguments must then be passed as the elements of an array rather than as a single command-line string.
for i in range(len(tab)):
exitCode = subprocess.call([ 'C:\\Program Files\\7-zip\\7z.exe', 'e', tab[i] ])
Note the use of exitCode = , which captures 7z.exe's exit code and therefore allows you to check for failure.
Alternatively, you could let Python raise an exception on failure automatically, by using subprocess.check_call() rather than subprocess.call()
I am trying to make a python program(python 3.6) that writes commands to terminal to download a specific youtube video(using youtube-dl).
If I go on terminal and execute the following command:
cd; cd Desktop; youtube-dl "https://www.youtube.com/watch?v=b91ovTKCZGU"
It will download the video to my desktop. However, if I execute the below code, which should be doing the same command on terminal, it does not throw an error but also does not download that video.
import subprocess
cmd = ["cd;", "cd", "Desktop;", "youtube-dl", "\"https://www.youtube.com/watch?v=b91ovTKCZGU\""]
print(subprocess.call(cmd, stderr=subprocess.STDOUT,shell=True))
It seems that this just outputs 0. I do not think there is any kind of error 0 that exists(there are error 126 and 127). So if it is not throwing an error, why does it also not download the video?
Update:
I have fixed the above code by passing in a string, and have checked that youtube-dl is installed in my default python and is also in the folder where I want to download the videos, but its still throwing error 127, meaning command "youtube-dl" is not found.
cd; cd Desktop; youtube-dl "https://www.youtube.com/watch?v=b91ovTKCZGU" is not a single command; it's a list (delimited by ;) of three separate commands.
subprocess.call(cmd, ..., shell=True) is effectively the same as
subprocess.call(['sh', '-c'] + cmd)
which is almost never what you want. Instead, just pass a single string and let the shell parse it.
subprocess.call('cd; cd Desktop; youtube-dl "https://www.youtube.com/watch?v=b91ovTKCZGU"', shell=True)
If you really want to use the list form (which is always a good idea), use the cwd parameter instead of running cd.
subprocess.call(['youtube-dl', 'https://www.youtube.com/watch?v=b91ovTKCZGU'],
cwd=os.path.expanduser("~/Desktop"))
I'll answer this with an example:
>>> subprocess.call(["echo $0 $2", "foo", "skipped", "bar"], shell=True)
foo bar
0
The first element of the list is the shell command (echo $0 $2), and the remaining elements are the positional parameters that the command can optionally use ($0, $1, ...).
In your example, you are creating a subshell that only runs the cd; command. The positional parameters are ignored. See the Popen and bash docs for details.
As noted in the comments, you should make the command a string (not a list).
In Python 3.7 running on Windows, what specific syntax is required to:
1. Navigate to a directory containing a terraform program
2. Execute "terraform apply -auto-approve" in that target directory
3. Extract the resulting output variables into a form usable in python
The output variables might take the form:
security_group_id_nodes = sg-xxxxxxxxxx
vpc_id_myvpc = vpc-xxxxxxxxxxxxx
Want to be using windows cmd style commands here, NOT powershell.
My first failed newbie attempt is:
import os
os.chdir('C:\\path\\to\\terraform\\code')
from subprocess import check_output
check_output("terraform apply -auto-approve", shell=True).decode()
Not sure about your output, but subprocess could definitely make the trick.
Try something like:
command = 'terraform apply -auto-approve'
TARGET_DIR = 'E:\Target\Directory'
subprocess_handle = subprocess.Popen(shlex.split(command), cwd=TARGET_DIR, shell=False, stdout=subprocess.PIPE)
subprocess_handle.wait()
result = subprocess_handle.communicate()[0]
print(result)
Worked for me once, just play around with params.
UPD: Here I assume that "terraform" is an executable.
I would like some help towards invoking a command prompt (& passing some argument to the command prompt) from a python script.
I use pyqt4 for developing the UI and on the UI I have a run button. On selection of run button, I would like to invoke a command prompt and pass on some script name as the argument.
self.connect(run_button, SIGNAL('clicked()'), self.runscript) # this is my run button signal and i'm calling the runscript()
def runscript(self):
print 'Inside Run Script'
os.chdir('C:\PerfLocal_PAL')
try:
subprocess.call(['C:\windows\system32\cmd.exe'])
except:
print 'Exception Caused.'
When I click on run button, the application dies and it does not invoke the command prompt at all. I tried with os.system as well same result.
also, I would like to know how to pass the argument to the call function?
Any help towards this is highly appreciated.
Thanks,
To correctly define file paths in Python on Windows, you need to do one of three things:
Use forward slashes: "C:/PerfLocal_PAL" (Python understands forward slashes regardless of platform)
Use raw strings: r"C:\PerfLocal_PAL"
Escape the backslashes: "C:\\PerfLocal_PAL"
This affects both your chdir call and your subprocess.call invocation.
However, you will also have trouble due to the fact that your parent process is a GUI application, and hence has no console streams for stdin, stdout and stderr. Try using the following instead to get a completely separate command window:
subprocess.call("start", shell=True)
You may also want to use the "/D" argument of start to set your working directory, rather than changing the cwd of the parent process:
subprocess.call(["start", "/DC:\\PerfLocal_PAL"], shell=True)
Have you tried debugging this at all? Which line does the script fail on? Does it actually start the runscript function at all?
Regarding passing arguments to cmd.exe, have a look at the documentation for subprocess.call. It will show you that you can have a second argument providing the command line parameters to the program, e.g.
subprocess.call(["C:\windows\system32\cmd.exe", "scriptname.bat"])
One problem is that subprocess.call will block until it is complete, and cmd.exe will not return until you exit it. That answers the 'just dies' but may not explain the console never appearing. Start with this:
subprocess.Popen(['C:\Windows\system32\cmd.exe'])
That at least will not block. If you can get it to appear, try your arguments, like this:
subprocess.Popen(['C:\Windows\system32\cmd.exe', 'program_or_script', 'arg1'])
Your signal connection and your subprocess call seems to be fine.
Change your chdir call to:
os.chdir(r'C:\PerfLocal_PAL')
I guess the error you are getting is of the form (when you launch your application from the command prompt):
WindowsError: [Error 123] The filename, directory name, or volume label syntax is incorrect: 'C:\PerfLocal_PAL'