我有一个脚本a.cmd,它调用另一个脚本b.cmd,并重定向其输出。被调用的脚本,启动永不终止的可执行文件。可执行文件的输出重定向到其自己的日志文件。简化代码:
a.cmd:
[1] @ECHO OFF
[2] SET LOG_FILE_NAME="log.txt"
[3] REM Start the b.cmd redirecting all output
[4] CALL b.cmd >> %LOG_FILE_NAME% 2>&1
[5] ECHO returned to a.cmd >> %LOG_FILE_NAME% 2>&1
[6] EXIT /B 0
b.cmd:
[1] @ECHO OFF
[2] SET ANOTHER_LOG_FILE_NAME="log2.txt"
[4] ECHO RunForEver.exe redirecting all output
[5] START CMD /C "RunForEver.exe >> %ANOTHER_LOG_FILE_NAME% 2>&1"
[6] ECHO b.cmd execution complete
[7] EXIT /B 0
(为方便起见添加了行号)
我遇到的问题是b.cmd中的第4行似乎抓住了初始日志文件(LOG_FILE_NAME)上的句柄,因为所有b.cmd输出都被重定向到它,并且在可执行文件时没有释放句柄(以及启动它的cmd)正在运行。 我没有这个行为,因为我认为只有start命令本身的输出将被重定向到LOG_FILE_NAME日志文件,而实际运行RunForEver.exe可执行文件的另一个进程的输出将被写入ANOTHER_LOG_FILE_NAME。 因此,a.cmd中的第5行错误输出,拒绝访问LOG_FILE_NAME。
有人可以解释发生了什么吗?有没有办法避免这种情况?
我尝试从b.cmd中将输出重定向到LOG_FILE_NAME,但后来我在b.cmd的第2行中得到了拒绝访问错误。
提前致谢!
答案 0 :(得分:7)
哇!这是一个令人着迷和令人不安的发现。
我没有解释,但我确实有解决方案。
在永不结束的进程开始后,简单地避免对log.txt进行任何额外的重定向。这可以通过将带括号的代码块重定向一次来完成。
@ECHO OFF
SET LOG_FILE_NAME="log.txt"
>>%LOG_FILE_NAME% 2>&1 (
CALL b.cmd
ECHO returned to a.cmd
)
EXIT /B 0
或者改为重定向CALLed子例程的输出。
@ECHO OFF
SET LOG_FILE_NAME="log.txt"
call :redirected >>%LOG_FILE_NAME% 2>&1
EXIT /B 0
:redirected
CALL b.cmd
ECHO returned to a.cmd
exit /b
如果您需要有选择地重定向a.cmd中的输出,那么只需将非标准流重定向到您的文件一次,然后在该块内选择性地将输出重定向到非标准流。
@ECHO OFF
SET LOG_FILE_NAME="log.txt"
3>>%LOG_FILE_NAME% (
echo normal output that is not redirected
CALL b.cmd >&3 2>&1
ECHO returned to a.cmd >&3 2>&1
)
EXIT /B 0
同样,可以使用CALL而不是带括号的块来完成相同的技术。
我开发了一个简单的,自包含的TEST.BAT脚本,任何人都可以运行来演示这个问题。我在我的机器上称它为TEST.BAT。
@echo off
del log*.txt 2>nul
echo begin >>LOG1.TXT 2>&1
call :test >>LOG1.TXT 2>&1
echo end >>LOG1.TXT 2>&1
exit /b
:test
echo before start
>nul 2>&1 (
echo ignored output
start "" cmd /c "echo start result >LOG2.TXT 2>&1 & pause >con"
)
echo after start
pause >con
exit /b
主要和STARTed进程都暂停,因此允许我选择首先完成哪个进程。如果STARTed进程在master之前终止,那么一切都按预期工作,如主控制台窗口的以下输出所示。
C:\test>test
Press any key to continue . . .
C:\test>type log*
LOG1.TXT
begin
before start
after start
end
LOG2.TXT
start result
C:\test>
以下是在STARTed进程终止之前允许主进程继续时会发生什么情况的示例:
C:\test>test
Press any key to continue . . .
The process cannot access the file because it is being used by another process.
C:\test>type log*
LOG1.TXT
begin
before start
after start
LOG2.TXT
start result
C:\test>
我发现行为令人不安的原因是我无法理解STARTed进程与LOG1.TXT有何关系。当START命令执行时,所有标准输出都被重定向到nul,所以我不明白新进程如何知道LOG1.TXT,更不用说它如何建立对它的独占锁定。 echo ignored output
没有可检测输出的事实证明标准输出已成功重定向到nul。