Reputation: 43
I'm trying to execute a shell command through python. The command is like the following one:
su -c "lftp -c 'open -u user,password ftp://127.0.0.1; get ivan\'s\ filename.pdf' " someuser
So, when I try to do it in python:
command = "su -c \"lftp -c 'open -u user,password ftp://127.0.0.1; get ivan\'s\ filename.pdf' \" someuser"
os.system(command)
Or:
command = subprocess.Popen(["su", "-c", "lftp -c 'open -u user,password ftp://127.0.0.1; get ivan\'s\ filename.pdf'", "someuser"])
I get the following error:
bash: -c: line 0: unexpected EOF while looking for matching `''
bash: -c: line 1: syntax error: unexpected end of file
Referred to: ivan\'s single quote.
I know there are a lot of single/double quotes in that but how can I escape this?
Thanks in advance!
EDIT: THIS WORKED FOR ME:
subprocess.call(["su","-c",r"""lftp -c "open -u user,password ftp://127.0.0.1; get ivan\'s\ filename.pdf" """, "someuser"])
Thank you all very much!
Upvotes: 3
Views: 6789
Reputation: 2785
If you printed your test string you would notice that it results in the following:
su -c "lftp -c 'open -u user,password ftp://127.0.0.1; get ivan's\ filename.pdf' " someuser
The problem is that you need to escape the slash that you use to escape the single quote in order to keep Python from eating it.
command = "su -c \"lftp -c 'open -u user,password ftp://127.0.0.1; get ivan\\'s\\ filename.pdf' \" someuser"
will get the backslash across, you will then get an error from lftp instead...
This works:
command = "su -c \"lftp -c \\\"open -u user,password ftp://127.0.0.1; get ivan\\'s\\ filename.pdf\\\" \" someuser"
(It uses (escaped) double quotes instead, to ensure that the shell started by su still interprets the escape sequences)
(os.system(a)
effectively does subprocess.call(["sh","-c",a])
, which means that sh
sees su -c "lftp -c 'open -u user,password ftp://127.0.0.1; get ivan's\ filename.pdf' " someuser
(for the original one). It does escape sequence processing on this and sees an unclosed single quote (it is initially closed by ivan'
), resulting in your error). Once that is fixed, sh
calls su, which in turn starts up another instance of sh
doing more escape processing, resulting in the error from lftp (since sh
doesn't handle escape sequences in the single quotes)
subprocess.call()
or curl
are better ways to implement this - curl
will need much less escaping, you can use curl "ftp://user:[email protected]/ivan's filename.pdf" on the command line, some more escaping is needed for going via
su -cand for python.
sudoinstead of
su` also results in less escaping being needed....
If you want to use subprocess.call()
(which removes one layer of shell), you can use
subprocess.call(["su","-c","lftp -c \\\"open -u user,password ftp://127.0.0.1; get ivan\\'s\\ filename.pdf\\\"", "someuser"])
(The problem is that python deals with one level of escaping, and the sh -c
invoked from su
with the next layer... This results in quite an ugly command...) (different quotes might slightly reduce that...)
Using r""
can get rid of the python level escape processing: (needing only the shell level escapes) (Using triple quotes to allow quotes in the string)
subprocess.call(["su","-c",r"""lftp -c \"open -u user,password ftp://127.0.0.1; get ivan\'s\ filename.pdf\"""", "someuser"])
Adding a space allows for stripping the shell escapes, since lftp
doesn't seem to need the filename escaped for the spaces and single quote.
subprocess.call(["su","-c",r"""lftp -c "open -u user,password ftp://127.0.0.1; get ivan's filename.pdf" """, "someuser"])
This results in the eventual lftp
ARGV being
["lftp","-c","open -u user,password ftp://127.0.0.1; get ivan's filename.pdf"]
For curl instead (it still ends up bad due to the su
being involved):
subprocess.call(["su","-c",r"""curl "ftp://user:[email protected]/ivan's filename.pdf" """, "someuser"])
Upvotes: 3
Reputation: 8589
Using subprocess.call() is the best and more secure way to perform this task.
Here's an example from the documentation page:
subprocess.call(["ls", "-l"]) # As you can see we have here the command and a parameter
About the error I think it is something related to the spaces and the ' charachter.
Try using string literals (pay attention to the r before the string, also be sure that the command is 100% matching the one you use in BASH):
r"My ' complex & string"
So, in your case:
command = subprocess.Popen(["su", "-c", r"lftp -c 'open -u user,password ftp://127.0.0.1; get ivan's filename.pdf'", "someuser"])
Upvotes: 1