恢复以前的回声状态

时间:2012-03-06 22:10:45

标签: batch-file subroutine

在DOS Batch File子程序中,如何在子程序中关闭echo,但在返回之前,将其恢复到以前的状态(打开或关闭)?

例如,如果有一个名为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 个答案:

答案 0 :(得分:5)

我的第一次尝试是完全失败 - 感谢jeb指出错误。对于那些感兴趣的人,原始答案可以在编辑历史中找到。

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

这是一个无需第二批文件即可运行的解决方案。它实际上这次工作! :)

(根据jeb在评论中的建议编辑2 - 优化代码)

: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
)

答案 1 :(得分:4)

最简单的方法是将子例程提取到另一个.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时的值;这种方法的唯一缺点是执行速度稍慢......

答案 2 :(得分:2)

最简单的方法是首先不要关闭回声

相反,使用echo off行执行当前对子例程的其余部分执行的操作 - 在子例程中使用@符号作为前缀。这具有关闭该命令的回声的效果,但保留未来命令的回声状态。

如果使用执行其他命令的命令(如IF或DO),则还需要在“子命令”前加上@,以防止在打开echo时打印它们。

答案 3 :(得分: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

添加此初步代码以提高解决方案的稳健性(尽管奇数很高,但没有必要):

@:: define TEMP path
@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 )

答案 4 :(得分:1)

由于语言问题,我对上述解决方案并不满意,只是通过将当前回声设置的结果与显式设置为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

答案 5 :(得分:0)

我正在为同样的问题找到相同的解决方案,在阅读你的评论后我有了一个想法(这不是问题的答案,但我的问题更好)。

我对cmd.exe /c mysub.cmd不满意,因为它很难甚至不可能返回变量(我没有检查) - (无法评论,因为这是我第一次在这里发帖:)

相反注意到我们想要的一切 - 最终 - 是压制stdout:

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变体(或启动)也能正常工作。 它还有一个优点,我们可以保留或丢弃stderr,用>nul替换>nul 2>&1

我注意到像我这样的ss64.com scares小孩说,通过电话“重定向与& |<>也无法按预期工作”。 这个简单的测试按预期工作。他一定是在考虑更复杂的情况。