如何在批处理文件输出的日志文件中为日志行添加时间戳?

3
我想在批处理输出的日志行中添加时间戳。
以下是我的批处理文件:
@Echo off
SET LOGFILE=MyLogFile.log
call :Logit >> %LOGFILE% 
exit /b 0

:Logit
set "affix=%date%_%time%"
set "affix=%affix::=%"
set "affix=%affix:/=%"
xcopy "I:\DF\AB\Data.xlsx" "D:\TL\BACKUP\Data_%affix%.xlsx"*

日志文件的输出:
I:\DF\AB\Data.xlsx
1 File(s) copied

我希望输出的日志文件看起来像这样:
I:\DF\AB\Data.xlsx
20180831_124500 : 1 File(s) copied

这如何实现?
更多信息:
  1. 在目标参数字符串末尾的星号是用于在没有提示的情况下复制文件的必需品。如果目标文件名末尾不使用 *,将会出现一个提示询问目标是文件还是目录。
  2. 使用 xcopy 是因为要从网络驱动器复制文件到本地驱动器。
运行批处理文件后的输出结果如下:
I:\DF\AB\Data.xlsx
08312018_163959.07 :I:\DF\AB\Data.xlsx
1 File(s) copied

可能是如下所示吗?
I:\DF\AB\Data.xlsx
08312018_163959.07  1 File(s) copied 

因此,该地区的日期格式为月/日/年,时间格式为小时:分钟:秒.毫秒

在最后一行的下方添加以下代码:echo %affix%。这将按照您的要求设置所有内容并复制文件,但只是在调用标签时,在文件复制后会将修改时间和日期回显到日志文件中。 - Gerhard
4个回答

1
你可以使用 echo| set /p=%affix% 来消除回车符,示例如下:
@Echo off
SET LOGFILE=MyLogFile.log
call :Logit >> %LOGFILE% 
exit /b 0
:Logit
set "affix=%date%_%time%"
set "affix=%affix::=%"
set "affix=%affix:/=%"
echo|set /p=%affix% :
xcopy "I:\DF\AB\Data.xlsx" "D:\TL\BACKUP\Data_%affix%.xlsx"*

结果:

I:\DF\AB\Data.xlsx
2018-08-31_124900 :        1 file(s) copied.

@v1787v。这是我能做到的最好的了,我已经删除了文件回显,因为它只是一个测试,但你的结果不可能与此不同,你不能默认地分割命令的输出,唯一可能的方法是创建一个for循环,将命令管道到nul,然后使用输出并将其拆分成变量并回显到文件中。但那似乎真的很不必要。 - Gerhard

1
你只是在复制一个文件,所以你知道成功时的最后一行输出将是与语言有关的字符串“1 File(s) copied”。由于你已经限制了脚本使用与本地相关的“%DATE%”和“%TIME%”,我假设这个任务的语言依赖性是可以接受的。
因此,以下是一个看起来荒谬的示例脚本:
@Echo Off
Set "srcfile=I:\DF\AB\Data.xlsx"
Set "destdir=D:\TL\BACKUP"
Set "logfile=MyLogFile.log"

For %%A In ("%srcfile%") Do Set "dstname=%%~nA" & Set "destext=%%~xA"

For /F "Tokens=1-2 Delims=|" %%A In ('
    Echo F^|XCopy "%srcfile%" "|%DATE:/=%_%TIME::=%|" /L 2^>Nul ^&^
    Echo F^|Xcopy "%srcfile%" "%destdir%\%dstname%_%DATE:/=%_%TIME::=%%destext%" /Y ^>Nul 2^>^&1
') Do (If Not "%%B"=="" Set "_=%%B"
    If Defined _ If /I "%%A"=="%srcfile%" ((
            Echo %%A&Call Echo %%_%%  1 File(s^) copied)>"%logfile%"))

你只需要更改第2-4行的变量值,不要更改其他内容。
但如果你使用现有的日志文件,则可能需要将最后一行的>更改为>>


0
我建议使用以下代码在日志文件中生成刚开始期望的输出内容。
@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "LOGFILE=MyLogFile.log"
del "%LOGFILE%" 2>nul
call :Logit >>"%LOGFILE%"
endlocal
exit /B 0

