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 -l
)。
当我在 Python 脚本中启动命令时,我得到的结果(output1
和 output2
)是字节而不是字符串。
我曾希望对字符串进行简单的类型转换能让我有机会计算换行符的数量,从而计算差异的数量,但这太容易了。
我试过使用 wc -l
但 subprocess.Popen()
内的管道似乎不是个好主意。
如何处理 output1
和 output2
结果以搜索差异数?
你为什么不把它传送到 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')
。
我正在编写 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 脚本中启动命令时,我得到的结果(
output1
和output2
)是字节而不是字符串。我曾希望对字符串进行简单的类型转换能让我有机会计算换行符的数量,从而计算差异的数量,但这太容易了。
我试过使用 wc -l
但 subprocess.Popen()
内的管道似乎不是个好主意。
如何处理 output1
和 output2
结果以搜索差异数?
你为什么不把它传送到 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')
。