check_call and command line arguments on Windows - python

My python program is invoking a script on Windows using check_call:
import subprocess
subprocess.check_call(['my_script.bat', 'foo=bar'])
I expect my_script.bat to receive a single command line argument (foo=bar), but is instead receiving two (foo and bar). In other words, it seems that the equal sign is being converted to whitespace. Everything works as expected on Linux.
What is the correct way to format this string on Windows such that my_script sees a single argument containing the equal sign?
my_script.bat:
echo %1
In my actual application, my_script.bat is scons.bat.

This case tests just fine for me under windows 7, using python2.7
my_script.py
import sys
print sys.argv
python shell
>>> import subprocess
>>> subprocess.check_call('python', 'my_script.py', 'arg1', 'foo=bar')
['myscript.py', 'arg1', 'foo=bar']
0
You may want to verify how your "my_script" is handling the incoming args.
Update
Since you have updated your question to reflect that you are specifically working with windows batch files, then here is the solution: http://geoffair.net/ms/batch.htm
Note: If a semicolon, ';' or an equal sign (=) is used as a command
line argument to a batch file, it is treated as a blank space. For
example, in the following test.bat batch file -
Equal sign is a special character that will be replaced with whitespace. It needs to be quoted:
subprocess.check_call(['my_script.bat', '"foo=bar"'])

Related

Open a command prompt at a specific location followed by running a script at that location with some command line arguments using python

I was able to open command prompt and change the directory to required location using the subprocess module, but I was unable to pass further arguments to run an application along with some command line arguments. I am new to the subprocess module, so I did some search over stackoverflow couldn't find the desired result.
Mycode:
import subprocess
path = r"C:/Users/Application_Folder"
p = subprocess.Popen(r"cmd.exe", cwd="C:/Project_Files", shell=True)
Desired output:
Path: C:\Users\Application_folder\Application.exe
Need to open the cmd prompt in windows at the Application_folder location,
run the Application.exe by passing some command line arguments, using python
Just pass the command line you actually want to execute, with the executable path and whatever arguments you want to pass:
command_line = [r'C:\Users\Application_Folder\Application.exe', '/argument1', '/argument2']
p = subprocess.Popen(command_line, cwd=r'C:\Project_Files')
A couple of notes to keep in mind:
You shouldn't use shell=True. It's not necessary here -- in fact it's almost never necessary -- but it does introduce a potential security risk.
The whole point of raw string literals (starting with r' or r") is to change how backslash characters within the string are interpreted. r'C:\Program Files' is exactly the same string as "C:\\Program Files". If your string doesn't have backslashes in it, don't bother using the r prefix.

executable python file sys not accepting '(' character

I am trying to write a python file that takes command line input and performs some actions. The input will consist of a-z, [, ], ( and ). I made the following program just to check that I could keep going:
#!/usr/bin/env python
import sys
print str(sys.argv)
I did chmod +x program and tried calling ./program qwerty (abc) [hi] and it returned:
-bash: syntax error near unexpected token `('
Is there any way of changing the program so that it accepted parentheses in arguments?
Note: I have also tried placing square brackets before parentheses and it returns the same error.
There's nothing the script can do about shell syntax when invoking the script. The shell parses the command line first. You have to escape or quote characters that have special meaning in the shell (which includes most punctuation characters):
./program qwerty \(abc\) '[hi]'

cupsfilter Does Not Work With Python subprocess.call()

After reading this question, I've been experimenting and using subprocess.call() in Python to run a command as if from the terminal. I've found it works quite well and I prefer it over os.system. I've run into a problem with cupsfilter, though. I'm trying to run this command on OS X, from Python:
cupsfilter -o cpi=10 PDFTestIn.txt 2>PDFTestError.txt >PDFTestOut.pdf
(I tried it, originally, with more than one option, but am limiting it to one option until I can get it working.)
I can run the command fine from the command line and from os.system, but I'm having trouble with this command in subprocess.call(). I've been experimenting in IDLE and cupsfilter does not work with this as other programs do. If I provide options in the form:
-o optionname=optionvalue
I always get error messages. I tried this:
import subprocess
import os
cmd = '/usr/sbin/cupsfilter'
args = ['-o cpi=10']
ifile='PDFTestIn.txt'
ofile='PDFTestOut.pdf'
efile='PDFTestError.txt'
cmdargs = [cmd] + args + [ifile]
with open(ofile, 'w') as pdfout, open(efile, 'w') as errout:
subprocess.call(cmdargs, stdout=pdfout, stderr=errout)
When I do that, I get a return value of 1 from the last command. I check PDFTestError.txt for output and it reads:
cupsfilter: Unknown option " ".
I experimented by changing the 4th line to this:
args = ['-ocpi=10']
and I get this error:
cupsfilter: Unknown option "c".
Whatever character comes after the "-o" is seen as an option, and only that one letter is acknowledged, not the word (like "cpi"). Even though there are other options I can use besides "-o," I thought I'd try it without the "-o" just in case. When I do that, I get this error:
cupsfilter: Only one filename can be specified.
(And if I use only the command as an argument in the list passed to subprocess.call(), and still specify stdout and stderr, it works okay.)
Summary: When I use "-o" to provide an option for cupsfilter, in subprocess.call(), cupsfilter looks only at the next character, not the next word. If I have a space after "-o" as I would on the command line, it expects that space to be an option. If I leave the space out, it looks at the next character and not the next word. If I leave out "-o" it sees the option as another file name (as I'd expect).
But if I use the command line, above, from a terminal, or from os.system(), there's no problem at all.
Why won't this work with subprocess.call() and is there a way to correct that?
You need to separate each arg, '-o cpi=10' -> '-o', 'cpi=10':
subprocess.call([cmd,'-o','cpi=10',infile], stdout=pdfout, stderr=errout)

Passing empty string to argparse

I'm using argparse (Python 3.2). A parameter mode is defined simply as:
p.add_argument('--mode', dest='mode')
I want to call the program from the command line in such a way that parameter mode is set to an empty string; or at least to a single space ' ' (I can strip whitespace later).
I tried to use (under Windows) program_name --mode "" and program_name --mode " ", but neither worked.
This seems to work for me under OS-X:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--mode')
p = parser.parse_args()
print(p)
and then:
python test.py --mode=
I don't have a windows machine, so I don't know anything about how those operate...
Of course, the other things you mentioned above would work as well on OS-X. The advantage here is that it shouldn't rely on how the shell splits arguments enclosed in quotations which should make it work in more places I would think.

python behaviour when passed an asterisk

I'm writing a small script, that should be able to handle multiple files. So I've added that files can be passed comma seperated, and do a arg.split(',') and then handle each one.
Now I've wanted to add asterisk as input possibility like
python myPythonScript.py -i folder/*
If I print the the argument to option -i right when I access it the first time I get
folder/firstFileInFolder.txt
But if I call my script with
python myPythonScript.py -i someFolder/someFile,folder/*
it works just fine. Does anyone have an idea, why python might behave that way?
Try to run this script
import sys
for arg in sys.argv:
print arg
python script.py *
your shell expands the asterisk before python sees it.
As mentioned in the comments, your shell is expanding the asterisk for the non-comma separated case. If you know that the user may specify an asterisk as part of a file name as in your second example, you can have Python do the path expansion by using the glob module.
from glob import glob
glob('*')
code which would allow either the shell or Python to do asterisk expansion may look something like this:
import glob
file_list = []
for pattern in sys.argv[1:]:
file_list.extend(glob.glob(pattern))
In your case, using a comma as a separator would then prevent you from using a comma as part of a filename.

Categories

Resources