为什么使用FORFILES执行批处理文件会导致空行的不需要的输出到控制台?

时间:2018-02-01 17:53:42

标签: windows batch-file cmd

我写了一个小批量代码,它使用命令 FORFILES 来循环遍历目录。操作系统是Windows 10 Pro x64,批处理文件使用cmd.exe执行。

代码是:

setLocal EnableDelayedExpansion
@echo off

SET LogFile=C:\logs
SET Days=1
SET Source=\\SERVER\SHARE\Folder

PUSHD %Source%
FOR /f %%a IN ('type %LogFile%\dirs.log') DO (
    MKDIR "%LogFile%\%%a" 2> NUL
    DEL %LogFile%\%%a\archive.log /Q 2>NUL
    ECHO     Collecting  %%a....
    FORFILES /P %%a /D -%Days% /C "cmd /C ECHO @RELPATH >> %LogFile%\%%a\archive.log" 2> NUL
)
POPD

批处理文件使用命令 FOR 从文本文件中读取要扫描的文件夹。此文本文件由以下批处理代码创建:

dir.log创建批处理代码:

PUSHD %Source%
FOR /f %%a IN ('cd') DO SET CurPath=%%a
FOR /f %%a IN ('dir /B /S /AD') DO (
    SET Directory=%%a
    SET Directory=!Directory:%CurPath%\=!
    ECHO !Directory! >> %LogFile%\dirs.log
)

POPD

文件dirs.log包含具有以下文件夹结构的文件夹列表:

Folder1
Folder2
Folder1\Subfolder1
Folder1\Subfolder2
Folder2\Subfolder1

此文本文件仅包含逐行显示的目录路径,没有空格或任何其他具有Windows命令解释程序特殊含义的字符。因此,在第一个发布的批处理文件的 FOR 循环中不使用"delims="会导致问题。

对具有适当参数的文本文件中的每个目录路径执行命令 FORFILES

批处理文件输出 FOR 的循环变量a。但是在每个批处理文件执行中,使用ECHO Collecting %%a....输出的行之间随机输出额外的空行。

FORFILES 在行动时随机将空行放入控制台。

造成这种情况的原因是什么?

1 个答案:

答案 0 :(得分:0)

此批处理代码创建文件dirs.log并运行 FORFILES ,并在将cmd.exe运行到设备<之前重定向 FORFILES 本身的空行输出强> NUL 压制它。通过将 STDERR 重定向到 STDOUT 重定向到设备 NUL ,也可以抑制 FORFILES 的错误输出。有关详细信息,请阅读有关Using Command Redirection Operators的Microsoft文章。

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "LogFilesPath=C:\logs"
set "Days=1"
set "Source=\\SERVER\SHARE\Folder"

pushd "%Source%"
if not "%CD:~-1%" == "\" ( set "CurPath=%CD%" ) else set "CurPath=%CD:~0,-1%"
del "%LogFilesPath%\dirs.log" 2>nul

setlocal EnableDelayedExpansion
rem WARNING: Directory paths with one or more exclamation marks are
rem          not correct processed by this batch code because of
rem          enabled delayed environment variable expansion.
for /F "delims=" %%I in ('dir /B /S /AD 2^>nul') do (
    set "Directory=%%I"
    set "Directory=!Directory:%CurPath%\=!"
    echo !Directory!>>"%LogFilesPath%\dirs.log"
)
endlocal

if not exist "%LogFilesPath%\dirs.log" goto ExitBatch

for /F "usebackq delims=" %%I in ("%LogFilesPath%\dirs.log") do (
    mkdir "%LogFilesPath%\%%I" 2>nul
    del "%LogFilesPath%\%%I\archive.log" 2>nul
    echo     Collecting  %%I ...
    %SystemRoot%\System32\forfiles.exe /P "%CurPath%\%%I" /D -%Days% /C "%SystemRoot%\System32\cmd.exe /C echo @RELPATH>>"%LogFilesPath%\%%I\archive.log"" >nul 2>&1
)

:ExitBatch
popd
endlocal

关于此代码的一些注意事项:

命令提示符窗口中运行set /?时命令 SET 输出的帮助在最后一个帮助页面上列出了当前目录路径的动态环境变量CD。没有解释的是%CD%扩展到当前目录路径,最后没有反斜杠,当前目录的例外是驱动器的根目录。 dirs.log创建代码中的第一个 FOR 命令行可以通过使用{strong> IF 条件的%CD%安全地替换,该条件比更快FOR 在后台循环运行cmd.exe以执行cd并捕获其输出。

DIR 命令行在以cmd.exe /C开头的后台命令进程中由 FOR 执行。 DIR 可能找不到目录,在这种情况下,会向 STDERR 输出令人困惑的错误消息。这就是在 DIR 命令行上使用2>nul的原因。重定向运算符>必须使用 FOR 命令行上的插入符^进行转义,以便在执行命令之前Windows命令解释程序处理此命令行时将其解释为文字字符FOR ,它在后台启动的单独命令进程中执行嵌入式dir命令行。

echo !Directory!>>"%LogFilesPath%\dirs.log"之间没有空格,因为这个空格也会作为尾随空格写入日志文件,这里不需要这些空间,对剩余的代码也不好。

FOR 可以逐行读取文本文件,跳过空行并使用默认选项跳过以分号开头的行。选项usebackq用于获取用双引号括起来的日志文件名,解释为文件名而不是要处理的字符串。选项delims=定义了一个空的分隔符列表,它禁用默认情况下在空格/制表符上完成的行拆分。在命令提示符窗口for /?中运行以获取有关此命令的帮助。