Batch file 批处理文件日志功能,用于将命令输出打印到日志文件和屏幕

Batch file 批处理文件日志功能,用于将命令输出打印到日志文件和屏幕,batch-file,Batch File,我发现有些主题与这个问题类似,但并不完全是我想要的,所以我提出了这个主题 我想创建一个日志函数,用于将消息打印到格式化的日志文件和控制台输出中。功能如下: :LOGDEBUG @echo DEBUG: %~1 if NOT EXIST %LOG_FILE% exit /b 1 @echo [%date - %time%] DEBUG: %~1 >> %LOG_FILE% 2>&1 exit /b 0 我尝试使用它来打印命令执行输出,如果输出包含特殊字符,如“”,则此函

我发现有些主题与这个问题类似,但并不完全是我想要的,所以我提出了这个主题

我想创建一个日志函数,用于将消息打印到格式化的日志文件和控制台输出中。功能如下:

:LOGDEBUG
@echo DEBUG: %~1
if NOT EXIST %LOG_FILE% exit /b 1
@echo [%date - %time%] DEBUG: %~1 >> %LOG_FILE% 2>&1
exit /b 0
我尝试使用它来打印命令执行输出,如果输出包含特殊字符,如“”,则此函数无法正常工作,并提示“系统无法找到指定的文件”。我执行命令的代码如下:

for /f "usebackq delims=" %%a in (`dir c:\temp`) do (
    CALL :LOGDEBUG "%%a"
)
但是,当我直接使用“echo”命令而不是log函数时,可以在控制台上正确打印输出。如以下代码所示:

for /f "usebackq delims=" %%a in (`dir c:\temp`) do (
    echo %%a
)

请问问题出在哪里?如何使用log函数正确打印输出?谢谢

这是应该可以使用的批处理代码

@echo off
setlocal EnableDelayedExpansion
set "LOG_FILE=C:\program.log"
del "%LOG_FILE%" 2>nul
for /F "delims=" %%a in ('dir "C:\temp\*" 2^>nul') do call :LOGDEBUG "%%a"
endlocal
goto :EOF

:LOGDEBUG
set "StringToOutput=%~1"
echo DEBUG: !StringToOutput!
echo [%DATE% - %TIME%] DEBUG: !StringToOutput!>>"%LOG_FILE%"
goto :EOF
启用第一个延迟环境变量扩展,并创建现有环境表的副本。下文解释了为什么这样做

接下来,将具有完整路径的日志文件的名称分配给局部变量表中的环境变量。此路径中可以包含或不包含1个或多个空格。如果日志文件已存在于上一次运行中,则会删除该日志文件。如果要将新行附加到现有文件中,可以删除此代码。但是您应该在本例中添加代码,以避免日志文件永久性地增加,直到不再有可用存储空间

FOR命令执行命令DIR,并处理写入stdoutDIR输出的每一行。将跳过空行。默认分隔符是空格、制表符和换行符。这里需要的是DIR的整行,默认的分隔符列表被替换为零,这意味着只剩下换行符,循环变量
%a
始终被分配一个完整的非空行

命令DIR的输出包含
,它们被解释为命令处理器在未引用的行中找到。因此,DIR输出的行被引用传递给子例程
LOGDEBUG
。在命令提示窗口中执行
cmd/?
时,在打印到命令提示窗口的最后一个帮助页上列出了通常必须引用的字符

循环完成后,本地环境表将被删除,这意味着
LOG_FILE
StringToOutput
也将被删除,并恢复以前的环境,这通常意味着在批处理执行退出之前,延迟的扩展将再次关闭,并跳转到预定义的标签到文件末尾

子例程
LOGDEBUG
首先将传递的字符串分配给环境变量,而不使用引号,因为行中有特殊字符,如

接下来,使用延迟扩展将行写入控制台窗口,不带引号,否则
将被解释为重定向运算符,而不是字面意义上的

同一行也会写入日志文件,并在行首插入日期和时间差。代码中的
date
后的百分比符号缺失。同样,延迟扩展用于获取将字符
写入文件的行,而不被解释为重定向运算符

同样重要的是,在
>>
之前没有空格,否则日志文件中的每一行都会有一个尾随空格
2>&1
在这里是无用的,因为命令echo不会向stderr写入内容

退出子例程时,跳转到文件末尾,导致FOR命令处理下一行

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

  • 呼叫/?
  • del/?
  • dir/?
  • 获取/?
  • goto/?
  • 设置/?
当然,可以在的命令体中直接执行所有输出,而无需使用子例程

@echo off
setlocal EnableDelayedExpansion
set "LOG_FILE=C:\program.log"
del "%LOG_FILE%" 2>nul
for /F "delims=" %%a in ('dir "C:\temp\*" 2^>nul') do (
    echo DEBUG: %%a
    echo [!DATE! - !TIME!] DEBUG: %%a>>"%LOG_FILE%"
)
endlocal

尽管如此,此处仍需要延迟变量扩展,否则命令处理器将扩展
%DATE%
%TIME%
,如
%LOG\u FILE%
,以解析
定义的整个块(
在执行FOR命令之前,将导致所有行写入日志文件的日期和时间相同。

您已经自己回答了您的问题:当我直接使用“echo”命令时

资源(必读):

  • (命令参考)
  • (其他特殊性)
  • %~A
    等特殊页面)
  • (专页)

检查
%LOG\u文件%
变量是如何定义的?并尝试
echo-CALL:LOGDEBUG“%%a”
而不是
CALL:LOGDEBUG“%%a”
查看驱动器C中的卷是否为SysDisk“(:Hi@npocmaka,我希望日志文件和控制台输出都在打印前格式化。我想我不能直接使用tee.bat,对吗?Hi@JosefZ,很抱歉在代码中没有定义%log_file%变量。它只是日志文件的一个位置,c:\program.log。对于两个有问题的行的回显输出是“CALL:LOGDEBUG”2015年6月13日00:02.“和“CALL:LOGDEBUG”2015年6月13日00:02.“嗨,JosefZ&Mofi,我已经尝试了你的答案,两个都有效!”。非常感谢。我个人选择JosefZ的答案,因为我更喜欢在声明变量之前设置本地,但我认为它不是q
@ECHO OFF
SETLOCAL EnableExtensions
set "LOG_FILE=D:\tempx\program.log"     my testing value 
rem type NUL>"%LOG_FILE%"
for /f "usebackq delims=" %%a in (`dir d:\temp 2^>NUL`) do (
    CALL :LOGDEBUG "%%a"
)
rem type "%LOG_FILE%"
ENDLOCAL
exit /b

:LOGDEBUG
FOR %%A in ("%~1") do (
  @echo DEBUG: %%~A
  if NOT EXIST "%LOG_FILE%" exit /b 1
  @echo [%date% - %time%] DEBUG: %%~A >> "%LOG_FILE%" 2>&1
)
exit /b 0