【问题标题】:Python Script Called in Powershell Fails to Write to Stdout when Piped to File管道传输到文件时,Powershell 中调用的 Python 脚本无法写入标准输出
【发布时间】:2019-07-30 20:13:08
【问题描述】:

所以我试图将几个脚本链接在一起,一些在 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 编写文件,或者删除不符合 cp1252 的字符。我为这种情况做了后者,因为这只影响了两个文件。

标签: python python-3.x powershell io-redirection zero-width-space


【解决方案1】:

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

现在,当在 shell 中运行时,此检测返回 UTF-8,因为 powershell 在 UTF-8 中工作,并在系统和正在运行的程序之间放置了一层翻译,但是当管道到另一个程序时,通信是直接的,没有 powershell 翻译, 这种直接通信使用系统的编码,对于 windows 是 cp1252 (AKA 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 中的文件, 如果这个文件是其他编码你会得到错误,最好的情况是你会得到垃圾文本,最坏的情况是你会得到一个编码异常。

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

【讨论】:

  • 我正在写入的数据来自通过 COM 对象从 powershell 读取的 Word 文件。 Word 返回大量非犹太字符(例如:BEL),因此它们已被较早删除。另一件事是powershell默认以UTF-16写入文件,因此我的文件通过管道传输到Set-Content -Encoding utf8,并在python中使用encoding="utf-8-sig"打开。我发现有趣的是,到 Set-Content 的管道可以正常工作,但不是通过 python 脚本。我猜这与powershell如何管道对象有关,但不能与python有关,所以它默认为文本。
猜你喜欢
  • 1970-01-01
  • 2014-02-22
  • 2015-01-18
  • 1970-01-01
  • 2011-02-15
  • 2019-01-24
  • 2012-04-15
  • 2010-10-24
相关资源
最近更新 更多