当我的Windows .bat被CALL时,ECHO是开启还是关闭的?

3

当我从一个Windows的.bat文件调用另一个文件时,被调用的文件可以知道调用方中ECHOON还是OFF吗?

进入一个.bat文件时,ECHO设置为ON(即使在CALLER中它是OFF)。

我希望保留调用者的ECHO状态,以便我可以从同一位置控制两个文件(可能是超过两个嵌套调用)。

我可以传递参数或设置自己的环境变量,但是否有更好的方法?

编辑 为了澄清需要的内容: callee.cmd 需要在其运行期间仅关闭 echo。返回到其调用方后,它必须恢复调用方原有的 echo 状态。

请在以下脚本中填写 ?????:

callee.cmd:

@rem save the echo state
@ ???? HOW???? ????
@ set "savedEchoState=??????"

@rem the following commands must not be echoed
@echo off
command1
command2
command3

@rem restore the previous echo state
echo %savedEchoState%

caller1.cmd:

@echo off
call callee.cmd
echo

caller2.cmd:

@echo on
call callee.cmd
echo

需要的输出:

caller1.cmd 必须打印 echo off,caller2.cmd 必须打印 echo on

能否在不创建任何磁盘文件的情况下完成?


3
当输入.bat文件时,ECHO被设置为ON(即使在调用者中它是OFF)--> 这不是真的。如果在调用者中关闭了ECHO,则会保持关闭状态。然而,这是一个重要的问题。我们倾向于在批处理文件中放置@echo off,因为在交互提示符下echo是开启的。但是,如果我们故意在调用者批处理中打开echo,并调用以@echo off开头的此批处理文件,则在退出之前我们需要将echo重新设置为on,以便调用者保持其echo-on状态。 - Klitos Kyriacou
1
谢谢Klitos,你当然是对的。我的.bat文件调用了echo off。如果我直接调用它,那通常是我想要的,但如果我从另一个打开echo(用于调试)的.bat文件中调用它,我希望在被调用的.bat文件中保持echo开启状态。 - Denis Howe
@klitoskyriacou 这个修改很有道理,但在我的例子中原则仍然是相同的。我会进行更改并更新。 - Gerhard
5个回答

5
这是一个棘手的问题。任何涉及管道或for的内容都无法正常工作,因为会生成新的进程,从而使echo状态无效。使用临时文件可能是可行的:
A.BAT:
echo on
echo
call b.bat
echo off
echo
call b.bat
echo

B.BAT:

@(>tmp echo)
@type tmp|find "ON" >nul && (@set "oldEcho=ON") || (@set "OldEcho=OFF")
@echo off
echo B:echo was previously %OldEcho%
REM insert your payload here
REM restore previous Echo Status:
echo %OldEcho%

结果:

C:\Folder>a

C:\Folder>echo on

C:\Folder>echo
ECHO ist eingeschaltet (ON).

C:\Folder>call b.bat
B:echo was previously ON

C:\Folder>echo off
ECHO ist ausgeschaltet (OFF).
B:echo was previously OFF
ECHO ist ausgeschaltet (OFF).

