2

i have built a small script that runs a simple shell utility called imapsync, with a bunch of variables taken from a dictionary, the command is as follows:

os.system("imapsync --host1 %s --user1 %s --password1 '%s' --host2 %s --user2 %s --password2 '%s' --ssl1 --no-modulesversion --ssl2" % (fromHost, emails, passwords, toHost, emails, passwords))

the deal is that passwords often contain special characters, example: djDJS*^%%%^&)

this imapsync tool allows such characters if enclosed in single quotes: 'djDJS*^%%%^&)'

what I am trying to achieve is post the single quotes in the command, itself.. I tried "'", backquotes - ``, escaped quotes - \'\', enclosing the command in single quotes, nothing worked thus far

Florin
  • 113
  • 1
  • 1
  • 9
  • How do you determine, that the single quotes are not escaped if you use for example double backslashes? – kalehmann Sep 03 '18 at 22:51
  • the command outputs something such as: "expecting matching ( character, exiting..." – Florin Sep 03 '18 at 22:57
  • 1
    Possible duplicate of [How to escape os.system() calls?](https://stackoverflow.com/questions/35817/how-to-escape-os-system-calls) – Amadan Sep 03 '18 at 23:02
  • 1
    Do you really have to use `os.system()`? Could use `subprocess.call()` instead, so that you can pass an array of arguments rather than a string that needs parsing? – Barmar Sep 03 '18 at 23:14
  • See also https://stackoverflow.com/questions/4256107/running-bash-commands-in-python – tripleee Sep 04 '18 at 11:32

2 Answers2

0

After looking through the documentation of imapsync, I found the recommendation to enclose passwords in double quotes within single quotes to avoid common problems.

Since you already start the string with double quotes, you have to escape the double quotes around your password with a backslash \".

There are also two things you could do to make your code even better. First, you can use .format syntax for string formatting instead of the old % syntax.

Second replace os.system with subprocess.Popen. This allows you to split your command string into a list of all arguments, which looks more clear.

Your new code would look like

import subprocess

args = [
  "imapsync",
  "--host1",
  fromHost,
  "--user1",
  emails,
  "--password1",
  "'\"{}\"'".format(passwords),
  "--host2",
  toHost,
  "--user2",
  emails,
  "--password2",
  "'\"{}\"'".format(passwords),
  "--ssl1",
  "--no-modulesversion",
  "--ssl2"
]

p = subprocess.Popen(args, stdout=subprocess.PIPE)

output = p.communicate()[0]

print(output)

In this example Popen.communicate is used to gather the output of the imapsync command as a string. The communicate method returns a tuple with the outputs of the subprocess to stdout and stderr streams.

If you also want to read the output to stderr from the subprocess, change the code as following:

p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

output, errors = p.communicate()

print(output)
print(errors)
kalehmann
  • 4,821
  • 6
  • 26
  • 36
-1

The best format for passing string parameters in Python is to use the format string method. You could do something like this:

line_command = "imapsync --host1 {fromHost} --user1 {emails} --password1 '\"{passwords}\"' --host2 {toHost} --user2 {emails} --password2 '\"{passwords}\"' --ssl1 --no-modulesversion --ssl2".format(fromHost=fromHost, emails=emails, passwords=passwords, toHost=toHost)
os.system(line_command)
  • This does not answer the OP's question about escaping. `%` and `.format` are equivalent, the way you're using it. – Amadan Sep 05 '18 at 04:33