2

我试图在特定时刻捕获命令提示符(cmd 窗口)中显示的信息并将其发送到文本文件。

我有一个 C/C++ 应用程序,它由这样的批处理脚本启动:

c:\myProgramInC.exe
echo "Ending with error"
PAUSE

myProgramInC.exe 始终在运行(带有无限循环),因此如果我的脚本到达 echo 命令,则意味着我的应用程序以异常结束结束。

我想要得到的是在脚本结束执行之前的前几行,因为我的 myProgramInC.exe 总是打印有关正在发生的事情的信息,并且在发生错误时查看发生的情况将非常有用。像这样的东西

c:\myProgramInC.exe
**** Execution of process to get te previous N lines in the command prompt ****
echo "Ending with error"
PAUSE

我知道 cmd 窗口有一个缓冲区,我一直在考虑从这样的缓冲区中捕获所有这些数据。有什么方法可以将此信息作为文本存储在文件中?

我一直在尝试更专业的东西,例如使用 ProcDump 转储内存,但由于我同时运行了各种 myProgramInC.exe(每个都存储在不同的位置),我只收到消息“多个进程匹配指定的名称。” 其余的选项对我没有用,因为我的应用程序没有反应迟钝,它只是结束了。

有任何想法吗?

4

6 回答 6

2

快速技巧是在上下文中执行for /f,您甚至不需要批处理文件,您可以直接从 cmd 行执行:
for /f "tokens=*" %F in ('c:\myProgramInC.exe') do @echo %F >>myPrograminC.log

这将抑制所有输出,直到您的程序异常结束,然后才会将所有消息写入文件。如果您的应用程序不经常写入日志消息(或快速失败:-))它应该可以工作。我用 10 000 行测试了它。

下面的批处理代码基于相同的想法 - 请注意,即使它只写了最后 5 行,它仍然必须扫描所有这些行,所以我不确定它是否比 1 行以上更好。

@echo off
setlocal 

for /f "tokens=*" %%P in ('c:\myProgramInC.exe') do (
 for /L %%C in (4,-1,1) do (
  set /A next=%%C+1
  call set line_%%next%%=%%line_%%C%%
 )
 set line_1=%%P
)
echo program ended abnormally! %date% %time%
for /L %%C in (5,-1,1) do call echo %%line_%%C%% 
于 2012-08-11T22:35:50.670 回答
2

好的,可能有一种更优雅的方法可以做到这一点,但假设你有 PowerShell,这将起作用。

创建一个名为的 PowerShell 脚本文件Get-ConsoleAsText.ps1,其中包含以下脚本。注意,我没有创建这个脚本。我在Windows PowerShell 博客 - 捕获控制台屏幕上找到了它。

#################################################################################################################
# Get-ConsoleAsText.ps1
#
# 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")
#

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

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

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

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

return $textBuilder.ToString()

如果您自己调用 PowerShell 脚本,它将读取控制台缓冲区并将其写回屏幕

PowerShell -noprofile -sta -command "C:\Scripts\Get-ConsoleAsText.ps1"

您也可以这样调用它来将内容捕获到文件中:

PowerShell -noprofile -sta -command "C:\Scripts\Get-ConsoleAsText.ps1 | Out-File MyOutput.txt -encoding ascii"

如果您想处理它并在批处理文件中执行一些操作,您可以调用它并使用FOR命令处理输出。我会把这个练习留给你。

因此,例如,您的批处理文件将如下所示将控制台输出捕获到文件中:

c:\myProgramInC.exe
echo "Ending with error"
PowerShell -noprofile -sta -command "C:\Scripts\Get-ConsoleAsText.ps1 | Out-File MyOutput.txt -encoding ascii"
PAUSE
于 2012-08-14T15:09:30.387 回答
1

You can do this in C easily enough, using the ReadConsoleOutputCharacter function.

于 2012-08-11T09:09:51.070 回答
1

最后,这就是我让它发挥作用的方法。按照 Harry Johnston 的建议,我搜索了 ReadConsoleOutputCharacter 函数的工作方式,并运行了下一个程序。

#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <fstream>

#define BUFFER_SIZE 50000


int main(){
    HANDLE hOut;
    COORD location = {0, 0};
    char *buffer=NULL;
    DWORD numberRead;
    std::ofstream fileLog;

    buffer = new TCHAR[BUFFER_SIZE];

    if ( buffer==NULL )
    {
        printf("\nError: memory could not be allocated.\n");
        return 1;
    }

    fileLog.open ("logger.txt");

    if ( fileLog.fail() )
    {
        printf("\nError: file could not be opened.\n");
        return 1;
    }

    hOut = GetStdHandle(STD_OUTPUT_HANDLE);

    ReadConsoleOutputCharacter(hOut, buffer, BUFFER_SIZE, location, &numberRead);

    fileLog << buffer ;

    free(buffer);
    fileLog.close();

    return 0;
}

我在互联网上找到的大部分代码,现在我没有 URL(除了它来自另一个论坛站点,我不知道是否可以引用竞争对手的站点)但是在谷歌中进行一次搜索不会不难找到。

我添加了它写入文件和内存分配的部分。它在 VS C++ 6 中完美运行。

这对我来说很好,虽然可能会更好。有两件事不是很好

  1. 它将缓冲区中的所有内容写入一个文件,但它不保留换行符,所以我得到一个文本文件,其中所有信息都在一行中。
  2. 无法定义要捕获多少行。它只需要从缓冲区开始到程序启动点的所有内容,这还不错,但是由于这个应用程序应该在不同的服务器上运行,每个服务器对于每组命令提示符窗口都有不同的缓冲区,效率不高只是在某些情况下缓冲区较小时使用 50000 字节的缓冲区。我认为这可能很多,但由于它是实时分配的,因此使用尽可能多的内存可能并没有那么错误。
于 2012-08-14T22:20:16.470 回答
1

如果任何错误消息将被报告回命令输出流,则可以通过 STDOUT(标准输出)和 STDERR(标准错误)的重定向运算符轻松重定向。取决于您是否要捕获输出和/或错误,这取决于您。您很可能正在寻找类似的东西:

  • 命令 2>> 文件名

“2”是重定向运算符的一部分,表示 STDERR 将被重定向并附加到文件名。

来源、示例和文档:

  1. http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/redirection.mspx
  2. http://ss64.com/nt/syntax-redirection.html
于 2013-07-29T06:44:11.487 回答
0

立即想到的一件事是将命令行的输出重定向到文件:

c:\myProgramInC.exe >> myProgramInC.log

然后您可以查看日志文件中的内容。

于 2012-08-11T01:48:48.033 回答