双重逃脱角色

EEP*_*EEP 6 python regex escaping python-2.7

我目前正在尝试执行以下操作:

cmd = r'sudo sed -irn "1!N; s/<ip>127.0.0.1<\/ip>(\n.*4000.*)/<ip>0.0.0.0<\/ip>\1/" /usr/something.conf'
subprocess.Popen(cmd)
Run Code Online (Sandbox Code Playgroud)

然而,Popen抱怨说\\ 1是无效的引用.在pdb中检查它我看到了,

'sudo sed -irn "1!N; s/<ip>127.0.0.1<\\/ip>(\\n.*4000.*)/<ip>0.0.0.0<\\/ip>\\1/" /usr/something.conf'
Run Code Online (Sandbox Code Playgroud)

好像python正在添加一个额外的\.有没有办法防止这种情况,以便我可以像使用Popen一样运行命令?

此外,为简化起见,我将其排除在示例之外,但实际上这是在传递给Popen之前将其包装在SSH调用中,所以是的......它确实需要用Popen和完成sed.

这里参考的是字符串经过的完整步骤链...

def _formatCmd(cmdString, host=None, user=None, keyfile=None):
    cmd = []
    if host:
        cmd.append('ssh')
        keyfile = keyfile or getKeyFile()
        if keyfile:
            cmd.append('-i')
            cmd.append(keyfile)
        cmd.append("%s@%s" % (user, host))
        cmd.append(cmdString)
    else:
        cmd += cmdString.split()

    return cmd


def runCmd(host, user, cmd, timeout=None, cleanup=False):
    try:
        cmd = _formatCmd(cmd, host=host, user=user)
    except:
        pass

    #create cmd and add it to list of running cmds
    proc = Popen(cmd, stdout=PIPE, stderr=PIPE)
    runningCmds[proc.pid] = proc

    t = threading.Timer(timeout, proc.kill)
    t.start()
    stdout, stderr = proc.communicate()
    t.cancel()

    del runningCmds[proc.pid]
    return (proc.returncode, stdout, stderr)

cmd = r'sudo sed -irn "1!N; s/<ip>127.0.0.1<\/ip>(\n.*4000.*)/<ip>0.0.0.0<\/ip>\1/" /usr/something.conf'
runCmd('1.1.1.1', 'username', cmd)
Run Code Online (Sandbox Code Playgroud)

返回的确切错误消息是:

sed: -e expression #1, char 59: invalid reference \\1 on `s' command's RHS
Run Code Online (Sandbox Code Playgroud)

aca*_*lon 1

问题在于 shell 正在执行自己的解释/转义。我在使用 cygwin 时发现了类似的东西(在 cygwin 的情况下,shell 是 bash)。

事实上,您收到错误:

sed: -e expression #1, char 59: invalid reference \\1 on `s' command's RHS
Run Code Online (Sandbox Code Playgroud)

意味着()问题出在括号上,而不是 的转义\1。本质上,它无法找到该组,因此您需要转义括号\(...\)

要找到原因,技巧是使用echo调试发送的内容:

  1. 将正则表达式简化为一组和匹配。就像是:

    s/(one)/\1\1/
    
    Run Code Online (Sandbox Code Playgroud)

    其中输入字符串为“one”,预期输出为oneone

  2. 更改您的cmdtoecho以便传递给 shell 的内容是echo s/(one)/\1\1/

  3. 我猜你会看到类似的东西bash: syntax error near unexpected token '('。这给了我们线索。基本上,我们需要转义括号。

  4. 所以现在尝试一下echo s/\(one\)/\1\1。就我而言,我看到类似的东西

    s/(one)/\1\1
    
    Run Code Online (Sandbox Code Playgroud)
  5. 如果运气好的话,你应该能够将它应用到你有问题的表达上。

最简单的方法可能是使用强引号(将命令放在单引号中),它告诉 bash 不要解释字符串,尽管您可能仍然需要转义括号()

顺便说一句,对于 cygwin 来说,事情需要两次转义,所以实际正确的表达式是:

sed s/\\\(one\\\)/\\1\\1/
Run Code Online (Sandbox Code Playgroud)

所以

echo one | sed s/\\\(one\\\)/\\1\\1/
Run Code Online (Sandbox Code Playgroud)

给出

oneone
Run Code Online (Sandbox Code Playgroud)

使用强引用的等效项是:

 echo one | sed 's/\(one\)/\1\1/'
Run Code Online (Sandbox Code Playgroud)