subprocess.Popen 输出:如何克服与命令行执行的差异

subprocess.Popen output: how to overcome the differences with commandline execution

我正在编写 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 -lsubprocess.Popen() 内的管道似乎不是个好主意。

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

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

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

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

我猜你正在使用 python 3.x(你没有指定 2.7 与 3.x 但在 2.7 中,subprocess.communicate() returns两个字符串或 None 值的元组,而在 3.x 中它 returns 两个字节或 None 值的元组,你具体说 "bytes"):

$ 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(而不是尝试对字节序列和 "stringy" 事物使用字符串)。

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

  • 将字节作为字节处理:

    >>> 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
    

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

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

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

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

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