【问题标题】:Python subprocess cannot capture output of Windows programPython 子进程无法捕获 Windows 程序的输出
【发布时间】:2018-04-12 20:00:57
【问题描述】:

使用stdout=subprocess.PIPE 会阻止输出进入控制台,但不会捕获任何内容。

>>> import subprocess
>>> proc = subprocess.Popen(['C:\\Users\\me\\program.exe'])
>>> ERROR: please provide an argument
// TRUNCATED USAGE OUTPUT
proc.wait()
0
>>> proc = subprocess.Popen([''C:\\Users\\me\\program.exe''], stdout=subprocess.PIPE)
>>> proc.communicate()
('', None)

我已经尝试了 stackoverflow 上所有可用的组合。 shell=True 没有用。生成子 cmd 没有奏效。 subprocess.check_output 什么也没捕获。我很高兴在 cmets 中重试这些命令中的任何一个。

我猜这与程序是否附加到外壳有关。

这是程序用来输出的程序集(mcall 只是一个将内存对齐到 16 位的宏)。我包含此内容的原因是,以防GetStdHandle 影响事物。

console_write PROC
; rcx   MSG
; rdx   LEN
  prologue
  push    rcx
  push    rdx
  xor     rcx, rcx
  mov     ecx, [stdout]
  mcall   GetStdHandle
  mov     rcx, rax
  xor     rdx, rdx
  pop     r8    ; len
  pop     rdx   ; msg
  push    0
  mov     r9, rsp
  push    0
  mcall   WriteConsoleA
  pop     rcx
  pop     rcx
  epilogue
console_write ENDP

我被这个难住了。我在 Linux 上做过很多次。我只是不太了解 Windows 内部结构来解决这个问题。谢谢!


编辑:我尝试过的其他事情:

管理员(也带有 STDERR 捕获)

