![](/img/trans.png)
[英]Why does execution in shell give different result that execution in batch script?
[英]Why does execution of batch file with FORFILES result in unwanted output of empty lines to console?
我寫了一個小批量代碼,它使用命令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在運行時將空行隨機放入控制台。
這是什么原因造成的?
此批處理代碼創建文件dirs.log
並運行FORFILES ,並在運行cmd.exe
到設備NUL以抑制它之前將FORFILES本身的空行輸出重定向。 FORFILES可能的錯誤輸出也通過將其從STDERR重定向到STDOUT被重定向到設備NUL來抑制。 有關詳細信息,請閱讀有關使用命令重定向運算符的 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命令行可以安全地替換為使用%CD%
和IF條件,這比在后台運行cmd.exe
執行cd
並捕獲其輸出的FOR循環更快。
DIR命令行由FOR在以cmd.exe /C
啟動的后台命令進程中執行。 DIR可能找不到目錄,在這種情況下,會向STDERR輸出令人困惑的錯誤消息。 這就是在DIR命令行上使用2>nul
的原因。 重定向運算符>
必須在FOR命令行上使用脫字符^
進行轉義,以便在 Windows 命令解釋器在執行命令FOR之前處理此命令行時解釋為文字字符,該命令使用在后台啟動的單獨命令進程執行嵌入式dir
命令行。
echo !Directory!
和>>"%LogFilesPath%\dirs.log"
因為這個空間也將作為尾隨空間寫入日志文件,這在此處不需要並且對剩余代碼不利。
FOR可以逐行讀取文本文件本身,跳過空行並使用默認選項跳過以分號開頭的行。 選項usebackq
用於獲取用雙引號括起來的日志文件名,解釋為文件名,而不是要處理的字符串。 選項delims=
定義了一個空的分隔符列表,默認情況下禁用空格/制表符上的行拆分。 在命令提示符窗口for /?
尋求有關此命令的幫助。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.