【问题标题】:Getting a Windows command prompt contents to a text file将 Windows 命令提示符内容获取到文本文件
【发布时间】:2020-05-03 16:10:46
【问题描述】:

我想编写一个批处理实用程序来将命令提示符窗口的输出复制到文件中。我运行最大深度为 9999 行的命令提示符窗口,有时我想获取其输出在屏幕外的命令的输出。我可以使用 Ctrl-ACtrl-C 键手动执行此操作,然后将结果粘贴到记事本中 - 我只想在批处理文件中自动执行此操作,并调用:

SaveScreen <text file name>  

我知道我可以通过重定向来做到这一点,但这需要知道我需要事先保存批处理命令序列的输出。

如果我有一个批处理脚本:

call BuildPhase1.bat
if "%ErrorLevel% gtr 0 goto :ErrorExit
call BuildPhase2.bat
if "%ErrorLevel% gtr 0 goto :ErrorExit
call BuildPhase3.bat
if "%ErrorLevel% gtr 0 goto :ErrorExit

我会写:

cls
call BuildPhase1.bat
if "%ErrorLevel% gtr 0 call SaveScreen.bat BuildPhase1.err & goto :ErrorExit
call BuildPhase2.bat
if "%ErrorLevel% gtr 0 call SaveScreen.bat BuildPhase2.err & goto :ErrorExit
call BuildPhase3.bat
if "%ErrorLevel% gtr 0 call SaveScreen.bat BuildPhase3.err & goto :ErrorExit

或者当我看到运行失败时,我可以输入SaveScreen batch.log

我的实验让我走到了这一步:

<!-- : Begin batch script

    @cscript //nologo "%~f0?.wsf" //job:JS
    @exit /b

----- Begin wsf script --->
<package>
  <job id="JS">
    <script language="JScript">

      var oShell = WScript.CreateObject("WScript.Shell");
      oShell.SendKeys ("hi folks{Enter}") ;

      oShell.SendKeys ("^A") ;              // Ctrl-A  (select all)
      oShell.SendKeys ("^C") ;              // Ctrl-C  (copy)
      oShell.SendKeys ("% ES") ;            // Alt-space, E, S  (select all via menu)
      oShell.SendKeys ("% EY") ;            // Alt-space, E, Y  (copy via menu)

      // ... invoke a notepad session, paste the clipboard into it, save to a file

      WScript.Quit () ; 
    </script>
  </job>
</package>

我的击键正在进入命令提示符,所以大概我有正确的窗口焦点 - 它似乎忽略了 CtrlAlt 修饰符。它还可以识别Ctrl-C,但不能识别Ctrl-A。因为它忽略了 Ctrl-A 来选择所有文本,所以 Ctrl-C 导致批处理文件认为它已经看到了一个中断命令。

我见过像this one 这样的其他答案,但它们都处理使用重定向的方法,而不是在“按需”之后进行处理的方法。

* 更新 *

基于@dxiv 的指针,这里是例程的批处理包装器:

Get-ConsoleAsText.bat

::  save the contents of the screen console buffer to a disk file.

    @set "_Filename=%~1"
    @if "%_Filename%" equ "" @set "_Filename=Console.txt" 

    @powershell Get-ConsoleAsText.ps1 >"%_Filename%"
    @exit /b 0

Powershell 例程与链接中介绍的非常相似,除了:

  • 我必须对其进行清理以删除选择/复制/粘贴操作引入的一些更有趣的字符替换。

  • 原版也保存了尾随空格。那些现在被修剪了。

Get-ConsoleAsText.ps1

# Get-ConsoleAsText.ps1  (based on: https://devblogs.microsoft.com/powershell/capture-console-screen/)
#  
# The script captures console screen buffer up to the current cursor position and returns it in plain text format.
#
# Returns: ASCII-encoded string.
#
# Example:
#
# $textFileName = "$env:temp\ConsoleBuffer.txt"
# .\Get-ConsoleAsText | out-file $textFileName -encoding ascii
# $null = [System.Diagnostics.Process]::Start("$textFileName")
#

if ($host.Name -ne 'ConsoleHost')                               # Check the host name and exit if the host is not the Windows PowerShell console host.
  {
  write-host -ForegroundColor Red "This script runs only in the console host. You cannot run this script in $($host.Name)."
  exit -1
  }

$textBuilder    = new-object system.text.stringbuilder          # Initialize string builder.

$bufferWidth    = $host.ui.rawui.BufferSize.Width               # Grab the console screen buffer contents using the Host console API.
$bufferHeight   = $host.ui.rawui.CursorPosition.Y
$rec            = new-object System.Management.Automation.Host.Rectangle 0,0,($bufferWidth - 1),$bufferHeight
$buffer         = $host.ui.rawui.GetBufferContents($rec)

for($i = 0; $i -lt $bufferHeight; $i++)                         # Iterate through the lines in the console buffer.
  {
  $Line = "" 
  for($j = 0; $j -lt $bufferWidth; $j++)
    {
    $cell = $buffer[$i,$j]
    $line = $line + $cell.Character
    }
  $line = $line.trimend(" ")    # remove trailing spaces.
  $null = $textBuilder.Append($line)
  $null = $textBuilder.Append("`r`n")
  }

return $textBuilder.ToString()

【问题讨论】:

  • 可能类似于Capture console screen(使用powershell)。
  • 好像最多只能访问可见的屏幕区域。它似乎占据了屏幕的一个矩形区域,然后返回该区域中的文本。这似乎是一种奇怪的方式。
  • PSHostRawUserInterface.GetBufferContents 确实获得了整个 buffer,无论控制台窗口中显示什么。并且可以在主机控制台上运行,可以是cmd提示符,不一定是PowerShell自己的控制台。
  • 备注 如果矩形完全在屏幕缓冲区之外,将返回一个零行零列的 BufferCell 数组。如果矩形部分位于屏幕缓冲区之外,则将读取并返回屏幕缓冲区和矩形重叠的区域。返回数组的大小与 r 的大小相同。该数组非重叠区域的每个BufferCell设置如下:
  • (摘自链接)全文在别处可用吗?

标签: batch-file command-prompt windows-scripting


【解决方案1】:

控制台缓冲区的内容可以通过评论中提到的 PowerShell 团队博客Capture console screen 中的 PS 脚本检索,现在已编辑为 OP 的问题。

最后一行也可以更改为将内容复制到剪贴板而不是返回它。

Set-Clipboard -Value $textBuilder.ToString()

作为旁注,How does StringBuilder work internally in C#How the StringBuilder class is implemented 中讨论了使用 StringBuilder 而不是直接串联的原因。

【讨论】: