In Python, with subprocess.Popen, is it possible to pass literal quotes as an argument, when the command and its parameters are in list form?
I'll explain further what I mean. Some commands can have literal quotes in their arguments e.g. I'm trying "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --profile-directory="Profile 1" Some might even require them.
Note that one answer points out that technically it is possible to get Chrome from the command line to launch whatever profile, without passing a literal quote C:\Users\User>"C:\Program Files....\chrome.exe" "--profile-directory=Profile 2"
Nevertheless, i'm asking about passing literal quotes so "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --profile-directory="Profile 1"
For simplicity's sake i'll use calc.exe since it's in the path.
import time
import subprocess
proc=subprocess.Popen("calc.exe"+" "+'--profile-directory="Profile 3"')
proc2=subprocess.Popen(["calc.exe",'--profile-directory="Profile 4"'])
time.sleep(3)
proc.wait()
proc2.wait()
Now look at the difference in the command line as visible in task manager or via wmic.
C:\Users\User>wmic process where caption="calc.exe" get commandline | findstr calc
c:\windows\system32\calc.exe --profile-directory="Profile 3"
c:\windows\system32\calc.exe "--profile-directory=\"Profile 4\""
C:\Users\User>
You can see this from the python interpreter
>>> subprocess.Popen(["c:/windows/system32/calc.exe","abc"+'"'+"def"])
...
>>>
>>> subprocess.run("C:\Windows\System32\wbem\WMIC.exe process where caption=\"calc.exe\" get commandline")
...
c:/windows/system32/calc.exe abc\"def
....
>>>
You see it's sticking a backslash in there.
Some comments regarding some suggestions given.
One suggestion assumes that --profile-directory="Profile 1" is the same as --profile-directory "Profile 1" i.e. the assumption that you can replace the = with a space and chrome will work the same. But that isn't the case. So writing subprocess.Popen(["C:\...\chrome.exe", "--profile-directory", "Profile 3"]) will indeed produce "C:\....\chrome.exe" --profile-directory "Profile 1" but that won't work.. it leads chrome to either not open at all, or to open a browser window that offers profiles to click on. The equals sign is necessary.
Another suggestion does
subprocess.Popen(
" ".join(
[
"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
'--profile-directory="Person 1"',
]
)
That's not passing a list to Popen, that's passing a list to join, and join is converting it to a string.
Another suggestion is
subprocess.Popen('C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe --profile-directory="Profile 3"')
That's using a string. But as you see from my question, I managed it using a string. I'm asking about using a list.
Another suggestion suggested "--profile-directory='Profile 1'"
If I run chrome with --profile-directory="Profile 1" I get a particular profile that I use sometimes. But if running chrome with "--profile-directory='Profile 1'" Then it doesn't load up that profile. It loads up a blank profile. And going to chrome://version shows "'profile 1'" rather than "profile 1" It's like a different profile, like you may as well have said chrome.exe --profile-directory="profile A". And it also creates directories starting with ' like C:\Users\User\AppData\Local\Google\Chrome\User Data\'Profile 1234' that should be removed.
Another suggestion suggested
subprocess.Popen(
[
"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
"--profile-directory=Profile 1",
]
That is interesting because it does "C:\...chrome.exe" "--profile-directory=Profile 1"
And it does infact load chrome with the specified profile. Though it doesn't try to pass literal quotes!
My question asks about when passing literal quotes. It's as if maybe it assumes it's a linux shell and inserts a backslash before it, which in a linux would ensure the quote makes it past the shell and to the program being run. Though i'm not sure it'd even go to the linux shell on linux. e.g. on Windows if I stick a cmd escape character in there like ^ so "--pro^file-directory=Profile 1" then the ^ just gets passed literally. So the cmd shell doesn't intervene.
Why is it that on Windows, subprocess.Popen calls list2cmdline when passed a list, which(and here's the big 'why'), then adds a backslash to any literal double quote within a string, meaning that when using the 'method' of passing a list to to Popen rather than passing a string to it, there is this problem, that you can't pass a literal double quote! So, why does it add that backslash!
I did here a suggestion that looking at argsv in windows vs linux might show a difference. I'm not sure that they would since both implement C.
I don't see why POpen in any situation should behave like Windows needs a backslash inserted more than Linux does.
$ cat ./testargs.py
#!/usr/bin/env python3
import sys
print(sys.argv)
C:\blah>type .\testargsw.py
import sys
print(sys.argv)
in both cases
C:\blah>.\testargsw.py abc\^"def
['C:\\Users\\User\\testargsw.py', 'abc"def']
>.\testargsw.py abc\"def
['C:\\Users\\User\\testargsw.py', 'abc"def']
C:\blah>
$ ./testargs.py abc\"def
['./testargs.py', 'abc"def']
Maybe Windows , specifically the MS C Runtime.. The Code responsible for sending a program's arguments received from the shell, to the main method into argv, is requiring an extra backslash, in a sense because after escaping the double quote, a backslash is then required. (And [here] is put in by the user).
That said, I have heard though that looking at what a shell does on Linux is basically misleading, because a major part of the purpose of the subprocess module is to ensure that you can avoid using a shell entirely.
The script example is perhaps not that relevant(it was just something somebody suggested I check), but my issue is that POpen when passed a list is adding in a backslash as shown by WMIC output(also visible in task manager in the command line column).
added
I spoke to a person that has used python for a long time. They said subprocess was added somewhere in 2.x They still use os.popen(). That takes a string not a list. There have been moves to shift people from os.popen to subprocess.Popen https://docs.python.org/3/library/subprocess.html#replacing-os-popen-os-popen2-os-popen3
An issue with subprocess.Popen in Windows, is it has this list feature, that I think behaves funny.
The easy workaround to that is to not use the list feature of it. To not pass it a list. It's a new feature and not necessary. You can pass it a string.
The question includes an example from the python interpreter and shows how (on windows at least), python adds a backslash to the literal quote.
The person I spoke to pointed out to me two documents that relate to that.
A string is a sequence. A sequence could be a string or list or tuple, though in this document they use the term sequence to just mean list or tuple, and they don't mean string when they say sequence.
https://peps.python.org/pep-0324/
"class Popen(args........."
"args should be a string, or a sequence of program arguments"
It mentions about on unix, shell=True and shell=False
And then it says
"On Windows: the Popen class uses CreateProcess() to execute the child program, which operates on strings. If args is a sequence, it will be converted to a string using the list2cmdline method. Please note that not all MS Windows applications interpret the command line the same way: The list2cmdline is designed for applications using the same rules as the MS C runtime."
Technically a string is a sequence, but that document uses the term sequence in a funny way. But what it means is it's that on Windows, if args is not given a string, but is given a list or tuple, then it uses the list2cmdline method.
Be sure to use print otherwise it uses repr() of the string
>>> print(subprocess.list2cmdline(['a', '"b c"']))
a "\"b c\""
>>>
so that's the function that it's using behind the scenes, on windows, that is inserting a backslash in there.
The guy I spoke to pointed me to this document too
https://bugs.python.org/issue11827
a technical user comments, "list2cmdline() in subprocess is publicly accessible (doesn't begin with underscores) but it isn't documented."
And the point is made there that, let's say they made list2cmdline() private, the fact is that what Popen is doing to the list, in Windows, to get the command line, is undocumented.
So the question then becomes, what is the design trying to do, what is the justification for the insertion of backslash. If a programmer wanted to insert a backslash they could do so. It seeems to me to make more sense then to avoid passing a list to subprocess.POpen.
Windows cmd doesn't even use backslash as an escape character!!!! It uses caret.
C:\Users\User>echo \\
\\
C:\Users\User>echo ^\
\
C:\Users\User>
it's linux eg bash, that uses backslash as an escape character
$ echo \\
\
$
Some executables in windows might want a quote escaped and with a backslash, but then the technical user can do that just as a technical linux user does.
So given that they haven't even documented the "feature" (or bug), how they would justify it, I don't know, but they could start by documenting it!
So I don't understand why passing a list to subprocess.ppopen is adding a backslash?
I could take the list join it with space and pass it as a string to popen, so it won't add a backslash, but as mentioned, that'd be avoiding the question.
In Python, with subprocess.Popen, is it possible to pass literal
quotes as an argument, when the command and its parameters are in list
form?
I think yes, but to elements of argsv, not to the command line..
I'll explain further what I mean. Some commands can have literal
quotes in their arguments e.g. I'm trying "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --profile-directory="Profile 1" Some might even require them.
Note that one answer points out that technically it is possible to get
Chrome from the command line to launch whatever profile, without
passing a literal quote C:\Users\User>"C:\Program Files....\chrome.exe" "--profile-directory=Profile 2"
Nevertheless, i'm asking about passing literal quotes so
"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --profile-directory="Profile 1"
I think there was some confusion, that I might have resolved now.
When running a binary executable in windows(so, an exe, not a bat file) and giving it arguments
People often speak of how linux and windows behave differently..
Windows doesn't separate arguments out, it just dumps stuff to the command line. And there's no concept of space separating out arguments.
Apparently in linux, the command line is parsed by the shell and split into arguments and the arguments are passed by the shell to a POSIX function called execv, which then get passed to main's argsv.
Whereas in windows, there's an aspect of C pertaining to Windows called the MS C Runtime, that parses the command line and splits it into arguments.
So when looking in task manager or from WMIC, at the command line, it's just a dump when it comes to quotes. The quotes aren't some special some literal. But when the MS C Runtime looks at it then the quotes will have that meaning of some special some literal, a special one will not go to argsv, and a literal one will. Usually they are all special, none are literal.
>calc """"""""""^G
>wmic process where caption="calc.exe" get commandline | findstr calc
calc """"""""""G
One we see what's in the command line then one can consider what the arguments are.. what would would go to argsv.
In the case of the two Chrome examples, there's actually no literal quote, in the sense of a literal quote that'd go to argsv. in either example
Example 1
"C:\Program Files(x86)\Google\Chrome\Application\chrome.exe" --profile-directory="Profile 1"
Example 2
"C:\Program Files....\chrome.exe" "--profile-directory=Profile 2"
Looking at "Example 1"
It's not that there's an argument --profile-directory="Profile 1" that has two literal quotes.
The quotes when read by MS CRT(MS C runtime), will just preserve space keeping that "1" as part of the same argument.
So a program that displays argv will show
C:\blah>type w.c
#include <stdio.h>
int main(int argc, char *argv[]) {
int i = 0;
while (argv[i]) {
printf("argv[%d] = %s\n", i, argv[i]);
i++;
}
return 0;
}
C:\blah>w.exe --profile-directory="Profile 1"
argv[0] = w.exe
argv[1] = --profile-directory=Profile 1
C:\blah>
See, no literal quotes there
Infact both different command lines, will produce the same thing going to argv
>w.exe "--profile-directory=Profile 1"
argv[0] = w.exe
argv[1] = --profile-directory=Profile 1
>w.exe --profile-directory="Profile 1"
argv[0] = w.exe
argv[1] = --profile-directory=Profile 1
>
So, how would you even get a literal quote there, well, this link mentions it https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments?view=msvc-170
the find command might have funny parsing, but if you are using find or a windows implementation of grep.. and wanted to look for a quote.. Then, you'd want one of the argsv parameters to contain a quote.
And that's when you'd put a backslash in there
>w.exe \"
argv[0] = w.exe
argv[1] = "
>
The command line would have a backslash.. So that the argv would have a literal quote.
And that's what passing a list to subprocess.Popen is all about..
You are putting in what you want to be in the argv.
It is then producing the command line that would result in those things being in the argv.
That's why it's inserting a backslash before the double quotes!
So..
When I trying to use list, I was under the mistaken impression that I needed to get literal quotes into the arguments. And I thought I was forming the command line. Both of those premises were wrong.
I suppose if a python script has two options.. one to run an executable for linux users, and one to run an executable for windows users. The command line might be different, because on the one hand, the windows command line (either what's typed or it, what windows dumps as it), and on the other hand, what's typed on the linux command line.
And with using a list where each element is an element of argsv, it lets the library do the work, in the case of windows, using the list2cmd function, to produce whatever the command line would be in order for the MS C Runtime to produce the result you want in argsv.
side note
In the testargs.py file used in the question, it'd not give expected output https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments?view=msvc-170 for some strings e.g. `"ab\"c" "\\" d` because it is giving the repl representation Adjust it as follows.
user#comp:~# cat ./testargs.py
#!/usr/bin/env python3
import sys
print(sys.argv)
user#comp:~# cat ./testargs_corrected.py
#!/usr/bin/env python3
import sys
# print(sys.argv) would behaves unintended for "ab\"c" "\\" d
# (it's escaping strings for output in an repr)
# so not using print(sys.argv), using below line instead.
# '//' that it shows, means / 'cos '//' is a single character in python
# but much clearer to show it not in the repl form.
for n, a in enumerate(sys.argv): print(f"argv[{n}] = {a}")
user#comp:~# ./testargs.py "ab\"c" "\\" d
['./testargs.py', 'ab"c', '\\', 'd']
user#comp:~#
user#comp:~# ./testargs_corrected.py "ab\"c" "\\" d
argv[0] = ./testargs_corrected.py
argv[1] = ab"c
argv[2] = \
argv[3] = d
user#comp:~#
Should do the trick (might wanna pass shell=True to Popen if it doesn't):
subprocess.Popen(["C:\Program Files (x86)\Google\Chrome\Application\chrome.exe", "--profile-directory", "Profile 3"]);
This is possible because --some-flag="some value" is the same as --some-flag "some value"
ChRomE solution (working, omg):
import subprocess
subprocess.Popen(
[
"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
"--profile-directory=Profile 1",
]
)
my subprocess.call problem is that my shortcut target is with extra INI file which is LIV2.INI and my exe file should run whit it . and my target link in shortcut looks like this
"C:\Program Files (x86)\AMO\EXE\PROGRAM LIVE 2.exe" LIV2.INI
i tried this
subprocess.call('"C:\Users\admin\Desktop\PROGRAM LIVE 2.exe" LIV2.INI')
and i tried this
subprocess.call('C:\Users\admin\Desktop\PROGRAM LIVE 2.exe LIV2.INI')
and i still get error that the ini file missing ? How can i fix this :)
THank you in advance
ERROR : INI FILE Missing or Wrong Name
Please also edit your question to actually include the error since you will get a syntax error, not an error that the ini file is missing.
You have two issues here, first you have a syntax error since "\Us" is not a valid string in python. \u marks the start of a Unicode escape sequence and the character S is not a valid unicode escape key. You can fix this by using double \\ to escape the \ character and tell python you want your string to include a \ and not use it as the start of a escape sequence.
Secondly, subprocess.call excpects a list, not a string (unless you set shell=True; but don't do that, since it means you have to manually escape things which you have already discovered is hard). The first element of the list
is the executable to run and the rest are command line arguments. For example if you wanted to run pythoneand print "hello world" you would type:
subprocess.call(['python', '-c', 'print ("hello world")'])
Notice the missing quotes around the python string? You don't need those since the command line arguments are passed in raw and no shell will attempt to split them if you don't include quotes.
Putting it all together creates something like this:
subprocess.call(['C:\\Users\\admin\\Desktop\\PROGRAM LIVE 2.exe', 'LIV2.INI'])
Notice the double backslashes and how each command line argument is its own list element.
I need to transform a string containing single and double quotes and newline characters for use in a system call. Consider the following input string:
"""one'two\nthree"four"""
This should be transformed to the following output string:
"$'one\'two\nthree\"four'"
So that it can be submitted as a message argument in a git commit command:
git commit --message=$'one\'two\nthree\"four'
The odd syntax with the leading $ and surrounding single quotes ' is a bash construct described in the bash manpage in the section on quoting (search for QUOTING). I have tried many python functions including str.replace, re.sub, json.dumps, repr, and str.encode('unicode-escape'). But none have yielded the required transformation. It seems that, in this case, python is too high-level for its own good. Suggestions on how to proceed will be very gratefully received.
The system call itself will be made using code like this (omitting the try block for clarity):
import subprocess
call = ["git", "commit", "--message", "'one\'two\nthree\"four'"]
cpi = subprocess.run(call)
I may also use a git library of some description, but I have not done my homework yet for that.
Note: the unnecessary $ character in the last item in the above call list has been removed.
Your wanted command is erroneous at the moment, it is not a valid Python string since a right " is missing, it should be:
"$'one\'two\nthree\"four'"
This is easily constructed by a simple .format call:
>>> "$'{}'".format("""one'two\nthree"four""") == "$'one\'two\nthree\"four'"
True
I have a script that I run from the command line which I would like to be able to pass string arguments into. As in
script.py --string "thing1\nthing2"
such that the program would interpret the '\n' as a new line. If string="thing1\nthing2" I want to get
print string
to return:
thing1
thing2
rather than thing1\nthing2
If I simply hard-code the string "thing1\nthing2" into the script, it does this, but if it's entered as a command line argument via getopt, it doesn't recognize it. I have tried a number of approaches to this: reading in the cl string as r"%s" % arg, various ways of specifying it on the commandline, etc, and nothing seems to work. Ideas? Is this completely impossible?
From https://stackoverflow.com/a/4918413/478656 in Bash, you can use:
script.py --string $'thing1\nthing2'
e.g.
$ python test.py $'1\n2'
1
2
But that's Bash-specific syntax.
This is really a shell question since the shell does all the command parsing. Python doesn't care what's happening with that and only gets what comes through in the exec system call. If you're using bash, it doesn't do certain kinds of escaping between double quotes. If you want things like \n, \t, or \xnn to be escaped, the following syntax is a bash extension:
python test.py $'thing1\nthing2'
Note that the above example uses single quotes and not double quotes. That's important. Using double quotes causes different rules to apply. You can also do:
python test.py "thing1
thing2"
Here's some more info on bash quoting if you're interested. Even if you're not using bash, it's still good reading:
http://mywiki.wooledge.org/Quotes
This one is relatively simple and I am surprised no one has said it.
In your python script just write the following code
print string.replace("\\n", "\n")
and you will get the string printed with the new line and not the \n.
I am trying to run a program from the command prompt in windows. I am having some issues. The code is below:
commandString = "'C:\Program Files\WebShot\webshotcmd.exe' //url '" + columns[3] + "' //out '"+columns[1]+"~"+columns[2]+".jpg'"
os.system(commandString)
time.sleep(10)
So with the single quotes I get "The filename, directory name, or volume label syntax is incorrect." If I replace the single quotes with \" then it says something to the effect of "'C:\Program' is not a valid executable."
I realize it is a syntax error, but I am not quite sure how to fix this....
column[3] contains a full url copy pasted from a web browser (so it should be url encoded). column[1] will only contain numbers and periods. column[2] contains some text, double quotes and colons are replaced. Mentioning just in case...
Thanks!
Windows requires double quotes in this situation, and you used single quotes.
Use the subprocess module rather than os.system, which is more robust and avoids calling the shell directly, making you not have to worry about confusing escaping issues.
Dont use + to put together long strings. Use string formatting (string %s" % (formatting,)), which is more readable, efficient, and idiomatic.
In this case, don't form a long string as a shell command anyhow, make a list and pass it to subprocess.call.
As best as I can tell you are escaping your forward slash but not your backslashes, which is backwards. A string literal with // has both slashes in the string it makes. In any event, rather than either you should use the os.path module which avoids any confusion from parsing escapes and often makes scripts more portable.
Use the subprocess module for calling system commands. Also ,try removing the single quotes and use double quotes.