恢复先前的回显状态。

9
在DOS批处理程序的子程序中,我该如何关闭回显,但在返回之前将其恢复到先前的状态(打开或关闭)?
例如,如果有一个名为echo restore的命令,我会像这样使用它:
echo on
... do stuff with echoing ...
call :mySub
... continue to do stuff with echoing ...
exit /b

:mySub
@echo off
... do stuff with no echoing ...
echo restore
goto :EOF
6个回答

6

我的第一次尝试彻底失败了 - 感谢jeb指出错误。对于那些感兴趣的人,原始答案在编辑历史中可用。

如果你不介意将子程序放在单独的文件中,Aacini有一个不错的解决方案。

这是一种不需要第二个批处理文件就能运行的解决方案。而且这次它真的可以工作!:)

(编辑2 - 根据jeb的评论建议进行了优化代码)

:mysub
::Silently get the echo state and turn echo off
@(
  setlocal
  call :getEchoState echoState
  echo off
)
::Do whatever
set return=returnValue
::Restore the echo state, pass the return value across endlocal, and return
(
  endlocal
  echo %echoState%
  set return=%return%
  exit /b
)

:getEchoState echoStateVar
@setlocal
@set file=%time%
@set file="%temp%\getEchoState%file::=_%_%random%.tmp"
@(
  for %%A in (dummy) do rem
) >%file%
@for %%A in (%file%) do @(
  endlocal
  if %%~zA equ 0 (set %~1=OFF) else set %~1=ON
  del %file%
  exit /b
)

如果您愿意承担两个进程同时尝试访问同一文件的轻微风险,那么:getEchoState例程可以简化,而无需使用SETLOCAL或临时变量。

:getEchoState echoStateVar
@(
  for %%A in (dummy) do rem
) >"%temp%\getEchoState.tmp"
@for %%A in ("%temp%\getEchoState.tmp") do @(
  if %%~zA equ 0 (set %~1=OFF) else set %~1=ON
  del "%temp%\getEchoState.tmp"
  exit /b
)

2
好主意,但是行不通!因为在 for /f "tokens=3 delims=. " %%A in ('echo') 中的 echo 将在新的 cmd 上下文中执行,并且始终为 ON。第二个问题是即使你得到了状态,它也是本地化的,因为在我的系统上我得到了 ECHO ist eingeschaltet (ON). - jeb
当然,你是正确的,该死 :) 最糟糕的部分是,我很久以前就已经发现了所有这些问题(除了德语问题),但是忘记了!我会想出一个可行的版本。 - dbenham
你可以使用重定向 echo>tmpfile.tmp,然后再使用 FOR 循环。也许最后一个标记总是 ON/OFF 或 (ON)/(OFF)。 - jeb
1
现在 call :getEchoState2 可以被优化为 FOR %%A in (dummy) do rem,因此不需要调用/标签。 - jeb

5
最简单的方法是一开始就不要关闭回显。相反,对于子例程中的所有命令都像使用“echo off”行一样,在其前面加上“@”符号。这将关闭该命令的回显效果,但保留未来命令的回显状态。
如果使用执行其他命令的命令(如IF或DO),还需要在子命令前缀中加上@,以防止它们在打开回显时被打印出来。

3
在调试期间,您希望看到打印的命令,但在发布后不需要。频繁地在奇数位置上添加和删除“@”符号会导致维护方面的一些问题。 - bobbogo

5
最简单的方法是将子程序提取到另一个 .bat 文件中,并通过 CMD /C 调用它,而不是使用 CALL,如下所示:
echo on
... do stuff with echoing ...
cmd /C mySub
... continue to do stuff with echoing ...
exit /b

mySub.bat:

@echo off
... do stuff with no echoing ...
exit /b

这样,回显状态将会自动恢复到执行CMD /C时的值;这种方法唯一的缺点是执行速度稍慢...


1

我并不满意上面的解决方案,尤其是因为语言问题。我发现只需将当前的echo设置与明确设置为OFF时的结果进行比较,就可以找到非常简单的解决方案。具体操作如下:

::  SaveEchoSetting
::  :::::::::::::::::::::::::::
::  Store current result    
@echo>  %temp%\SEScur.tmp

::  Store result when explicitly set OFF
@echo   off
@echo>  %temp%\SESoff.tmp

::  If results do not match, it must have been ON ... else it was already OFF
@for /f "tokens=*" %%r in (%temp%\SEScur.tmp) do (
    @find   "%%r" %temp%\SESoff.tmp > nul
    @if     errorlevel 1 (
        @echo   @echo on > %temp%\SESfix.bat
    ) else (
        @echo   @echo off > %temp%\SESfix.bat
    )
)

::  
::  Other code comes here
::  Do whatever you want with echo setting ...
::

::  Restore echo setting
@call   %temp%\SESfix.bat

1

这里有一个简单的解决方案,它依赖于单个临时文件(使用%random%避免竞争条件)。 它有效,并且至少是本地化抵抗的,即对@JoelFan和@jeb提到的两种已知情况均有效。

@set __ME_tempfile=%temp%\%~nx0.echo-state.%random%.%random%.txt
@set __ME_echo=OFF
@echo > "%__ME_tempfile%"
@type "%__ME_tempfile%" | @"%SystemRoot%\System32\findstr" /i /r " [(]*on[)]*\.$" > nul
@if "%ERRORLEVEL%"=="0" (set __ME_echo=ON)
@erase "%__ME_tempfile%" > nul
@::echo __ME_echo=%__ME_echo%
@echo off
...
endlocal & echo %__ME_echo%
@goto :EOF

为了增加解决方案的鲁棒性,请添加此预处理代码(尽管不必要的可能性很高):

@:: 定义TEMP路径 @if NOT DEFINED temp ( @set "temp=%tmp%" ) @if NOT EXIST "%temp%" ( @set "temp=%tmp%" ) @if NOT EXIST "%temp%" ( @set "temp=%LocalAppData%\Temp" ) @if NOT EXIST "%temp%" ( @exit /b -1 ) :__ME_find_tempfile @set __ME_tempfile=%temp%\%~nx0.echo-state.%random%.%random%.txt @if EXIST "%__ME_tempfile%" ( goto :__ME_find_tempfile )

0
我正在寻找解决同样问题的相同方案,在阅读了您的评论之后,我有了一个想法(这不是对问题的答案,但对我的问题来说更好)。
我对cmd.exe /c mysub.cmd不满意,因为它使返回变量变得困难甚至不可能(我没有检查)-(不能发表评论,因为这是我第一次在这里发布 :)
相反,注意到我们最终想要的是抑制标准输出:
echo on

rem call "mysub.cmd" >nul
call :mysub >nul

echo %mysub_return_value%

GOTO :eof 

:mysub
    setlocal
    set mysub_return_value="ApplePie"
    endlocal & set mysub_return_value=%mysub_return_value%
GOTO :eof

使用带标签的子程序时,它能正常工作,子程序包含在.cmd文件中,我想即使是使用cmd.exe /c变体(或者start),也应该能正常工作。

它还有一个优点,我们可以保留或丢弃stderr,用>nul替换为>nul 2>&1

我注意到ss64.com 会吓唬像我这样的孩子,声称用call "重定向 & | <> 也不能按预期工作"。 这个简单的测试按预期工作。他可能在考虑更复杂的情况。


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