Windows 批量回显输出到 LOG 结果频繁 "The process cannot access the file because it is being used by another process."

Windows Batch Echo Output to LOG Results in Frequent "The process cannot access the file because it is being used by another process."

我有一个基本的 Windows 批处理脚本,比较两个文件的散列值,吐出两个 txt 文件中都不存在的散列值。

首先它生成一个没有 headers 的干净文件并忽略像 Thumbs.db 或 desktop.ini 这样的杂项文件并将它们存储在“#_file1_clean.txt”, “#_file2_clean.txt”。这很好用。

然后我使用那个干净的文件生成一个只包含散列的日志文件。这就是问题所在,仅从 ECHO %%b>>logfile.txt 语句开始,它偶尔会抛出“该进程无法访问该文件,因为它正被另一个进程使用”。错误。

然后我使用findstr命令输出不匹配的行。这很好用。

代码如下:

@ECHO OFF
SET "batchpath=%~dp0"
CD /D "%batchpath%"

ECHO Cleaning up temp log files
del #_*.txt 2>NUL
timeout 2

REM *** ENTER TWO HASHLOGS TO COMPARE ***
set "file1=LOGS\hashlog_syno_archive.txt"
set "file2=LOGS\hashlog_Win_archive.txt"

CALL :SETSRC1 "%file1%"
CALL :SETSRC2 "%file2%"

findstr /G:"exclude.txt" /V "%file1%" > #_%fname1%_clean.txt
findstr /G:"exclude.txt" /V "%file2%" > #_%fname2%_clean.txt

CLS
FOR /F %%a in ('Find "" /v /c ^< "#_%fname1%_clean.txt"') DO (SET /a "line1=%%a")
ECHO Number of files to process in %file1%: %line1%

FOR /F %%a in ('Find "" /v /c ^< "#_%fname2%_clean.txt"') DO (SET /a "line2=%%a")
ECHO Number of files to process in %file2%: %line2%

TIMEOUT 3

ECHO,
ECHO Extracting %line1% Hashes from '%file1%'
FOR /F "usebackq tokens=1,2,3* delims=," %%a in ("#_%fname1%_clean.txt") do (ECHO %%b>>"#_hash1.txt")

ECHO,
ECHO Extracting %line2% Hashes from '%file2%'
FOR /F "usebackq tokens=1,2,3* delims=," %%a in ("#_%fname2%_clean.txt") do (ECHO %%b>>"#_hash2.txt")


ECHO,
ECHO Extracting NON-MATCHING Hashes
findstr /G:"#_hash1.txt" /V /I /L "#_%fname2%_clean.txt" > #_HASH_IN_%fname2%_NOT_IN_%fname1%.txt
findstr /G:"#_hash2.txt" /V /I /L "#_%fname1%_clean.txt" > #_HASH_IN_%fname1%_NOT_IN_%fname2%.txt

ECHO,
ECHO **COMPLETE**

GOTO :END

:SETSRC1
SET "fname1=%~n1"
GOTO :EOF

:SETSRC2
SET "fname2=%~n1"
GOTO :EOF

:END
PAUSE

比较的输入文件的文件大小为数字、哈希值、文件名,如下所示(来自日志的示例):

228825,91eaf030a59ee15f3846b25454350f29,Documents/Computer Review/P150SM-A/titanfall max settings no AA gpuz.jpg
14795,8c0c1533f1ee0ae0bf67235f8439d552,Documents/Computer Review/P150SM-A/charts/cpu cinebench.jpg
30590,673bd509c401b4b405243dc7a2fda73f,Documents/Computer Review/P150SM-A/charts/bf4 - fps.jpg
14026,be371bc60dbe70cc5e4667e11914ffbc,Documents/Computer Review/P150SM-A/charts/cpu fritz.jpg
13522,8dae26001302effaa3dacd93372d805a,Documents/Computer Review/P150SM-A/charts/cpu wprime.jpg
15666,f45893ec97e3e1177aa563cdd4f4f714,Documents/Computer Review/P150SM-A/charts/cpu 7zip.jpg
8463,351834a1d43c6181864d8647892864d9,Documents/Computer Review/P150SM-A/charts/game coh2.jpg
14711,cdc011f776b48148f51acc40e6c769eb,Documents/Computer Review/P150SM-A/charts/cpu x264.jpg