:Logit
set "FileDate=%DATE:~-4%%DATE:~-10,2%%DATE:~-7,2%_%TIME:~0,2%%TIME:~3,2%%TIME:~6,2%"
for /F "tokens=1* delims=:" %%I in ('%SystemRoot%\System32\xcopy.exe "I:\DF\AB\Data.xlsx" "D:\TL\BACKUP\Data_%FileDate%.xlsx*" /C /V /Y 2^>nul') do (
    if not "%%J" == "" (
        echo %%I:%%J
    ) else (
        echo %FileDate% : %%I
    )
)
goto :EOF

区域相关的日期和时间通过使用动态环境变量DATETIME的字符串替换,按照yyyyMMdd_HHmmss格式重新格式化。例如,在问题的答案中详细解释了这个过程:What does %date:~-4,4%%date:~-10,2%%date:~-7,2%_%time:~0,2%%time:~3,2% mean? 如果需要一个速度较慢但与区域无关的解决方案,可以参考以下答案:Why does %date% produce a different result in batch file executed as scheduled task?

当前的日期和时间以yyyyMMdd_HHmmss格式分配给环境变量FileDate,在下一行中两次使用,一次用于目标文件名,另一次用于XCOPY命令的重新格式化输出的最后一行。

此处使用的XCOPY命令行示例为:

C:\Windows\System32\xcopy.exe "I:\DF\AB\Data.xlsx" "D:\TL\BACKUP\Data_20180831_163959.xlsx*" /C /R /V /Y 2>nul

这个命令行是由FOR在后台使用cmd.exe /C启动的单独命令进程中执行的。 FOR在处理捕获的行之前,捕获了此命令进程的STDOUT句柄写入的所有行。

XCOPY将复制的文件名(包括完整路径)输出到STDOUT句柄,并作为最后一行输出摘要信息。文件复制错误会被写入STDERR句柄,并通过重定向到设备NUL来抑制。

还可以阅读微软关于使用命令重定向运算符的文章,以了解2>nul的说明。在FOR命令行上,重定向运算符>必须使用插入符号^进行转义,以便在Windows命令解释器在执行嵌入的xcopy命令行时,将其解释为文字字符,该命令行使用单独的命令进程在后台执行。

目标文件名末尾的星号*应该在第二个参数字符串的双引号内,而不是外部,否则cmd.exexcopy.exe将不得不更正这个错误的语法。

请注意,在此处使用目标文件名末尾的星号*的技巧只是偶然的,因为源文件和目标文件具有相同的文件扩展名,并且源文件名始终比目标文件名短。否则,命令将失败或目标文件将获得一个不需要的名称,即目标文件名+源文件名中目标文件名后n个字符的字符连接。

通常有更好的方法来避免在复制新文件名的单个文件时出现提示,这是XCOPY请求的。可以首先将答案字母输出到STDOUT并重定向到XCOPY命令的STDIN处理程序,如batch file asks for file or folder中所示,这是与语言无关的示例。

XCOPY 的捕获输出通过 FOR 逐行处理,跳过空行和以分号 ; 开头的行,因为这是选项 eol= 的默认行结束符,此处未使用。

这里的目标是在后台命令进程中输出所有由 XCOPY 输出的完全限定文件行,并在此命令进程中输出最后一行的汇总信息,但在其前面加上所需格式的日期/时间、一个空格、一个冒号和一个空格。

出于这个原因,通过选项 tokens=1* delims=: 修改了只将第一个子字符串(标记)分配给指定循环变量 I 的默认行拆分行为,现在 FOR 在冒号上拆分一行。

只有以驱动器字母和冒号开头的全限定文件名行才包含冒号。在这样的行上,根据 tokens=1 指定的方式,将驱动器字母分配给指定的循环变量 I。文件名行中第一个冒号后面的其余部分将根据 ASCII 表 分配给下一个循环变量 J,即驱动器字母后面的冒号之后的所有内容。

摘要信息行不包含冒号。因此,FOR 将整个摘要信息分配给循环变量 I,而 J 保持为空字符串。

