i have a problem. Here my code:
set listPath=1 2
for %%n in (%listPath%) do (
setlocal enabledelayedexpansion
IF "!ver!"=="" (
:loop
echo Insert Version:
set /p "ver="
IF "!ver!"=="" (
echo Invalid!
Timeout /T 1 /NoBreak>nul
goto loop
) ELSE (
goto Next
)
:Next
)
echo ver: !ver!
pause > nul
)
Because it does not work? Each time I execute the first loop and then exit the foreach ending the execution. If, on the other hand, I omit the set / p everything works normally and cycles the set well. Why? How can I go about putting that user input into the loop without breaking the loop? Thanks
I think you are overcomplicating things for yourself, let me show you why I say that:
the choice command will force you to only select one of the options. It does not allow any other choices, other than what is in the list:
#echo off
choice /c 12 /m "Select version"
echo Ver: %errorlevel%
You already have the %errorlevel% variable which you can use to reflect as version, but you can assign that to your ver variable, if you really wanted as well:
#echo off
choice /c 12 /m "Select version"
set "ver=%errorlevel%"
echo Ver: %ver%
Other than making it that simple, your current code has numerous issues.
You do not have your if and else statements correctly formatted. run from cmd if /? to see the correct syntax.
You cannot goto inside of a for loop parenthesized block
Your label position is invalid. the first command it sees after label is ) which is an invalid command.
The for loop is not even needed and makes no sense to be honest.
If you however wanted to still use your overcomplicated version, then this will be a better version:
#echo off
set "listPath=1 2"
set ver=
:loop
set /p "ver=Insert Version: "
if not %ver% equ 1 if not %ver% equ 2 (
echo Invalid!
Timeout /T 1 /NoBreak>nul
goto :loop
)
echo ver: %ver%
pause>nul
Related
I want to check if count exists in mycountry or not, and then do some operations according.
My code snippet :
rem #ECHO OFF
cls
SET FILE="mycountry"
If true I want to run 3 statements and if false I want to run 3 other statements.
I have tried this combination:
Echo.%FILE% | findstr /C:"count">nul && (Echo.TRUE) || (Echo.FALSE)
But how to write multiple statements if the condition gets true? I don't wanna use any flag variable.
Below snippet is not working.
Echo.%FILE% | findstr /C:"count">nul &&
(
Echo.TRUE
echo "ran correct."
)
|| (Echo.FALSE)
You can use the %errorlevel% value combined with an if/else.
See the example below:
REM #echo off
cls
SET FILE="mycountry"
SET STR="TEST"
findstr %STR% %FILE% >nul
if %errorlevel% equ 1 (
goto searchError
) else (
goto searchSucces
)
:searchSucces
echo String %STR% found in file %FILE%
pause
exit
:searchError
echo String %STR% not found in file %FILE%
pause
exit
Your code, (integrated into a batch file), appears to work as expected:
#Echo Off
Set "FILE="
Set /P "FILE=Enter String: "
If Not Defined FILE Exit /B
Echo.%FILE% | findstr /C:"count">nul && (Echo.TRUE) || (Echo.FALSE)
Pause
In addition, the following two methods both appear to work as expected:
Using Echo and FindStr (as in your code):
#Echo Off
Set "FILE="
Set /P "FILE=Enter String: "
Echo=%FILE%|FindStr /IC:"count">Nul 2>&1&&(Echo TRUE
Echo Ran correct.
Timeout 3 /NoBreak>Nul
Echo Still running!)||Echo FALSE
Pause
Using variable substitution:
#Echo Off
Set "FILE="
Set /P "FILE=Enter String: "
If /I "%FILE:count=%"=="%FILE%" (Echo FALSE) Else (Echo TRUE
Echo Ran correct.
Timeout 3 /NoBreak>Nul
Echo Still running!)
Pause
If the examples above do not work for you, you should edit your question to include the actual code and strings you're using in your real world scenario. We cannot fix something we cannot see, especially if you don't fully explain the issue, (snippet is not working is a statement only, not an explanation).
Been wrecking my brain all night trying to figure out why this isn't working, but one of my variables isn't releasing on the next iteration of my loop and I can't figure out why... The first pass of the loop seems to work fine, but the next iteration, the first variable gets locked and the script connects to the system that's already been configured.
I've been staring at this for a while now and no matter how I approach it, it still behaves badly. :/ The purpose is to read a text-string of a given file, and use it to modify (via Find and Replace (fnr.exe)) another file with several instances of the required data. I didn't have alot of luck with 'findstr' replacing so many instances of the text required so I went with a tool I've used before that seemed to work really well in it's previous scripting application...
Truth be told, I find myself stumbling with even the most basic code a lot of times, so any kind soul willing to impart some wisdom/assistance would be greatly appreciated!
Thanks in advance...
#ECHO ON
setlocal enabledelayedexpansion
> "%~dp0report.log" ECHO Batch Script executed on %DATE% at %TIME%
rem read computer list line by line and do
FOR /F %%A in (%~dp0workstations.txt) do (
SET lwn=
SET WKSTN=%%A
rem connect to workstation and read lwn.txt file
pushd "\\%WKSTN%\c$\"
IF ERRORLEVEL 0 (
FOR /F %%I in (\\%wkstn%\c$\support\lwn.txt) DO (
SET LWN=%%I
%~dp0fnr.exe --cl --dir "\\%WKSTN%\c$\support\folder\config" --fileMask "file.xml" --find "21XXXX" --replace "%%I"
IF ERRORLEVEL 0 ECHO Station %LWN%,Workstation %WKSTN%,Completed Successfully >> %~dp0report.log
IF ERRORLEVEL 1 ECHO Station %LWN%,Workstation %WKSTN%, A READ/WRITE ERROR OCCURRED >> %~dp0report.log
echo logwrite error 1 complete
popd
)
)
IF ERRORLEVEL 1 (
ECHO ,,SYSTEM IS OFFLINE >> %~dp0report.log
)
popd
set wkstn=
set lwn=
echo pop d complete
)
msg %username% Script run complete...
eof
The ! notation must be used on all variables that are changed inside the loop.
C:>type looptest.bat
#ECHO OFF
setlocal enabledelayedexpansion
rem read computer list line by line and do
FOR /F %%A in (%~dp0workstations.txt) do (
SET WKSTN=%%A
ECHO WKSTN is set to %WKSTN%
ECHO WKSTN is set to !WKSTN!
pushd "\\!WKSTN!\c$\"
ECHO After PUSHD, ERRORLEVEL is set to %ERRORLEVEL%
ECHO After PUSHD, ERRORLEVEL is set to !ERRORLEVEL!
IF !ERRORLEVEL! NEQ 0 (
ECHO ,,SYSTEM IS OFFLINE
) ELSE (
ECHO Host !WKSTN! is available
)
popd
)
EXIT /B 0
The workstations.txt file contained the following. (I should not give out actual host names.)
LIVEHOST1
DEADHOST1
LIVEHOST2
The output is...
C:>call looptest.bat
WKSTN is set to
WKSTN is set to LIVEHOST1
After PUSHD, ERRORLEVEL is set to 0
After PUSHD, ERRORLEVEL is set to 0
Host LIVEHOST1 is available
WKSTN is set to
WKSTN is set to DEADHOST1
The network path was not found.
After PUSHD, ERRORLEVEL is set to 0
After PUSHD, ERRORLEVEL is set to 1
,,SYSTEM IS OFFLINE
WKSTN is set to
WKSTN is set to LIVEHOST2
After PUSHD, ERRORLEVEL is set to 0
After PUSHD, ERRORLEVEL is set to 0
Host LIVEHOST2 is available
Although your code have several issues, the main one is the use of % instead of ! when you access the value of variables modified inside a for loop (although you already have the "enabledelayedexpansion" part in setlocal command). However, I noted that you sometimes use the FOR replaceable parameter (like in --replace "%%I") and sometimes you use the variable with the same value (%LWN%), so a simpler solution in your case would be to replace every %VAR% with its corresponding %%A for parameter.
I inserted this modification in your code besides a couple small changes that make the code simpler and clearer.
#ECHO ON
setlocal
> "%~dp0report.log" ECHO Batch Script executed on %DATE% at %TIME%
rem Read computer list line by line and do
FOR /F %%A in (%~dp0workstations.txt) do (
rem Connect to workstation and read lwn.txt file
pushd "\\%%A\c$\"
IF NOT ERRORLEVEL 1 (
FOR /F "usebackq" %%I in ("\\%%A\c$\support\lwn.txt") DO (
%~dp0fnr.exe --cl --dir "\\%%A\c$\support\folder\config" --fileMask "file.xml" --find "21XXXX" --replace "%%I"
IF NOT ERRORLEVEL 1 (
ECHO Station %%I,Workstation %%A,Completed Successfully >> %~dp0report.log
) ELSE (
ECHO Station %%I,Workstation %%A, A READ/WRITE ERROR OCCURRED >> %~dp0report.log
echo logwrite error 1 complete
)
)
) ELSE (
ECHO ,,SYSTEM IS OFFLINE >> %~dp0report.log
)
popd
echo pop d complete
)
msg %username% Script run complete...
I'm trying to write manual document validation part of my program. It's basically opening all the pdf documents one by one in the same folder. When its open i would like to echo few possibilities for user. Here starts the problem. I have around 180 possible choices. I was thinking to ask for the first letter of choice. Then it will echo all choices with started with X letter and user has to simply enter the number of this choice. So for example we have :
1. Asomething
2. Asomename
3. Asomenametoo
4. Bname
5. Bname 2
6. Bname 3
I want user to choose first letter and print possible choices. When the choice is made program should add some string to txt file with the same name in the same folder. Here i have a problem with IF statement inside FOR loop. I wanted to use goto but i can't do it inside FOR loop.
I can set up all the strings for each number before. For example : When you choose 1 it will add SomeString to txt. It's important to use choice option to avoid any typo's. Does anybody knows any other way to do this inside FOR loop ?
CODE:
setlocal enabledelayedexpansion
FOR %%b IN (c:\test\*.txt) DO (
IF "%ERRORLEVEL%"=="0" ECHO Document will open now...
start Acrobat.exe %%b.pdf
ECHO 1. Sample 1
ECHO 2. Sample 2
set /p choice= Please enter number:
call :OPTION
ECHO !choice! >> %%b
PAUSE
taskkill /IM Acrobat.exe >> c:\test\log\temp.txt
)
PAUSE
GOTO MENU
:OPTION
IF !choice!==1 SET /A !choice!==MNV666
IF !choice!==2 SET /A !choice!==MNV777
GOTO:EOF
I'm having some trouble understanding the problem you're having, but it looks like all of the statements following the IF should all be conditions of the IF, not just the ECHO statement. For that, you can put the entire block in parentheses like this:
setlocal enabledelayedexpansion
FOR %%b IN (c:\test\*.txt) DO (
IF "%ERRORLEVEL%"=="0" (
ECHO Document will open now...
start Acrobat.exe %%b.pdf
ECHO 1. Sample 1
ECHO 2. Sample 2
set /p choice= Please enter number:
call :OPTION
ECHO !choice! >> %%b
PAUSE
taskkill /IM Acrobat.exe >> c:\test\log\temp.txt
) else (
goto :EOF REM Just an example of else
)
)
PAUSE
GOTO MENU
:OPTION
IF !choice!==1 SET /A !choice!==MNV666
IF !choice!==2 SET /A !choice!==MNV777
GOTO:EOF
Were you having some problem using goto in the FOR loop?
Hi I want to make a batch file menu, that asks 'Select app you want to install?' for example
App1
App2
App3
App4
App5
ALL Apps
Select what app:_
What I want is, for example I want to install App2, App3, and App5, so I can type on by App ID's 'Select what app:2,3,5' . And when user Select option 6, it will install all Applications!
I know this is possible on bash scripting, but Im not sure on batch scripting?
An example of batch menu is http://mintywhite.com/software-reviews/productivity-software/create-multiple-choice-menu-batchfile/
Answer
This will do what you want. Let me know if you have any questions. All you have to do is follow the two steps listed in the script.
Script
:: Hide Command and Set Scope
#echo off
setlocal EnableExtensions
:: Customize Window
title My Menu
:: Menu Options
:: Specify as many as you want, but they must be sequential from 1 with no gaps
:: Step 1. List the Application Names
set "App[1]=One"
set "App[2]=Two"
set "App[3]=Three"
set "App[4]=Four"
set "App[5]=Five"
set "App[6]=All Apps"
:: Display the Menu
set "Message="
:Menu
cls
echo.%Message%
echo.
echo. Menu Title
echo.
set "x=0"
:MenuLoop
set /a "x+=1"
if defined App[%x%] (
call echo %x%. %%App[%x%]%%
goto MenuLoop
)
echo.
:: Prompt User for Choice
:Prompt
set "Input="
set /p "Input=Select what app:"
:: Validate Input [Remove Special Characters]
if not defined Input goto Prompt
set "Input=%Input:"=%"
set "Input=%Input:^=%"
set "Input=%Input:<=%"
set "Input=%Input:>=%"
set "Input=%Input:&=%"
set "Input=%Input:|=%"
set "Input=%Input:(=%"
set "Input=%Input:)=%"
:: Equals are not allowed in variable names
set "Input=%Input:^==%"
call :Validate %Input%
:: Process Input
call :Process %Input%
goto End
:Validate
set "Next=%2"
if not defined App[%1] (
set "Message=Invalid Input: %1"
goto Menu
)
if defined Next shift & goto Validate
goto :eof
:Process
set "Next=%2"
call set "App=%%App[%1]%%"
:: Run Installations
:: Specify all of the installations for each app.
:: Step 2. Match on the application names and perform the installation for each
if "%App%" EQU "One" echo Run Install for App One here
if "%App%" EQU "Two" echo Run Install for App Two here
if "%App%" EQU "Three" echo Run Install for App Three here
if "%App%" EQU "Four" echo Run Install for App Four here
if "%App%" EQU "Five" echo Run Install for App Five here
if "%App%" EQU "All Apps" (
echo Run Install for All Apps here
)
:: Prevent the command from being processed twice if listed twice.
set "App[%1]="
if defined Next shift & goto Process
goto :eof
:End
endlocal
pause >nul
you may use choice.exe see here : http://ss64.com/nt/choice.html
You want to use set /p Example below:
echo What would you like to install?
echo 1 - App1
echo 2 - App2
set /p whatapp=
if %whatapp%==1 (
codetoinstallapp1
) else if %whatapp%==2 (
codetoinstallapp2
) else (
echo invalid choice
)
Here's a trick I learned:
echo.1) first choice
echo.2) second choice
echo.3) third choice
echo.4) fourth choice
:: the choice command
set pass=
choice /c 1234 /n /m "Choose a task"
set pass=%errorlevel%
::the choices
if errorlevel 1 set goto=1
if errorlevel 2 set goto=2
if errorlevel 3 set goto=3
if errorlevel 4 set goto=4
goto %goto%
While I use only 1-4 it would be very easy to add more possible choices.
#echo off
:menu
cls
echo.
echo Select the case color you want to create:
echo ==========================================
echo.
echo App 1
echo App 2
echo App 3
echo App 4
echo.
echo ==========================================
echo Please answer Y/N to the following:
set /p App1= Install App 1?
set /p App2= Install App 2?
set /p App3= Install App 3?
set /p App4= Install App 4?
if /I "%App1%" EQU "Y" goto :Option-1
if /I "%App1%" EQU "N" goto :1
:1
if /I "%App2%" EQU "Y" goto :Option-2
if /I "%App2%" EQU "N" goto :2
:2
if /I "%App3%" EQU "Y" goto :Option-3
if /I "%App3%" EQU "N" goto :3
:3
if /I "%App4%" EQU "Y" goto :Option-4
if /I "%App4%" EQU "N" goto :End
:Option-1
App 1 Loc.
goto 1
:Option-2
App 2 Loc.
goto 2
:Option-3
App 3 Loc.
goto 2
:Option-4
App 4 Loc.
:End
Exit
Menu with analog of checkbox.
#echo off
set size=3
::preset
set chbox2=x
:prepare
for /L %%i in (0,1,%size%) do (
if defined chbox%%i (
set st%%i=Y
) else (
set chbox%%i=
)
)
:menu
cls
echo.
echo 1. [%chbox1%] name_1:
echo.
echo 2. [%chbox2%] name_2:
echo.
echo 3. [%chbox3%] name_3:
echo.
echo.
echo.
choice /C 1234567890qa /N /M "Select [1-9] >> [a]pply or [q]uit:"
echo.
set inp=%errorlevel%
if %inp%==11 (
exit
)
if %inp%==12 (
call :apply
)
::switch
if defined st%inp% (
set st%inp%=
set chbox%inp%=
) else (
set st%inp%=Y
set chbox%inp%=X
)
goto :menu
:apply
for /L %%i in (0,1,%size%) do (
if defined st%%i (
call :st%%i
echo.
)
)
echo.
pause
goto :menu
:st1
echo First Command
goto :eof
:st2
echo Second Command
goto :eof
:st3
echo Third Command
goto :eof
You can set lines checked as defaults under :preset label.
A batch file is a list of command prompt commands. The following code prints to the terminal:
echo whateveryouwant
print your menu using these echo statements in a batch file.
Getting user input can be found here: How to read input from console in a batch file?
The installing of applications is a little more complex - you need to know the requirements of your apps and where files should be moved - that should be simple, as well; use move on the appropriate files in the appropriate place.
Here's an example of a batch script menu I'm using:
#echo off
setlocal
:begin
cls
echo [LOCAL ACCOUNTS REMOTE ADMIN] --------------------------------------
echo 1 -- List local accounts on a remote machine
echo 2 -- Create a local account on a remote machine
echo 3 -- Change a local account password on a remote machine
echo 4 -- Delete a local account on a remote machine
echo;
echo 5 -- exit
echo;
set /P rmFunc="Enter a choice: "
echo --------------------------------------------------------------------
for %%I in (1 2 3 4 5 x) do if #%rmFunc%==#%%I goto run%%I
goto begin
:run1
rem list local accounts code
goto begin
:run2
rem create local account code
goto begin
rem and so on, until...
:run5
:run9
:run99
:runx
endlocal
goto :EOF
The most relevant bits are the set /p line and the for...in lines. The for...in line basically compares the choice entered with every menu item number, and if match, goto run#; otherwise start over from the beginning.
I saw that none of the above answers completely answered his/her question. One feature that they have left out is selecting all the software it installs in one go (so to speak).
So I made this off the top of my head (extremely sorry if there is something wrong with it, I'll edit it if there is).
#echo off & setlocal enabledelayedexpansion
echo What would you like to install?
::Put your options here, preferably numbered.
set /p op=Type the numbers of the software you want to install (separated by commas with no spaces. E.g: 1,3,2):
for /f "delims=, tokens=1-5" %%i in ("op") do (
set i=%%i
set j=%%j
set k=%%k
set l=%%l
set m=%%m
)
if %i%X neq X set last=1b & goto %i%
:1b
if %j%X neq X set last=2b & goto %j%
:2b
if %k%X neq X set last=3b & goto %k%
:3b
if %l%X neq X set last=4b & goto %l%
:4b
if %m%X neq X set last=%m% & goto %m%
goto next
:1
::Put the code for doing the first option here
goto %last%
:2
::Put the code for doing the second option here
goto %last%
:3
::Put the code for doing the third option here
goto %last%
:4
::Put the code for doing the fourth option here
goto %last%
:5
::Put the code for doing the fifth option here
goto %last%
:next
::Put some more stuff here...
So that was a bit excessive. Feel free to change some things around and such.
What the code is doing is getting the user input (such as if you put in "1,3,4"), putting each number into its own variable, checking if that variable is empty, and if it isn't, sending you to code that does whatever the option was. It does this a few times until all the variables have been assessed.
This is a proposed analysis for improvement on David Ruhmann's code relating to the "Validate Input" section above:
Testing the menu for special characters works a charm except for the following characters "^&<". When each are submitted for input the program closes.
set "Input=%Input:"=%"
set "Input=%Input:^^=%"
set "Input=%Input:>=%"
set "Input=%Input:<=%"
set "Input=%Input:^&=%"
set "Input=%Input:|=%"
set "Input=%Input:(=%"
set "Input=%Input:)=%"
:: Equals are not allowed in variable names
set "Input=%Input:^==%"
Escaping the ^ and & works, but something very peculiar going on with the parsing of "<" and ">" (escaping these doesn't appear to work). If we reverse the order of the two statements as in the above amendment we find "<" works, but now ">" doesn't.
However, shifting the second statement with "<" down thus, both redirection characters work but now ")" doesn't!!
set "Input=%Input:"=%"
set "Input=%Input:^^=%"
set "Input=%Input:>=%"
set "Input=%Input:^&=%"
set "Input=%Input:|=%"
set "Input=%Input:(=%"
set "Input=%Input:)=%"
set "Input=%Input:<=%"
:: Equals are not allowed in variable names
set "Input=%Input:^==%"
Another great tutorial for batch menus is found here.
I'm using this
#echo off
:a
echo Welcome to a casual log-in (you are a idiot)
echo.
pause
echo ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
set /p c=Email:
echo ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
set /p u=Password:
echo ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
msg * Welcome %c%.
goto a
There is actually an extremely easy way to do this.
#echo off
echo Which app do you want to install?
echo [APP 1]
echo [APP 2]
echo [APP 3]
echo.
echo Type 1, 2, or 3.
set /p "AppInstaller=>"
if %AppInstaller%==1 goto 1
if %AppInstaller%==2 goto 2
if %AppInstaller%==3 goto 3
:1
[INSTALL CODE]
:2
[INSTALL CODE]
:3
[INSTALL CODE]
The menu, when coded like this, will look like this:
Which app do you want to install?
[APP 1]
[APP 2]
[APP 3]
Type 1, 2, or 3.
>_
The code sets the variable AppInstaller to 1, 2, or 3. The file determines this and redirects you to an installer for each one.
This is fairly easy code that I use a lot in multiple choice games:
#echo off
color 0a
cls
:download
echo App 1
echo App 2
echo App 3
echo App 4
echo App 5
echo All Apps
echo
echo Select What App (1, 2, 3, ect.):
set /p apps=
if %apps%==1 goto 1
if %apps%==1 goto 2
if %apps%==1 goto 3
if %apps%==1 goto 4
if %apps%==1 goto 5
if %apps%==1 goto all
:1
(Your Code Here)
:2
(Your Code Here)
:3
(Your Code Here)
:4
(Your Code Here)
:5
(Your Code Here)
:all
(Your Code Here)
In a DOS Batch File subroutine, how can I turn off echo within the subroutine, but before returning, put it back to what it was before (either on or off)?
For example, if there was a command called echo restore, I would use it like this:
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
My first attempt was an utter failure - thanks jeb for pointing out the errors. For those that are interested, the original answer is available in the edit history.
Aacini has a good solution if you don't mind putting your subroutine in a separate file.
Here is a solution that works without the need of a 2nd batch file. And it actually works this time! :)
(Edit 2 - optimized code as per jeb's suggestion in comment)
: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
)
If you are willing to put up with the slight risk of two processes simultaneously trying to access the same file, the :getEchoState routine can be simplified without the need of SETLOCAL or a temp variable.
: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
)
The simplest way is to not turn echo off in the first place.
Instead, do what you currently do with the echo off line to the rest of your subroutine - prefix all commands in the subroutine with an # sign. This has the effect of turning off echo for that command, but keeps the echo state for future commands.
If you use commands that execute other commands, like IF or DO, you will also need to prefix the "subcommand" with an # to keep them from being printed when echo is otherwise on.
The easiest way is to extract the subroutine to another .bat file and call it via CMD /C instead of CALL this way:
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
This way the echo status will be automatically restored to the value it had when the CMD /C was executed; the only drawback of this method is a slightly slower execution...
Here is a straight forward solution that relies on a single temporary file (using %random% to avoid race conditions). It works and is at least localization resistant, i.e., it works for the two known cases stated by #JoelFan and #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
Add this preliminary code to increase the solution's robustness (although the odd's are high that it's not necessary):
#:: 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 )
I wasn't really happy with the solution above specially because of the language issue and I found a very simple one just by comparing the result from current echo setting with the result when explicitly set OFF. This is how it works:
:: 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
I was looking for the same solution to the same problem, and after reading your comments I had an idea (which is not the answer to the question, but for my problem is even better).
I wasn't satisfied with the cmd.exe /c mysub.cmd because it makes hard or even impossible to return variables (I didn't check) - (couldn't comment because it's the first time I post here :)
Instead noticed that all we want -in the end- is to suppress 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
It works fine with labelled subroutines, with subroutines contained in .cmd files, and I suppose it would work fine even with the cmd.exe /c variant (or start).
It also has the plus that we can keep or discard the stderr, replacing >nul with >nul 2>&1
I note that ss64.com scares kids like me stating that with call "Redirection with & | <> also does not work as expected".
This simple test works as expected. He must have been thinking of more complex situations.