我正在尝试使用Windows批处理文件将数据从stdin(到日志文件)中加时间戳

时间:2013-01-28 18:10:52

标签: windows batch-file pipe

我正在使用wget的DOS端口从API收集数据,以生成日志文件的数据(将在以后进行分析)。 API提供了我需要的所有信息,除了当前时间(它在数据流开始时提供时间,但在此之后不再提供时间)。

API最初提供10行数据,然后每20-30秒提供一行。

我正在尝试为此输出添加时间戳并将其复制到日志文件中 - 我不介意时间戳与输出的其余部分或之前的行位于同一行。

我首先开始使用这个批处理文件:

addtimes.bat:

@echo off >nul
:start
set /p input="":

echo %time% 
echo %input%

goto:start

(称为“wget ..... | addtimes.bat> log.log”)

然而,这些数据丢失了 - 许多行数据的开头都丢失了。

我在这里看到并意识到我应该使用for循环。

addtimes2.bat:

@echo off
cls
setlocal EnableDelayedExpansion
for /F "tokens=*" %%a in ('more') do ( 
echo !time! %%a )
)

我尝试过使用和不启用延迟扩展。

我似乎无法使用不同的时间戳一次传递一行信息 - 一旦关闭数据流,我的所有行都会得到相同的时间戳。

典型输入数据的格式为:

[1,"219","265",14528,1359031137000,1359031137000]
[1,"6594","358",18188,1359031019000,1359031019000]
[1,"690","94",15920,1359031534000,1359031534000]
[1,"25164","102",2129,1359031457000,1359031457000]


[1,"3488","329",2109,1359030868000,1359030868000]

[1,"37247","6",11506,1359031223000,1359031223000]

您可能会注意到数据中有UTC时间,但它们不是当前时间。

2 个答案:

答案 0 :(得分:4)

我不相信您可以使用纯原生批次获得您想要的结果。所有时间都相同的原因是FOR / F循环不会处理任何输入行,直到整个输入流被缓冲(管道左侧的命令已经完成)。 FOR / F命令等待直到收到所有输入,然后在一次狂奔中转储每一行。如果输入流足够大,您将获得轻微的时间变化,但在原始命令生成每一行时,没有任何表示接近。

这是一个混合的JScript /批处理脚本,可以执行您想要的操作。它可以作为一个直接的JScript文件工作,但是你需要明确地使用CSCRIPT。混合方法使实用程序更方便。

将其命名为addtimes.bat,并像以前一样使用它。

@if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment

::************ Batch portion ***********
@echo off
cscript //E:JScript //nologo "%~f0"
exit /b 0

************* JScript portion **********/
while (!WScript.StdIn.AtEndOfStream) {
  var ts = new Date()
  var ms = (ts.getTime() % 1000)
  WScript.Stdout.WriteLine(
    ts.getFullYear() + "-" +
    ((ts.getMonth()<9)?"0":"") + (ts.getMonth()+1) + "-" +
    ((ts.getDate()<10)?"0":"") + ts.getDate() + " " +
    ((ts.getHours()<10)?"0":"") + ts.getHours() + ":" +
    ((ts.getMinutes()<10)?"0":"") + ts.getMinutes() + ":" +
    ((ts.getSeconds()<10)?"0":"") + ts.getSeconds() + "." +
    ((ms<10)?"00":((ms<100)?"0":"")) + ms + " - " +
    WScript.StdIn.ReadLine()
  );
}


修改

wmz有一个非常聪明的 和危险的 解决方案。该解决方案可以简化 - 无需使用自动运行功能。

警告 - 正如wmz所说,如果输出中的任何行以可执行命令或程序名开头,则下面的解决方案会产生非常糟糕的后果!我不建议实际使用此解决方案,但我发现它非常有趣。

(echo @prompt $D $T -$S & YourCommandHere )|cmd 2>nul|findstr /rbc:"../../.... ..:..:..\... - " >log.log

添加FINDSTR管道以去除CMD头信息,初始PROMPT命令以及CMD在每个“命令”之后插入的不需要的空白行。 FINDSTR正则表达式可能需要更改以匹配您选择的提示和区域设置的细节。

答案 1 :(得分:3)

请注意:这可能会产生很多副作用(或根本不起作用,或使您的系统不稳定等)并且未经过测试。这比批量运动更多是

编辑:请参阅debenham的回答,了解如何使用这个想法。

  1. 如果您愿意(prompt $T),请将提示设置为时间(prompt $D $T)或日期时间。您必须在注册表中的Autorun键(HKEY_CURRENT_USER\software\Microsoft\Command Processor)中执行此操作,因此它是默认值。如果没有自动运行键,则创建它(它包含打开cmd提示时执行的命令)
  2. 启动cmd提示符,然后将命令的输出管道输出到另一个cmd.exe,并将其输出重定向到文件:
    more | cmd 2>nul >timestamped.log(您在我使用more的地方使用您的命令)。使用more,输入:
  3.   这是一条消息   这是时间戳^ Z

    在timestamped.log中生成以下行(在具有cmd处理器版本信息的两行之后):

      

    23:19:57,17_这是一条信息   23:19:59,95_是时间戳的

    这是有效的,因为cmd会尝试执行您的日志条目。这失败了(错误消息被抑制/发送到nul),但同时与提示(时间/日期)一起回显。

    如果您的日志消息未被引用,则必须非常小心或更一般地说,如果您不确定其格式,或者它们不是在您的直接控制下创建的< / em>) - 如果您的行恰好以一个有效命令的单词开头 - 它将被执行!