【问题标题】:Issue with output redirection in batch批量输出重定向问题
【发布时间】:2013-12-30 23:55:35
【问题描述】:

我有一个脚本 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)正在运行。 我没有排除这种行为,因为我认为只有启动命令本身的输出将被重定向到 LOG_FILE_NAME 日志文件,而实际运行 RunForEver.exe 可执行文件的其他进程的输出将被写入 ANOTHER_LOG_FILE_NAME。 结果,a.cmd 中的第 5 行出错,拒绝访问 LOG_FILE_NAME。

有人可以解释发生了什么吗?有没有办法避免这种情况?

我尝试从 b.cmd 内部将输出重定向到 LOG_FILE_NAME,但随后在 b.cmd 的第 2 行出现访问被拒绝错误。

提前致谢!

【问题讨论】:

标签: cmd io-redirection


【解决方案1】:

哇!这是一个令人着迷和令人不安的发现。

我没有解释,但我有解决办法。

在永无止境的进程开始后,只需避免任何额外的重定向到 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 进程在 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。

【讨论】:

  • 非常感谢您的详细解答。我使用了第二种方法(使用子程序),现在正在显示 START 后的输出。但由于某种原因,第一个日志文件仍处于锁定状态。
  • @user1039580 - 是的,我相信第一个日志将保持锁定状态,直到已启动的“永无止境”进程终止。这对我来说没有意义,但它似乎是它的工作方式。
猜你喜欢
  • 2023-04-02
  • 2012-07-23
  • 2011-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多