在以驱动器字母和冒号开头的文件名行上,循环变量 J 永远不会为空。利用这一事实,可以确定是否应该将来自 XCOPY 的行按原样输出,并在驱动器字母和文件路径 + 文件名 + 文件扩展名之间插入已删除的冒号,或者输出带有日期/时间开头的摘要信息。

请注意,此方法仅适用于从带有驱动器号的驱动器复制文件。对于具有UNC路径的源文件,需要使用不同的方法。
实际上,即使从/到网络驱动器或使用UNC路径指定源/目标文件名,也可以使用命令COPY更轻松地复制单个文件。 COPY还具有选项/V/Y,甚至像XCOPY一样具有/Z选项。 COPY不像XCOPY那样创建目标目录结构,但是可以在之前使用命令MD来完成此操作。 COPY无法覆盖只读文件,而XCOPY可以使用选项/R来执行此操作,但是COPY的这种限制在这里很可能不相关。然而,总的来说,最好使用命令COPY而不是XCOPY来复制单个文件。

所以这里有另一种解决方案,使用命令COPY,它比XCOPY解决方案更快,因为没有理由在单独的命令进程中执行文件复制,捕获任何行,将它们拆分并再次连接或修改输出。

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "LOGFILE=MyLogFile.log"
md "D:\TL\BACKUP" 2>nul
del "%LOGFILE%" 2>nul
call :Logit >>"%LOGFILE%"
endlocal
exit /B 0

:Logit
set "FileDate=%DATE:~-4%%DATE:~-10,2%%DATE:~-7,2%_%TIME:~0,2%%TIME:~3,2%%TIME:~6,2%"
echo I:\DF\AB\Data.xlsx
copy /B /V /Y "I:\DF\AB\Data.xlsx" "D:\TL\BACKUP\Data_%FileDate%.xlsx" >nul 2>nul && echo %FileDate% : 1 File(s) copied|| echo %FileDate% : 0 File(s) copied
goto :EOF

这种解决方案的优点在于成功或错误时的行输出可以完全定制。 COPY 在出现错误(例如源文件不可用或目标文件/目录当前或永久受到写保护)时以大于0的值退出。

以下是单个复制文件成功或错误时更好的输出示例(仅子程序):

:Logit
set "FileDate=%DATE:~-4%%DATE:~-10,2%%DATE:~-7,2%_%TIME:~0,2%%TIME:~3,2%%TIME:~6,2%"
copy /B /V /Y "I:\DF\AB\Data.xlsx" "D:\TL\BACKUP\Data_%FileDate%.xlsx" >nul 2>nul
if not errorlevel 1 (
    echo %FileDate% : Copied successfully I:\DF\AB\Data.xlsx
) else (
    echo %FileDate% : Failed to copy file I:\DF\AB\Data.xlsx
)
goto :EOF

当然也可以使用命令行

set "FileDate=%DATE:/=%_%TIME::=%"

如果确实需要,可以在批处理文件中以格式MMddyyyy_HHmmss.ms获取日期和时间。我不建议使用此日期/时间格式,因为它在目录D:\TL\BACKUP中所有Data_*.xlsx文件的字母表列表中不好。按名称排序的文件列表也会自动按日期/时间格式yyyyMMdd_HHmmss排序。

为了理解所使用的命令及其工作原理,请打开命令提示符窗口,在其中执行以下命令,并非常仔细地阅读每个命令显示的所有帮助页面。

  • call /?
  • copy /?
  • del /?
  • echo /?
  • endlocal /?
  • exit /?
  • for /?
  • goto /?
  • if /?
  • md /?
  • set /?
  • setlocal /?
  • xcopy /?

另请参阅:


非常感谢你,Mofi。 - v1787v

0

powershell -command "(New-TimeSpan -Start (Get-Date "01/01/1970") -End (Get- Date)).TotalSeconds">LOG.TXT

虽然这不是您建议的格式,但这种格式被称为纪元时间
这种格式的好处是它总是一个浮点值。

LOG.TXT将是日志的名称,请确保您在正确的目录中。


非常感谢你,Benjamin2002。 - v1787v

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接