请参见我在for循环中如何使用的示例。 - Gerhard
它可以在英语和至少德语系统中使用,但我不确定它是否在所有可能的语言中稳定。 它能够与非拉丁字符集系统一起工作吗?也可能存在包含“on”的单词的语言,例如“ECHO eston deactivon(OFF)”(示例较差)。 - jeb
是的,我测试过了,它确实有效,但是我只基于英文进行了测试。无论如何,我不会删除这个答案,它可能在某些地方有用。谢谢。 - Gerhard
1
@GerhardBarnard,你可以让它在非英语的Windows上运行:只需将字符串括在引号中:set "echos=%%a"和`echo"%echos%"(顺便说一句:这不应该是延迟的吗?) - Stephan
延迟更好。是的,我会添加引号以适应其他语言。谢谢。 - Gerhard

3

为了在不依赖于Windows语言的情况下检测ECHO状态,您可以使用一个临时文件。
当ECHO关闭时,该文件为空;否则,它包含ECHO IS ON的语言字符串。

@echo off
echo Testing detectEchoState

echo on
@call :detectEchoState

@echo off
@call :detectEchoState off
exit /b


:detectEchoState
@(
    (
        FOR /F %%L in ("1") do REM
    ) > "%TEMP%\echoCheck.tmp"

    FOR /F "delims=" %%F in ("%TEMP%\echoCheck.tmp") do @if %%~zF == 0 (set "EchoState=OFF" ) ELSE (set "EchoState=ON")
    del "%TEMP%\echoCheck.tmp"
    echo EchoState !EchoState!
)
@exit /b

Win10无需创建文件的解决方案(需要vt100)

@echo off

for /F "delims=#" %%a in ('prompt #$E# ^& for %%a in ^(1^) do rem') do set "ESC=%%a"

echo OFF
@call :get_echo_state
@echo state=%state%

echo ON
@call :get_echo_state
@echo state=%state%
@exit /b

:get_echo_state
@(
    setlocal EnableDelayedExpansion
    <nul set /p "=%ESC%7%ESC%[1H"
    set "prompt=%ESC%["
    set "0=call "
    for %%a in (1) do !0!
    <nul set /p "=%ESC%[6n%ESC%8"
    endlocal
) > CON
@(
    set "state=on"
    pause < con > NUL
    pause < con > NUL
    for /F "tokens=1 skip=1 eol=" %%C in ('"replace /w /u ? . < con"') do @(
        if "%%C" == "1" @set "state=off"
    )
    exit /b
)

此代码使用ansi转义序列将光标移动到第一行。
echo关闭时,光标不会改变位置,否则它会移动到下一行。
然后,代码读取光标所在行的行号并恢复原始光标位置。
状态仅取决于检索到的行号。


1
@GerhardBarnard 由于没有文件,因此无法获取“ECHO状态”。使用“FOR”或“pipe”进行提取将始终导致“ECHO ON”状态,因为两者都会启动新的cmd.exe实例。对于“KEYS”或“VERIFY”的状态也存在同样的问题,请参见KEYS as boolean variable... - jeb
1
@GerhardBarnard 请尝试使用Stephan的帖子中的示例A.BAT文件进行检查,只需将call b.bat更改为call bat1.bat - jeb
我在想是否可以通过将输出重定向到不可写入的内容,然后检测错误,以避免创建文件。我对像C:\hiberfil.sys这样的文件进行了一些不成功的测试。这样做失败了,因为当该文件被重定向打开时,会发生写保护错误,而不是在写入时发生。所以我们需要一个可以以写模式打开但不能被写入的文件,例如完整卷等。 - Jean-François Larvoire
@Jean-FrançoisLarvoire 您可以将流重定向到流0 1>&0,这只会在echo on的情况下输出一些错误文本,但我无法检测到错误。请参见Dostips:Keys as boolean ...。正如dbenham在I/O error detection is broken中所写的那样。 - jeb
@Jean-FrançoisLarvoire 我添加了一种解决方案,避免使用临时文件,只需使用ANSI转义序列即可。 - jeb

1

我曾经遇到过同样的问题,最终来到了这个页面。
这是 jeb 的脚本的稍微改进版本。

  • 它使用 (call,) 而不是 rem,这样就可以避免将 ) 放在单独一行上。
  • 它不依赖于延迟扩展对测试的开启。
@echo off
call :TestEchoState off
call :TestEchoState on
exit /b

:TestEchoState %1=ON or OFF
echo %1
@echo
@call :GetEchoState STATE
@echo off
echo STATE=%STATE%
exit /b

:GetEchoState %1=OUTVAR
@for /f %%F in ("%TEMP%\EchoState%PID%.tmp") do @(
  (FOR /f %%N in ("1") do call,) > "%%F"
  for %%S in ("%%F") do @if %%~zS==0 (set "%1=OFF") else (set "%1=ON")
  del "%%F"
)
@exit /b

如果您只想测试错误级别,那么可以使检测程序更短:

@echo off
call :TestEchoLevel off
call :TestEchoLevel on
exit /b

:TestEchoLevel %1=ON or OFF
echo %1
@echo
@call :GetEchoLevel
@echo off
if errorlevel 1 (echo STATE=ON) else (echo STATE=OFF)
exit /b

:GetEchoLevel # Returns errorlevel 0 if echo is off
@for /f %%F in ("%TEMP%\EchoLevel%PID%.tmp") do @(
  (for /F %%N in ("1") do call,) > "%%F"
  for %%S in ("%%F") do @del "%%F" & exit /b %%~zS
)

1
Stephan编辑答案,考虑到回声状态也可以用小写字母报告,并且他的答案不允许编辑。以下是前两行和最后一行保存和恢复回声状态。第3至6行应替换为您的代码:
@(>tmp echo)
@type tmp|find /i "ON" >nul && (@set "oldEcho=ON") || (@set "OldEcho=OFF")
    @echo off                               &::line 3
    echo B:echo was previously %OldEcho%    &::line 4
    REM insert your payload here            &::line 5
    REM restore previous Echo Status:       &::line 6
echo %OldEcho%

我不会依赖关键字 ON,只需使用 @(FOR /F %%L in ("1") do call,) >tmp。如果使用 ECHO OFF,文件将为空。 - jeb
为什么要用 type file | find ...,直接用 find ... < file 不就行了吗?实际上,像许多命令一样,find 都可以将输入文件作为参数,因此你甚至不需要 < - Denis Howe

0

_echoStatus.bat

@call :call_echo>%~dp0echo_buf.txt
@echo off
setlocal
   for /f "usebackq" %%i in (`find /C "echo 1"^<"%~dp0echo_buf.txt"`) do set check=%%i
    del %~dp0echo_buf.txt
    if "%check%"=="1" ( 
        @echo on
    ) else ( 
        @echo off
    )
endlocal&set %~1=%check%
@goto :eof

:call_echo
echo 1

你可以用第四行替换(for)
for /f "usebackq" %%i in ("%~dp0echo_buf.txt") do ( 
    if "%%i"=="1" ( 
        set check=0 
    ) else ( 
        set check=1 
    )
    goto end_for
)
:end_for

test.bat

@echo off
@call %~dp0_echoStatus.bat status
@echo %status%
@echo on
@call %~dp0_echoStatus.bat status
@echo %status%

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