Python 在通过管道传输到文件时,在 Powershell 中调用的脚本无法写入标准输出

Python Script Called in Powershell Fails to Write to Stdout when Piped to File

所以我试图将几个脚本链接在一起,一些在 powershell (5.1) 中,一些在 python (3.7) 中。

我遇到问题的脚本是用 python 编写的,并通过 sys.stdout.write() 写入标准输出。这个脚本读入一个文件,完成一些处理,然后输出结果。

当这个脚本被自己调用时,也就是说没有输出到任何管道,它会正确执行并写入标准的 powershell 控制台。但是,一旦我尝试以任何方式传输输出,我就会开始出错。

特别是,两个文件具有字符 \u200b 或 zero-width-space。将这些字符的输出打印到控制台是可以的,但是尝试通过多种方法将输出重定向到文件:

py ./script.py input.txt > output.txt
py ./script.py input.txt | Set-Content -Encoding utf8 output.txt
Start-Process powershell -RedirectStandardOutput "output.txt" -Argumentlist "py", "./script.py", "input.txt"
$PSDefaultParameterValues['Out-File:Encoding'] = 'utf8'

全部失败:

File "\Python\Python37\lib\encodings\cp1252.py", line 19, in encode
    return codecs.charmap_encode(input,self.errors,encoding_table)[0]
UnicodeEncodeError: 'charmap' codec can't encode character '\u200b' in position 61: character maps to <undefined>

在python这边,修改脚本删除所有非UTF-8字符也会导致这个脚本失败,所以我有点卡住了。我目前认为问题的发生是由于管道输出如何导致 python 设置不同的环境,尽管我不确定如何在 python 代码中进行此类修改。

为了完整起见,这里是写入输出的函数。 (注意:file_lines是一个字符串列表):

import sys

def write_lines(file_lines):
    for line in file_lines:
        line = list(map(lambda x: '"' + x + '"', line))
        line = "".join(entry + ',' for entry in line)
        if not line is None:
            sys.stdout.write(line + "\n")

根本原因在于 python 处理 STDOUT 的方式。 Python 进行一些低级检测以获取系统编码,然后使用 io.TextIOWrapper 并将编码设置为它检测到的内容,这就是您在 sys.stdout 中获得的内容(stderr 和 stdin 有一样)。

现在,此检测 returns UTF-8 when 运行 in the shell because powershell works in UTF-8 and puts a layer of translation between the system 和 运行 程序,但是当管道连接到另一个程序时,通信是直接的,没有 powershell 转换,这种直接通信使用系统的编码,对于 windows 是 cp1252(又名 Windows-1252).

system <(cp1252)> posh <(utf-8)> python # here stdout returns to the shell
system <(cp1252)> posh <(utf-8)> python <(cp1252)> pipe| or redirect> # here stdout moves directly to the next program

至于你的问题,在不查看程序的其余部分和输入文件的情况下,我最好的猜测是编码不匹配,很可能是在读取输入文件时,默认情况下 python 3+ 将以 utf-8 读取文件,如果此文件采用其他编码,则会出现错误,最好的情况是出现垃圾文本,最坏的情况是出现编码异常。

要解决这个问题,您需要知道您的输入文件是使用哪种编码创建的,这可能会变得棘手并且检测通常很慢,其他解决方案是以字节为单位处理文件,但这可能无法实现,具体取决于处理完成。