command line arguments with subprocess.Popen - python

I've GUI where if i press a button "process" calls a python script which needs one command line argument. so i used subprocess.Popen like this:
subprocess.Popen(['chbif /home/vnandak/Work/VS30_Cradle/SV000_sv000/cradle_vs30_dkaplus01_fwd_dl140606_fem140704_v00_mod1.bif'],shell=True)
chbif is the alias for the .py script
This works fine but now i want to choose a file of my choice by browsing using askfilename() func from tkFileDailog() module. how can i do this?
I thought of doing it like this:
def bifcall():
name13= askopenfilename()
subprocess.Popen(['chbif', name13],shell=True)
But if i use this it throws an error saying that it does'nt have one more command line argument which is the file name

If you pass shell=True, the first argument should be a string, and it will be interpreted as the shell would interpret it. If shell=False, then the first argument should be a list, which sort of bypasses the shell.
Here's an answer to another question that explains it well: https://stackoverflow.com/a/15109975/451201

Because if you use shell=True, you can only use one string as the first argument.
Try to change
subprocess.Popen(['chbif', name13],shell=True)
to
subprocess.Popen('chbif ' + name13,shell=True)
But it is not recommended to use shell=True for security reasons (See https://docs.python.org/2/library/subprocess.html#frequently-used-arguments for details).

Related

%* equivalent in python

Does anybody know what is the equivalent of batch script's %* in python ?
Clarification: in a batch file %* is all parameters specified in the command line -- this is very useful if you want to forward the parameters to another program.
My Problem is that I want to convert one batch call to a python call :
Batch call is :
trial.bat %*
what is the equivalent python call ?(P.S. I know I can just go through the whole sys.argv list and append parameters in some string and pass that to the batch file, but I am looking for a simpler solution here)
I tried following :
os.system('trial.bat '+sys.argv)
os.system('trial.bat '+sys.argv[1:])
But that is not working. I tried similar thing using argparse as well. Its not working either. Please help.
sys.argv[1:] is pretty close. The thing is that argv is a list of arguments, and [1:] is a slice of a list, which is again a list. If you want a string with all arguments combined, you can join them again:
os.system('trial.bat ' + ' '.join(sys.argv[1:]))
Or even better, you use the subprocess module which method’s accept a list of arguments:
subprocess.check_call(['trial.bat'] + sys.argv[1:])
Subprocess is much more flexible when handling parameters and will also behave similar to the parsing in argv. As an example, when calling a script with the arguments foo "hello world" bar, argv will contain this:
>>> sys.argv[1:]
['foo', 'hello world', 'bar']
Now if we were to simply join this list, we would get a single string
>>> ' '.join(sys.argv[1:])
'foo hello world bar'
As you can see, the information of the compound argument hello world is lost, resulting in a completely different meaning.
When using subprocess however, you keep your list and subprocess will automatically make sure to pass these arguments correctly to the called program. So the called program will be able to get hello world as a combined argument too.
You want subprocess.Popen (or one of it's convenience wrappers):
import subprocess
import sys
process = subprocess.Popen(['trial.bat'] + sys.argv[1:])
process.wait()
It's definitely preferred to os.system. The advantage here is that commandline arguments which may need to be quoted to keep their meaning effectively stay quoted. Additionally, this alternative is probably safer than os.system since it avoids creating a subshell.
If you want to use os.system, you need to put the command line back together manually. Python has already parsed the command line apart into separate arguments (or MSVCRT has done it on Python's behalf). This means you need to not just concatenate them back together, but also quote them appropriately.
There is nothing in the stdlib that handles the "quote them appropriately" exactly the way MSVCRT wants. That's partly because Windows quoting is actually ambiguous; there are some cases where it is impossible to round-trip things. But, for simple cases, either POSIX-style quoting (with shlex.quote) or just sticking explicit quotes around each argument will work. So, either of these:
args = ' '.join(shlex.quote(arg) for arg in [program] + sys.argv[1:])
args = ' '.join('"{}"'.format(arg) for arg in [program] + sys.argv[1:])
Then:
os.system(args)
But using subprocess is better than os.system. One reason is that you don't have to fiddle with quoting things; you can just do this:
subprocess.check_call([program] + sys.argv[1:], shell=True)
Someone still needs to put the list of arguments back together in a string so it can be passed to the shell, but now that "someone" is the subprocess module rather than your code.

Python Subprocess Error in using "cp"

I was trying to use subprocess calls to perform a copy operation (code below):
import subprocess
pr1 = subprocess.call(['cp','-r','./testdir1/*','./testdir2/'], shell = True)
and I got an error saying:
cp: missing file operand
Try `cp --help' for more information.
When I try with shell=False , I get
cp: cannot stat `./testdir1/*': No such file or directory
How do I get around this problem?
I'm using RedHat Linux GNOME Deskop version 2.16.0 and bash shell and Python 2.6
P.S. I read the question posted in Problems with issuing cp command with Popen in Python, and it suggested using shell = True option, which is not working for me as I mentioned :(
When using shell=True, pass a string, not a list to subprocess.call:
subprocess.call('cp -r ./testdir1/* ./testdir2/', shell=True)
The docs say:
On Unix with shell=True, the shell defaults to /bin/sh. If args is a
string, the string specifies the command to execute through the shell.
This means that the string must be formatted exactly as it would be
when typed at the shell prompt. This includes, for example, quoting or
backslash escaping filenames with spaces in them. If args is a
sequence, the first item specifies the command string, and any
additional items will be treated as additional arguments to the shell
itself.
So (on Unix), when a list is passed to subprocess.Popen (or subprocess.call), the first element of the list is interpreted as the command, all the other elements in the list are interpreted as arguments for the shell. Since in your case you do not need to pass arguments to the shell, you can just pass a string as the first argument.
This is an old thread now, but I was just having the same problem.
The problem you were having with this call:
subprocess.call(['cp','-r','./testdir1/*','./testdir2/'], shell = False)
was that each of the parameters after the first one are quoted. So to the shell sees the command like this:
cp '-r' './testdir1/*' './testdir2/'
The problem with that is the wildcard character (*). The filesystem looks for a file with the literal name '*' in the testdir1 directory, which of course, is not there.
The solution is to make the call like the selected answer using the shell = True option and none of the parameters quoted.
I know that the option of shell=True may be tempting but it's always inadvisable due to security issues. Instead, you can use a combination of the subprocess and glob modules.
For Python 3.5 or higher:
import subprocess
import glob
subprocess.run(['cp', '-r'] + glob.glob('./testdir1/*') + ['./testdir2/'])
For Python 3.4 or lower:
import subprocess
import glob
subprocess.call(['cp', '-r'] + glob.glob('./testdir1/*') + ['./testdir2/'])

Execute cmd-like command in Python

I want to make a Python code that will open a program like cmd would, then export a .txt file from the file menu. The code looks like this for cmd:
c:\ESG\Statsvis.exe \\192.168.100.222\c\ESG\S1-424\2012\06\29\S1-42420120629.dsf /output=C:\Users\jessica.macleod\Desktop\outfile.txt /param=RMS Amplitude
In cmd, the above line does exactly what I want. What would be the equivalent for Python?
See subprocess.Popen, like this:
subprocess.Popen(["/bin/ls", "-l"]
Or, depending on what you want to get as result (stdout, return code), use subprocess.call, subprocess.call_check, or other snippets in this module.
Another way would be os.system().
import os
os.system("c:\\ESG\\Statsvis.exe \\192.16...0629.dsf /output=C:\\...\\outfile.txt ...")
If you want to have exact shell/cmd behavior, then set the shell argument to True in a suprocess.Popen() call. However, from the documentation:
Warning
Invoking the system shell with shell=True can be a security hazard if
combined with untrusted input. See the warning under Frequently Used
Arguments for details.
If you need the output of the command use subprocess:
import subprocess
out = subprocess.check_output("dir c:\ /AD", shell = True)

How to add an append symbol in a subprocess in python

I have the following code, which runs a command with arguments
subprocess.call(["schtasks.exe", "/Query","/V","/FO","csv",">>", r"D:/temp/fo3.csv"])
However I am having problems with the >> part of the argument, the command does not seem to like it, it gives the following message
ERROR Invalid argument /option - >>
So my question is how do i get the >> argument to work?
when you pass arguments as a list like that, subprocess passes each argument to the program. What is probably happening is that your program (schtasks.exe) is seeing the argument >> and it doesn't know what to do with it. When you run this in the shell, >> is interpreted by the shell as redirection and so your program never sees it.
You have 2 options, pass the arguments as a string and use shell = True. This is not recommended if you are accepting user input which can modify the string you're passing to subprocess as it would lead to a security risk in your program.
Your second option is to pass an open file object to stdout, e.g.
f = open(r"D:/temp/fo3.csv",'a')
subprocess.call(["schtasks.exe", "/Query","/V","/FO","csv"], stdout = f)
Output redirection (the >>) is a shell feature and won't work if you call the program directly. Use shell=True in the call.

How to use python in windows to open javascript, have it interpreted by WScript, and pass it the command line arguments

I have a format holding paths to files and command line arguments to pass to those files when they are opened in Windows.
For example I might have a path to a javascript file and a list of command line arguments to pass it, in such a case I want to open the javascript file in the same way you might with os.startfile and pass it the command line arguments - since the arguments are saved as a string I would like to pass it as a string but I can also pass it as a list if need be.
I am not quite sure what I should be using for this since a .js is not an executable, and thus will raise errors in Popen while startfile only takes verbs as its second command.
This problem can be extended to an arbitrary number of file extensions that need to be opened, and passed command line arguments, but will be interpreted by a true executable when opening.
If windows has registered the .js extension to open with wscript, you can do this, by leaving that decision up to the windows shell.
You can just use os.system() to do the same thing as you would do when you type it at the command prompt, for example:
import os
os.system('example.js arg1 arg2')
You can also use the start command:
os.system('start example.js arg1 arg2')
If you need more power, for example to get results, you can use subprocess.Popen(), but make sure to use shell=True (so that the shell can call the right application):
from subprocess import Popen
p = Popen('example.js arg1 arg2', shell=True)
# you can also do pass the filename and arguments separately:
# p = Popen(['example.js', 'arg1', 'arg2'], shell=True)
stdoutdata, stderrdata = p.communicate()
(Although this would probably require cscript instead of wscript)
If Windows doesn't have any default application to open the file with (or if it's not the one you want), well, you're on your own of course...

Categories

Resources