【问题标题】:subprocess.Popen output: how to overcome the differences with commandline executionsubprocess.Popen 输出:如何克服命令行执行的差异
【发布时间】:2015-10-23 09:18:25
【问题描述】:

我正在编写一个 Python 脚本来比较两个文件。因此,我选择使用grep 作为外部程序,启动以下两个命令:

grep -Fvf content1.txt content2.txt
grep -Fvf content2.txt content1.txt

从这些命令的结果给我的差异,我可以通过计算行数来提取差异的数量。

为了在 Python 脚本中执行此操作,我将这些 grep 命令嵌入到 subprocess.Popen() 函数中:

try:
    output1, errors1 = subprocess.Popen(
        ["c:\\cygwin\\bin\\grep", "-Fvf", "content1.txt", "content2.txt"],
        shell=True, stdout=PIPE, stderr=PIPE).communicate()

    output2, errors2 = subprocess.Popen(
        ["c:\\cygwin\\bin\\grep", "-Fvf", "content2.txt", "content1.txt"],
        shell=True, stdout=PIPE, stderr=PIPE).communicate()

    if (len(output1) + len(output2) + len(errors1) + len(errors2) > 0):
        print("Result : there are differences:")

        if (len(output1) + len(output2) > 0):
            print("  Output differences : ")
            print(output1)
            # print (str(str(output1).count('\n'))); (*)
            print(output2)
            # print (str(str(output2).count('\n'))); (*)
            if (len(errors1) + len(errors2) > 0):
                print("  Errors : ")
                print(errors1)
                print(errors2)
            else:
                print("Result : both are equal")

except Exception as ex:
    print("Result : Exception during comparison:")
    print(ex)
    raise

我已将两个有问题的行放在注释中(以(*) 结尾的行)。

如您所见,问题如下:

  • 当我在命令提示符下启动命令时,我得到的结果是一堆字符串。通过计算这些字符串的数量,我可以获得我想要的结果(例如使用wc -l)。

  • 当我在 Python 脚本中启动命令时,我得到的结果(output1output2)是字节而不是字符串。

    我曾希望对字符串的简单类型转换能让我有机会计算换行符的数量,从而计算差异的数量,但这太容易了。

我曾尝试使用wc -l,但subprocess.Popen() 内部的管道似乎不是一个好主意。

如何处理output1output2 结果以搜索差异数量?

【问题讨论】:

  • 我看到您使用的是 cygwin,您可能想尝试将 universal_newlines=True 添加到 subprocess.Popen 调用中。
  • 使用外部 grep 代替标准库中的 difflib 模块有什么特殊原因吗?
  • 你好 Serge,我是 Python 的新手,我不知道 difflib 模块,我已经尝试了“filecmp”,但我并不满意。我现在仔细看看difflib。感谢您的建议。
  • 不相关:您可以将r'c:\cygwin\bin\grep.exe'(原始字符串文字)用于 Windows 路径。删除 shell=True 并将完整路径传递给 grep.exe(包括文件扩展名)。
  • 你考虑过comm -3 <(sort file1) <(sort file2)吗?

标签: python python-3.x grep subprocess cygwin


【解决方案1】:

不要在字节上调用str()。这几乎总是一个错误。

要启用文本模式,请将universal_newlines=True 传递给subprocess.Popen()

或者您可以直接使用字节,例如,使用 .count(b'\n') 而不是 .count('\n')

【讨论】:

  • 我不知道谁投了反对票,但我反对了。
【解决方案2】:

我猜你正在使用 python 3.x(你没有指定 2.7 与 3.x,但在 2.7 中,subprocess.communicate() 返回两个字符串或无值的元组,而在 3.x它返回一个由两个字节或无值组成的元组,并且您专门说“字节”):

$ python3
...
>>> import subprocess
>>> proc = subprocess.Popen(['ls'], stdout=subprocess.PIPE)
>>> res = proc.communicate()[0]
>>> type(res)
<class 'bytes'>
>>> 

对比:

$ python2
...
>>> import subprocess
>>> proc = subprocess.Popen(['ls'], stdout=subprocess.PIPE)
>>> res = proc.communicate()[0]
>>> type(res)
<type 'str'>
>>> 

这是因为 python3 对其所有字符串都使用 Unicode(而不是尝试对字节序列和“字符串”事物使用字符串)。

有两种明显的方法可以解决这个问题。

  • 将字节作为字节处理:

    >>> res.count(b'\n')
    382
    >>> print(res.splitlines())[0])
    b'COPYING'
    

    (这个方法也适用于 Python 2.7,b'\n' 就是 '\n'。)

  • 将输入转换为 Unicode。我对 Python3 不是很了解,也不确定执行此操作的最佳方法,但这似乎相当不错:

    >>> stringy = res.decode('utf-8') # or whatever encoding your system uses
    >>> print(type(stringy), stringy.splitlines()[0])
    <class 'str'> COPYING
    

或者,您可以通过设置 universal_newlines=True 让 Python 将管道输出转换为 Unicode 字符串;见the documentation

或者,当然,您可以使用 Python 2 :-)(出于各种兼容性原因,我仍然这样做)

【讨论】:

  • 感谢您解决了我的问题:我确实在使用 Python 3,并且使用以下命令解决了我的问题:print (str(output1.count(b'\n'))); (mind b"\n" 而不是 "\n")跨度>
  • splitlines(ord(b'\n')) 具有误导性。参数是一个布尔值keepends,即你想要splitlines(True)。此外,在这里编写在 Python 2 和 3 上运行相同的代码也很容易,例如,.count(b'\n') 可以在 Python 2.7 和 Python 3 上运行,或者传递 universal_newlines=True 以启用文本模式。
  • @J.F.Sebastian:啊,正如我所说,我对 Python 3 不太了解。将更新答案。
【解决方案3】:

你为什么不把它传送到 unix 工具 diff

diff <(grep "^@" myfile1) <(grep "^@" myfile2)

您可以在 popen 命令中调用它。

【讨论】:

  • 我在 Windows 7 环境中工作,其中有一个“C:\Cygwin”目录,其中包含“grep”工具。但是我那里似乎没有“差异”工具。
  • 然后从 setup.exe 安装它。 diff 是 cygwin 中必不可少的工具。
  • @Dominique 也许将这些添加到您的问题中是个好主意 :)
猜你喜欢
  • 2012-02-28
  • 1970-01-01
  • 1970-01-01
  • 2011-12-11
  • 1970-01-01
  • 2014-06-24
  • 1970-01-01
  • 2021-08-24
  • 2011-09-16
相关资源
最近更新 更多