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

时间:2015-10-23 09:18:25

标签: python python-3.x cygwin subprocess

我正在使用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结果以搜索差异数量?

3 个答案:

答案 0 :(得分:1)

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

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

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

答案 1 :(得分:0)

为什么不将它传递给unix工具diff

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

您可以在popen命令中调用此方法。

答案 2 :(得分:0)

我猜你正在使用python 3.x(你没有指定2.7 vs 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'>
>>> 

VS

$ 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 :-)(我仍然出于各种兼容性原因)