所以它只是将 md5 哈希值提取为 %%b。

问题是 有时 我收到错误“该进程无法访问该文件,因为它正被另一个进程使用。”我已将范围缩小到 ECHO %%b>>"#_hash1.txt"(或 hash2.txt)。这会导致丢失的行输出到日志。

这是唯一的批处理文件 运行,唯一会接触这些文件的进程。我在另一台 PC 上试过 运行,结果相同。问题是它是零星的。不是所有的时间。有时一行,有时多行,并不总是同一行。

这似乎应该是一个简单的过程,但回显到日志文件似乎导致了问题,我不知道为什么。

感谢您的帮助。

下面两条命令行是已经分析正确的问题:

FOR /F "usebackq tokens=1,2,3* delims=," %%a in ("#_%fname1%_clean.txt") do (ECHO %%b>>"#_hash1.txt")
FOR /F "usebackq tokens=1,2,3* delims=," %%a in ("#_%fname2%_clean.txt") do (ECHO %%b>>"#_hash2.txt")

原因是 Windows 命令处理器打开输出文件,将行附加到文件,并在每次必须将行附加到文件时关闭输出文件。这使得后台的另一个进程 运行 像 anti-virus 应用程序一样也可以打开文件进行读取,并在 cmd.exe 关闭文件后再次打开文件之前对读取的数据进行处理下一次数据写入。另一个进程现在阻止 cmd.exe 在下一行再次打开文件以附加到输出文件。

解决方法是使用下面两条命令行:

(FOR /F "usebackq tokens=2 delims=," %%I in ("#_%fname1%_clean.txt") do ECHO %%I)>"#_hash1.txt"
(FOR /F "usebackq tokens=2 delims=," %%I in ("#_%fname2%_clean.txt") do ECHO %%I)>"#_hash2.txt"

在这种情况下,Windows 命令处理器在 运行 命令 FOR 之前创建新的输出文本文件,并在处理时保持输出文件永久打开使用 FOR 循环输入文本文件并将行附加到输出文本文件。这不仅效率更高,而且还可以防止其他进程打开行间输出文件。

改进批处理文件代码的更多提示:

    第六行的
  1. timeout 2 完全没用。它被添加为 TIMEOUT 3 用于一些在发布代码之前忘记删除这两行的故障排除。

  2. CALL :SETSRC1 "%file1%" 和整个子例程 SETSRC1 可以用单行替换:

    FOR %%I IN ("%file1%") DO SET "fname1=%%~nI"
    

    这样效率更高,因为 cmd.exe 必须从批处理文件中读取这一行,以获取使用字符串 hashlog_syno_archive 定义的环境变量 fname1。下一行和子程序 SETSRC2 可以替换为:

    FOR %%I IN ("%file2%") DO SET "fname2=%%~nI"
    
  3. SET /a "line1=%%a"可以简化为SET "line1=%%a",也可以将SET /a "line2=%%a"简化为SET "line2=%%a"。无需使用 算术表达式 将分配给循环变量 a 的数字字符串转换为 32 位有符号整数,然后将整数转换回相同的数字字符串分别定义环境变量line1和数字字符串line2

  4. ECHO, 应替换为 ECHO/ECHO(。原因请阅读 DosTips 论坛主题:ECHO. FAILS to give text or blank line - Instead use ECHO/

  5. FOR /F 选项 tokens=1,2,3* 可以缩短为 tokens=1-3* 尽管更好的用法是 tokens=2 因为只有第二个逗号分隔子串(令牌)是有趣的,其他的 none。

  6. 终于要读了.