批处理文件中的echo on/off无法正常工作

5

我不太习惯制作批处理文件,所以可能做错了什么,但我真的在echo off/on方面遇到了很大的问题。(话虽如此,如果您发现我漏掉了其他错误,请随时指出)

这是我代码的简化版本:

@echo off
setlocal
set args=
set dir="."

:getargs
IF "%2"=="" (
    set dir="%1"
    goto callbatch
)
set args=%args% %1
shift
goto getargs

:callbatch
for %%f in (%dir%\*.txt) do (
    echo processing %%f
    "%BATCHHOME%\batch.bat %args% %%f
    echo
)

基本上,我有一个在BATCHHOME中的批处理文件,它对单个txt文件执行某些操作,我想生成一个批处理文件,以处理给定目录中的所有txt文件。循环中的孤独回声是为了更好地说明问题。

现在,这就是我的问题所在。其输出结果看起来像:

processing foo\text1.txt
some output from batch.bat
ECHO is on.

C:\somedir>(
echo processing foo\text2.txt
   "C:\the\path\of\batch.bat" the arguments here foo\text2.txt
)
ECHO is on.

C:\somedir>(
echo processing foo\text3.txt
   "C:\the\path\of\batch.bat" the arguments here foo\text3.txt
)
ECHO is on.

(etc)

好的...我不知道为什么我调用的批处理文件会打开echo,但我并没有制作它,所以我每次只需将其关闭!我将最后一部分修改为:

:callbatch
for %%f in (%dir%\*.txt) do (
    echo processing %%f
    "%BATCHHOME%\batch.bat %args% %%f
    echo off
)
echo
echo on
echo
echo Finished!

(再次强调,这两个“echo”是为了调试而存在的)所以现在输出看起来像这样:
processing foo\text1.txt
some output from batch.bat
processing foo\text2.txt
some output from batch.bat
processing foo\text3.txt
some output from batch.bat
(etc.)
ECHO is off.
ECHO is on.
Finished!

这几乎是完美的,但有些奇怪。如果echo打开了,为什么会显示“Finished!”而不是“echo Finished!”?然而更大(但相关)的问题是,当批处理文件完成时,我不再显示路径。换句话说,我不再拥有:
C:\Somedir>_

我只是得到了
_

我需要手动输入“echo on”才能再次显示路径。我做错了什么?
编辑:为了澄清,我知道单独使用“echo”会打印echo的当前状态。那正是我的意图。在第一段代码中,它们是用来显示在调用其他批处理文件后echo神秘地开启的。在第二个代码中,它是用来显示在批处理文件结束时echo处于开启状态,但行为却好像没有这样。这就是我的问题所在。

如果您想输出换行符,必须使用 echo. 单独的 echo 命令会显示当前的回显设置。 - wmz
@wmz:这是有意的。我使用它们来帮助你理解问题出在哪里。例如,在第二段代码中,它显示"ECHO is on.",但实际输出的是"Finished!"而不是像应该的那样输出"echo Finished!",如果echo确实是打开的话。 - PhilBel
1个回答

16
我现在看到了你的问题 - 这是一个有趣的问题。你正在批处理文件中调用另一个批处理文件,但没有使用 CALL。
如果批处理 A 在没有使用 CALL 的情况下执行批处理 B,那么一旦批处理 B 完成,整个批处理过程就会终止 - 批处理 A 将不会在离开时继续进行。一旦批处理终止,ECHO 状态将返回到在批处理开始之前存在的原始状态,通常为打开状态。
但你的情况略微复杂,因为你在 FOR 循环内部调用第二个批处理文件。因此,即使批处理已经终止,FOR 循环仍然会执行缓存在内存中的 DO 命令。这些命令现在在具有 ECHO 打开的命令行上下文中执行。
在你的第二种情况下,你在 FOR DO 块中明确关闭了 ECHO。在第一个 "call" 之后,你再次处于命令行上下文中,然后你关闭了 ECHO,因此你需要手动打开 ECHO。
我不确定 FOR 循环后面的代码是如何被执行的。你说你已经简化了代码,所以我假设你实际运行的代码中也有某种代码块中的其他行。这些行将被缓冲并且在批处理终止后仍将执行。这也可以解释为什么打印 "Finished!" 的行没有被回显,尽管 ECHO 打开了。代码块中的所有命令使用进入最外层代码块之前存在的 ECHO 状态。唯一的例外是 FOR DO 块使用进入 DO 块之前存在的 ECHO 状态。
你张贴的两个代码集都无法运行,因为每个代码集中“调用”第二个批处理文件的行都存在不平衡的引号。
修复方法是在执行第二个批处理文件的语句中简单地添加 CALL。然后,批处理将继续运行,ECHO 状态将保持关闭,并且父级批处理将适当地恢复到离开时的状态。
我还改进了参数解析逻辑和引号的使用。你应该使用 ~ 从每个参数中去除引号,然后再显式添加你自己的引号。该参数可能已经有它自己的引号。任何时候当一个文件名或路径作为参数传递时,应该对其加上引号,以防它包含空格或特殊字符。你需要跟踪变量中的值是否被引用。通常情况下,我发现将变量值保持未引用,然后在需要时显式添加引号更容易些。
@echo off
setlocal
set args=
set "dir=."

:getargs
IF "%~2"=="" (
    if "%~1" neq "" set "dir=%~1"
    goto callbatch
)
set args=%args% %1
shift
goto getargs

:callbatch
for %%f in ("%dir%\*.txt") do (
    echo processing %%f
    call "%BATCHHOME%\batch.bat" %args% "%%f"
    echo
)

不...没有文本的回显确实是为了显示当前状态并显示问题所在。如果需要,可以使用调试输出。在代码的第二个版本中,它们显示“echo on”命令已经执行(因为它打印“ECHO is on.”),但行为仍然像echo关闭一样(即,它打印“Finished!”而不是“echo Finished!”,并且一旦批处理文件完成,它不会恢复路径)。 - PhilBel
@PhilBel - 我现在明白发生了什么。请查看我的修订答案。 - dbenham
太棒了!谢谢,那确实解决了问题。供参考,是的,for循环和后面的内容实际上在IF块中。至于引号部分,我也会修复它。谢谢! - PhilBel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接