C:\Windows\system32>C:\Python27\python.exe
Python 2.7.14 (v2.7.14:84471935ed, Sep 16 2017, 20:19:30) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import subprocess
>>> proc = subprocess.Popen(['C:\\Users\\me\\program.exe'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
>>> proc.communicate()
('', '')

STDOUT 文件重定向

>>> import os
>>> os.system('C:\\Users\\me\\program.exe > out.txt')
0
>>> f = open('out.txt', 'r')
>>> f.read()
''

STDERR 文件重定向

>>> import os
>>> os.system('C:\\Users\\me\\program.exe 2>out.txt')
ERROR: please provide an argument
// TRUNCATED USAGE OUTPUT
0
>>> open('out.txt', 'r').read()
''

命令行捕获失败(禁止输出到 cmd,但没有捕获任何内容)

program.exe > out.txt

【问题讨论】:

  • 你试过 stderr 了吗?
  • 是的。同样的问题。 >>> proc.communicate() ('', '')(另一条评论中的完整命令)
  • 或者你应该以管理员身份运行它,你检查了吗?
  • 你可以在 cmd 上看到它的输出,对吧?所以,把它写在一个 .bat 文件中,然后用你的 python 脚本启动那个 bat 文件,看看会发生什么......
  • EXE 没问题。用 ML64 组装(我今天重建了它,没有任何变化)。它输出到控制台并返回 0。捕获标准输出会抑制输出但无法捕获。

标签: python windows winapi cmd subprocess


【解决方案1】:

一位同事指出了这个问题。事实证明,WriteConsole 无法重定向到文件,它只能与控制台句柄一起使用。 [1][2]

StackOverflow answer 给出了如何解决它的大纲。如果其他人有这个问题,我已经用 Python 实现了。

import win32console
import subprocess
import msvcrt
import os

ccsb = win32console.CreateConsoleScreenBuffer()
fd = msvcrt.open_osfhandle(ccsb, os.O_APPEND)
proc = subprocess.Popen(['C:\\Users\\me\\program.exe'], stdout=fd)
proc.wait()
ccsb.ReadConsoleOutputCharacter(100, win32console.PyCOORDType(0,0)) # reads 100 characters from the first line

对我来说更好的解决方案是将WriteConsoleA 更改为WriteFile,使用相同的StdHandle

console_write PROC
; rcx   MSG
; rdx   LEN
  prologue
  push    rcx
  push    rdx
  xor     rcx, rcx
  mov     ecx, [stdout]
  mcall   GetStdHandle
  mov     rcx, rax
  ; Write File
  pop     r8    ; len
  pop     rdx   ; msg
  ; unalign for odd number of pushed args
  mov     rbx, rsp
  sub     rbx, 8        
  and     rbx, 0Fh
  sub     rsp, rbx
  ; args
  mov     rcx, rax        ; handle
  xor     r9, r9
  push    0               ; 1 arg (ununaligns)
  sub     rsp, 20h        ; shadow 
  call    WriteFile
  add     rsp, 28h        ; shadow + arg
  add     rsp, rbx        ; realign
  epilogue
console_write ENDP

【讨论】:

  • x64 asm 代码非常不寻常。我的意思是你如何使用堆栈。所有这些推/弹出在中间。在每次调用之前不必忘记堆栈对齐。为什么不使用mov params (4,5..) 的常用技术来堆叠?
  • @RbMm 任何常用代码示例?我有一个用于对齐的宏并调用 0-4 个参数。这是我对 5 进行的一次调用。我最终将编写一个新宏,您可以在其中提供参数的数量,它会根据它是偶数还是奇数来进行对齐/取消对齐。我看到的大多数代码都依赖于跟踪堆栈是否对齐。这不关心它是否对齐或未对齐。如果有更好的方法,我总是乐于学习。
  • 查找任何 Windows x64 二进制文件。真的,你只会让你自己的生活变得复杂。特殊宏绝对不需要。您需要 once 在函数序言对齐堆栈中。然后永远不要对 args 使用 push 或 pop。只需使用 mov。比如mov [rsp+20h],arg5mov [rsp+28h],arg6等等。并且代码变得更小,更可红色。
  • @RbMm sub rsp, 0xNN 需要知道堆栈是否对齐。 0xNN 需要是 shadow_space + 8 * n_stack args + alignment。我的代码只是测试对齐并首先减去它。我可以做同样的事情,只使用and rbx, 0Fh 的结果作为上述等式中的alignment,但这没有区别。我不是编译器,不想跟踪事物是否对齐或未对齐,因此测试很有用。然后,如果我更改调用上方的代码,它不会弄乱调用的对齐方式。
  • 如果您可以控制源代码,那么请务必更改为WriteFile。使用WriteConsoleA 没有任何好处,而且它的缺点是它只接受控制台屏幕缓冲区句柄。 WriteConsoleW 将是另一回事。这是在 Windows 7 中将全部 Unicode 以 UTF-16 文本形式写入控制台的唯一可靠方法,因为旧控制台对代码页 65001 (UTF-8) 的支持对于输出存在缺陷。 (即使在 Windows 10 中,控制台仍然无法将输入读取为 UTF-8。)
【解决方案2】:

您是否也尝试捕获 stderr?

proc = subprocess.Popen([''C:\\Users\\me\\program.exe''], stdout=subprocess.PIPE, stderr=subprocess.PIPE)

更新

您可以尝试在进程运行时逐行读取输出,例如:

p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while True:
    retcode = p.poll()
    print p.stdout.readline()
    if retcode is not None:
        break

【讨论】:

  • 是的。同样的交易。 >>> proc = subprocess.Popen(['C:\\Users\\me\\program.exe'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) >>> proc.communicate() ('', '')
  • os.system() 不捕获输出。重定向到像 os.system(program.exe > out.txt) 这样的文件会导致没有输出写入文件。
  • 还有os.system(program.exe 2> out.txt)?
  • 它正在打印到stdout。当我这样做时,输出将进入控制台,并且文件中没有捕获任何内容。将在上面进行编辑。
【解决方案3】:

我认为我没有足够的代表来建议一个问题可能是重复的,也没有评论,所以我将其发布为答案,因为尽管 douggard 的回答让我在思考方面走上了正确的道路,但它没有' t 实际上在我的情况下工作,我很难找到一个可行的解决方案。

这里有一个可以捕获 CONOUT 并为我工作的答案:https://stackoverflow.com/a/38749458/1675668

【讨论】:

    猜你喜欢
    • 2016-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-23
    • 2017-09-17
    • 1970-01-01
    • 2012-05-11
    相关资源
    最近更新 更多