Reputation: 5234
How do I add this: -param:abc=def
as a SINGLE command line argument?
Module subprocess
splits this up in TWO arguments by replacing the equal sign with a space.
Here is my python script:
import subprocess
pa=['test.bat', '--param:abc=def']
subprocess.run(pa)
Here is the test program test.bat:
@echo off
echo Test.bat
echo Arg0: %0
echo Arg1: %1
echo Arg2: %2
pause
and here the output:
Test.bat
Arg0: test.bat
Arg1: --param:abc
Arg2: def
Press any key to continue . . .
Because the equal sign is gone, the real app will not be started correctly. By the way, this problem also seems to happen when running on linux, with a sh script instead of a bat file.
I understand that removing the equal sign is a 'feature' in certain cases, e.g. with the argparse
module, but in my case I need to keep the equal sign. Any help is appreciated!
Upvotes: 3
Views: 460
Reputation: 141
While this is indeed mainly a batch file hell problem, Python's subprocess implementation detail is also relevant here. So I'd like to add some minor things to @jean-françois-fabre's great answer.
As said above, when using a BAT file directly, you can avoid the argument being split at equal sign by quoting it. And if you don't want the quotes later when you refer to the argument, you can simply use %~1
instead of doing string replacement manually.
E.g. in Test.bat
:
@echo off
echo Test.bat
echo Arg0: %0
echo Arg1: %~1
echo Arg2: %~2
pause
D:\3>Test.bat "abc=def"
Test.bat
Arg0: Test.bat
Arg1: abc=def
Arg2:
Press any key to continue . . .
(Note that it successfully got abc=def
as an argument, and by using %~1
we removed the quotes too.)
Now, this is only half of the problem. The other half is on Python's side: subprocess.run(['Test.bat', 'abc=def'])
will not work because Python won't quote argument with equal sign, and there is no way to force it to do it. (You can't manually add it, Python will escape it to \"
.)
So you have to manually construct the whole string and either use os.system
as said above, or simply pass it (the string, instead of a list) into subprocess.run()
:
subprocess.run('Test.bat "abc=def"')
Also note, never use string (instead of list) in subprocess.run()
on unix-like system. It will cause issues. If you really want to, add shell=True
(you can add it on Windows too).
In summary:
import subprocess
import os
subprocess.run(['Test.bat', 'abc=def']) # BAD
subprocess.run(['Test.bat', '"abc=def"']) # BAD
os.system('Test.bat "abc=def"') # OK
subprocess.run('Test.bat "abc=def"') # OK
subprocess.run('Test.bat "abc=def"', shell=True) # OK
Upvotes: 1
Reputation: 5234
As Jean-Francois said, this is caused by BAT file Hell:
C:\>test.bat -param:abc=def
Test.bat
Arg0: test.bat
Arg1: -param:abc
Arg2: def
Press any key to continue . . .
Powershell does a better job, test.ps1:
write-host "There are a total of $($args.count) arguments"
for ( $i = 0; $i -lt $args.count; $i++ ) {
write-host "Argument $i is $($args[$i])"
}
Running from the DOS prompt:
PS C:\> .\test.ps1 --params:abc=def
There are a total of 1 arguments
Argument 0 is --params:abc=def
Conclusion:
There is nothing wrong with Python.
Because the app that I need to start from Python has a BAT startup script, I have to replace that by a Powershell version *.ps1 script.
Upvotes: 0
Reputation: 352
I just checked, and you can use the shlex
package to parse the string before sending it to the shell:
import subprocess, shlex
pa = shlex.split('test.bat --param:abc=def') # parse string into a list
subprocess.run(pa)
Some info on the shlex
module:
It allows us to split strings using shell-like syntax, which is useful when working with the shell/command line. (docs)
Hope this helps.
Upvotes: 0
Reputation: 140148
Welcome to .bat file hell
To preserve equal sign, you'll have to quote your argument (explained here Preserving "=" (equal) characters in batch file parameters) ('"--param:abc=def"')
, but then subprocess
will escape the quotes
Test.bat
Arg0: test.bat
Arg1: \"--param:abc=def\"
Arg2:
Good old os.system
won't do that
import os
os.system('test.bat "--param:abc=def"')
result
Test.bat
Arg0: test.bat
Arg1: "--param:abc=def"
Arg2:
Damn, those quotes won't go off. Let's tweak the .bat script a little to remove them manually
@echo off
echo Test.bat
echo Arg0: %0
rem those 2 lines remove the quotes
set ARG1=%1
set ARG1=%ARG1:"=%
echo Arg1: %ARG1%
echo Arg2: %2
now it yields the proper result
Test.bat
Arg0: test.bat
Arg1: --param:abc=def
Arg2:
Alternatively, stick to subprocess
and remove quotes AND backslashes.
Upvotes: 1