使用os.path.expandvars
我可以在字符串中扩展环境变量,但需要注意:“格式错误的变量名称和对不存在的变量的引用保持不变”(强调我的)。此外,os.path.expandvars
也会扩展转义\$
。
我想以类似bash的方式扩展变量,至少在这两点上。比较:
import os.environ
import os.path
os.environ['MyVar'] = 'my_var'
if 'unknown' in os.environ:
del os.environ['unknown']
print(os.path.expandvars("$MyVar$unknown\$MyVar"))
给my_var$unknown\my_var
:
unset unknown
MyVar=my_var
echo $MyVar$unknown\$MyVar
给出my_var$MyVar
,这就是我想要的。
答案 0 :(得分:3)
以下实现与os.path.expandvars
保持完全兼容,但通过可选参数提供了更大的灵活性:
import os
import re
def expandvars(path, default=None, skip_escaped=False):
"""Expand environment variables of form $var and ${var}.
If parameter 'skip_escaped' is True, all escaped variable references
(i.e. preceded by backslashes) are skipped.
Unknown variables are set to 'default'. If 'default' is None,
they are left unchanged.
"""
def replace_var(m):
return os.environ.get(m.group(2) or m.group(1), m.group(0) if default is None else default)
reVar = (r'(?<!\\)' if skip_escaped else '') + r'\$(\w+|\{([^}]*)\})'
return re.sub(reVar, replace_var, path)
以下是一些调用示例:
>>> expandvars("$SHELL$unknown\$SHELL")
'/bin/bash$unknown\\/bin/bash'
>>> expandvars("$SHELL$unknown\$SHELL", '')
'/bin/bash\\/bin/bash'
>>> expandvars("$SHELL$unknown\$SHELL", '', True)
'/bin/bash\\$SHELL'
答案 1 :(得分:2)
试试这个:
re.sub('\$[A-Za-z_][A-Za-z0-9_]*', '', os.path.expandvars(path))
正则表达式应匹配任何有效的变量名称,如this answer所示,并且每个匹配将替换为空字符串。
编辑:如果您不想替换转义的变量(即\$VAR
),请在正则表达式中使用否定的lookbehind断言:
re.sub(r'(?<!\\)\$[A-Za-z_][A-Za-z0-9_]*', '', os.path.expandvars(path))
(表示匹配不应以\
开头)。
编辑2:让它成为一个功能:
def expandvars2(path):
return re.sub(r'(?<!\\)\$[A-Za-z_][A-Za-z0-9_]*', '', os.path.expandvars(path))
检查结果:
>>> print(expandvars2('$TERM$FOO\$BAR'))
xterm-256color\$BAR
变量$TERM
会扩展到其值,不存在的变量$FOO
会扩展为空字符串,并且不会触及\$BAR
。
答案 2 :(得分:1)
替代解决方案 - 正如@HuStmpHrrr所指出的那样 - 是让bash
评估你的字符串,这样你就不必在python中复制所有想要的bash功能。
不如我给出的其他解决方案有效,但它非常简单,这也是一个不错的功能:)
>>> from subprocess import check_output
>>> s = '$TERM$FOO\$TERM'
>>> check_output(["bash","-c","echo \"{}\"".format(s)])
b'xterm-256color$TERM\n'
P.S。谨防逃离"
和\
:您可能希望将\
替换为\\
,将"
替换为\"
s
在致电check_output
答案 3 :(得分:1)
这是一个使用原始expandvars
逻辑的解决方案:暂时将os.environ
替换为使未知变量为空字符串的代理对象。请注意defaultdict
无效,因为os.environ
对于转义问题,您可以将r'\$'
替换为保证不在字符串中且不会展开的值,然后将其替换回来。
class EnvironProxy(object):
__slots__ = ('_original_environ',)
def __init__(self):
self._original_environ = os.environ
def __enter__(self):
self._original_environ = os.environ
os.environ = self
return self
def __exit__(self, exc_type, exc_val, exc_tb):
os.environ = self._original_environ
def __getitem__(self, item):
try:
return self._original_environ[item]
except KeyError:
return ''
def expandvars(path):
replacer = '\0' # NUL shouldn't be in a file path anyways.
while replacer in path:
replacer *= 2
path = path.replace('\\$', replacer)
with EnvironProxy():
return os.path.expandvars(path).replace(replacer, '$')
答案 4 :(得分:0)
我遇到了同样的问题,但我会提出一种不同的,非常简单的方法。
如果我们看一下&#34;逃避角色的基本含义&#34; (因为它们是在打印机设备中启动的),目的是告诉设备&#34;做一些不同的事情来接下来的事情&#34;。这是一种离合器。在我们的特殊情况下,我们唯一的问题是当我们有两个角色时,我们会遇到这样的问题。和&#39; $&#39;在序列中。
不幸的是,我们无法控制标准的os.path.expandvars,因此字符串传递了lock,stock和barrel。然而,我们可以做的是愚弄这个功能,以便它无法识别&#39; $&#39;在这种情况下!最好的方法是用一些任意的&#34;实体&#34;替换$。然后将其转换回来。
def expandvars(value):
"""
Expand the env variables in a string, respecting the escape sequence \$
"""
DOLLAR = r"\$"
escaped = value.replace(r"\$", r"\%s" % DOLLAR)
return os.path.expandvars(escaped).replace(DOLLAR, "$")
我使用了HTML实体,但任何合理的不可能的序列都可以(随机序列可能更好)。我们可能会想象这种方法会产生不必要的副作用的情况,但它们应该不太可能忽略不计。
答案 5 :(得分:0)
我对各种答案感到不满意,需要更复杂一点来处理更多边缘情况,例如任意数量的反斜杠和$ {}样式变量,但不想支付bash eval的成本。这是我的基于正则表达式的解决方案:
#!/bin/python
import re
import os
def expandvars(data,environ=os.environ):
out = ""
regex = r'''
( (?:.*?(?<!\\)) # Match non-variable ending in non-slash
(?:\\\\)* ) # Match 0 or even number of backslash
(?:$|\$ (?: (\w+)|\{(\w+)\} ) ) # Match variable or END
'''
for m in re.finditer(regex, data, re.VERBOSE|re.DOTALL):
this = re.sub(r'\\(.)',lambda x: x.group(1),m.group(1))
v = m.group(2) if m.group(2) else m.group(3)
if v and v in environ:
this += environ[v]
out += this
return out
# Replace with os.environ as desired
envars = { "foo":"bar", "baz":"$Baz" }
tests = { r"foo": r"foo",
r"$foo": r"bar",
r"$$": r"$$", # This could be considered a bug
r"$$foo": r"$bar", # This could be considered a bug
r"\n$foo\r": r"nbarr", # This could be considered a bug
r"$bar": r"",
r"$baz": r"$Baz",
r"bar$foo": r"barbar",
r"$foo$foo": r"barbar",
r"$foobar": r"",
r"$foo bar": r"bar bar",
r"$foo-Bar": r"bar-Bar",
r"$foo_Bar": r"",
r"${foo}bar": r"barbar",
r"baz${foo}bar": r"bazbarbar",
r"foo\$baz": r"foo$baz",
r"foo\\$baz": r"foo\$Baz",
r"\$baz": r"$baz",
r"\\$foo": r"\bar",
r"\\\$foo": r"\$foo",
r"\\\\$foo": r"\\bar",
r"\\\\\$foo": r"\\$foo" }
for t,v in tests.iteritems():
g = expandvars(t,envars)
if v != g:
print "%s -> '%s' != '%s'"%(t,g,v)
print "\n\n"