【问题标题】:os.system(cmd) and subprocess.call(cmd, shell=True) don't execute the same as CMD.EXEos.system(cmd) 和 subprocess.call(cmd, shell=True) 的执行方式与 CMD.EXE 不同
【发布时间】:2018-04-01 19:26:12
【问题描述】:

我正在尝试将一个长而复杂的 Windows 批处理文件转换为 Python。

除了一些微妙的问题,我怀疑这与引用有关,但不能完全弄清楚。

在批处理文件中,这可以正常工作:

Reg.exe add "HKCR\7-Zip.zip\shell\open\command" /ve /t REG_SZ /d "\"C:\Program Files\7-Zip\7zFM.exe\" \"%%1\"" /f

此 Python 代码旨在但不会做同样的事情:

import os, subprocess

cmd = r'Reg.exe add "HKCR\7-Zip.zip\shell\open\command" /ve /t REG_SZ /d "\"C:\Program Files\7-Zip\7zFM.exe\" \"%%1\"" /f'
#os.system(cmd)
subprocess.call(cmd, shell=True)

请注意,(原始)字符串 cmd 和批处理文件完全相同

os.system() 和 subprocess.call() 的结果相同 - 没有错误(Reg.exe 表示一切正常),但对系统的影响不同。

在我的测试中,批处理文件将归档器 7z 配置为自行打开 .ZIP 文件(正确结果)。

Python 代码导致 7z 打开 .ZIP 所在的文件夹(错误)。

如何让 Python 完成批处理文件的工作?

【问题讨论】:

  • 你可以试试cmd = ['Reg.exe','add',r'HKCR\7-Zip.zip\shell\open\command','/ve','/t','REG_SZ','/d',r'\"C:\Program Files\7-Zip\7zFM.exe\" \"%%1\"','/f']
  • 这里有需要使用 shell 的原因吗?你没有使用任何 shell 功能,只是执行带有一些参数的命令,所以你只是想弄清楚如何用两层引用而不是这样来对抗......
  • (实际上,将那三层引用而不是一层,因为你也得到了不可避免的注册表字符串引用......)
  • 加倍百分比仅在批处理脚本中转义,而不是在交互式或/c 命令中。但是,转义百分比在这里不是问题,因为它没有配对。只需使用一个百分比。 (CMD 没有完全可靠的方法来转义命令行或/c 命令上的多个百分比。它的“^”转义字符通常会破坏变量名匹配,但如果变量名以“^”开头,则不会。)
  • 另外,不要使用 HKCR 设置值。这是一个用于阅读的动态视图。写入 HKCR 的结果取决于“[HKLM|HKCU]\Software\Classes”中已经定义的内容。添加设置时,应在 HKLM 或 HKCU 注册表配置单元中明确定义。

标签: python windows


【解决方案1】:

好的,在黑暗中拍摄:

我会删除shell=True 并使用参数列表传递给subprocess。报价将自动处理:

cmd = ['Reg.exe','add',r'HKCR\7-Zip.zip\shell\open\command','/ve','/t','REG_SZ','/d',r'"C:\Program Files\7-Zip\7zFM.exe" "%1"','/f']
rc = subprocess.call(cmd)

还要检查subprocess.call的返回码

如果你想“自动”处理几个这样的命令,我建议使用shlex.split,我并不是说它会解决所有问题,但它可以很好地处理引号(保护引用的参数,带引号嵌套):

import shlex
text = r"""Reg.exe add "HKCR\7-Zip.zip\shell\open\command" /ve /t REG_SZ /d "\"C:\Program Files\7-Zip\7zFM.exe\" \"%%1\"" /f"""

print([x.replace("%%","%") for x in shlex.split(text)])  # %% => % in listcomp, add more filters if needed

结果:

['Reg.exe', 'add', 'HKCR\\7-Zip.zip\\shell\\open\\command', '/ve', '/t', 'REG_SZ', '/d', '"C:\\Program Files\\7-Zip\\7zFM.exe" "%1"', '/f']

与原始前缀相同的是:

['Reg.exe', 'add', r'HKCR\7-Zip.zip\shell\open\command', '/ve', '/t', 'REG_SZ', '/d', r'"C:\Program Files\7-Zip\7zFM.exe" "%1"', '/f']

很接近吧? :)

【讨论】:

  • @eryksun 谢谢。顺便说一句(不相关)我已经对我的另一个 Popen/stdin 问题给予了赏金。你可能有兴趣:)
  • 是的,这行得通(赞成答案)。但是,我不清楚您用于将原始命令行转换为新命令行的确切规则。我有数千行批处理文件(并非全部涉及 reg.exe)要处理;我需要一种算法——在这里手动调整每个都行不通。
  • 例如,有些字符串是原始的,有些不是。为什么?您在 7zFM.exe 的路径之前删除“\”的确切原因尚不清楚,也不清楚引用或更改为 %1 周围的单个 %。
  • raw 对于避免加倍字符串文字中的反斜杠很有用。如果没有反斜杠,则不需要。看看我的编辑,应该是一个好的开始。
  • 这确实是一个好的开始。不幸的是,我没有耐心分析批处理文件以找出可能需要的其他过滤器。我找到了解决方案 - 发布我自己的答案。
【解决方案2】:

Jean-François Fabre 的回答很好,而且可能是最 Pythonic 的回答。

不幸的是,我没有耐心分析批处理文件以找出可能需要的任何其他过滤器,所以我想出了这个解决方案,它不管语法、引用和转义序列如何都可以工作批处理文件行:

def do(command):
    '''Executes command at Windows command line.'''

    import os, subprocess, uuid

    batchFile = open("temp_" + str(uuid.uuid4()) + ".bat", 'w')
    batchFile.write(command)
    batchFile.close()
    subprocess.call(batchFile.name, shell=True)
    os.remove(batchFile.name)

它所做的只是创建一个单行批处理文件,然后运行它。蛮力。

有点慢,因为每次都有创建、调用、删除一行批处理文件的开销。

这是一个更快的版本,它使用所有命令行创建一个大批处理文件。每当您使用defer=False 调用它时,它都会执行迄今为止的所有命令:

# thanks to https://stackoverflow.com/questions/279561/what-is-the-python-equivalent-of-static-variables-inside-a-function
def static_vars(**kwargs):
    def decorate(func):
        for k in kwargs:
            setattr(func, k, kwargs[k])
        return func
    return decorate

@static_vars(batchFile=None)
def do(command, defer=True):
    '''Executes command at Windows command line.
       Runs immediately, including all previously deferred commands, if defer is not True
    '''
    import os, subprocess, uuid

    if do.batchFile == None:
        do.batchFile = open("temp_" + str(uuid.uuid4()) + ".bat", 'w')

    do.batchFile.write(command + "\n") # append to file

    if not defer:
        do.batchFile.close()
        subprocess.call(do.batchFile.name, shell=True)
        os.remove(do.batchFile.name)
        do.batchFile = None

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-03-23
    • 1970-01-01
    • 2011-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-03
    相关资源
    最近更新 更多