Backslashes combined with eval
can be really tricky.
First consider these two strings:
>>> "\m"
"\\m"
>>> "\\m"
"\\m"
They both produce the same output. In the first case first backslash tries to consume next character, but m
is not a special character. Therefore, backslash remains a single backslash (raw: \m
). In normal strings backslashes are escaped, so Python escapes it for you.
In the second case first backslash escapes second backslash, so we have a single backslash (raw: \m
) which Python escapes for you.
Now, let's move to more backslashes. Consider a raw string \a
. To get a single backslash we need to escape it in out string, so we need to evaluate a raw string \\a
>>> eval(r'"\\a"')
'\\a'
Which is indeed raw \a
. However, if our initial string is not raw, Python performs first escaping before calling eval
which gives
>>> eval('"\\a"')
'\x07'
Not what we need. What happened here? First, Python takes out string "\\a"
and uses first backslash to escape second backslash. We end up with string "\a"
which gets evaluated. Python looks for special characters again and treats \a
as a special ASCII character BEL
.
If wee want to have the same output is in the first example we need four backslashes. First and second are consumed to one backslash, third and fourth to another backslash, so we end up with \\a
which is evaluated to raw \a
>>> eval('"\\\\a"')
'\\a'
Exactly what we need.
You should never leave odd number of backslashes because it can cause unexpected things. e.g.
>>> eval("{'a':'\\\\\my_host\\my_path'}")
{'a': '\\\\my_host\\my_path'}
Apparently it works fine, but what if the name started with b
>>> eval("{'a':'\\\\\by_host\\by_path'}")
{'a': '\\\x08y_host\x08y_path'}
Ugh...
The correct way to do this is:
>>> eval("{'a':'\\\\\\\\by_host\\\\by_path'}")
{'a': '\\\\by_host\\by_path'}
After double escaping, each four backslashes are merged into one, so our path is now \\by_host\by_path
as we need.