%errorlevel% returning 0 in loop command - windows

Ok, I've installed Dropbox but it didn't corresponded to what I was looking for so I uninstalled it with Revo Pro.
But, when i open the taskmanager there are still processes related to it running in my computer so I decided to make a batch to look out and delete all files that are related to it.
#echo off
cd c:\
:a
set /p a=Phrase that might be realted to it
for /r %%d IN (*.*) DO (
(
findstr /i /m /c:%a% "%%d"
if "%errorlevel%"=="0" del "%%d"
echo %errorlevel%
)
)
pause
The problem is: when I run findstr using loop even when there is no match for my variable "%a%" in an analized file %errorlevel% returns as 0. But when I use findstr alone and there isn't a match %ERRORLEVEL% returns as 1 and 0 for a match.
If I use it, I'll delete all my PC files haha. What's wrong with the code?

Within a parenthesised series of statements, any %var% is replaced by the value of that variable at the time the verb controlling that statement-sequence (or block) is encountered.
Here, the block is the entire sequence of statements controlled by the for. %errorlevel% is replaced by the status of errorlevel at the time the for is encountered, so probably 0.
If you use
findstr /i /m /c:%a% "%%d"
if not errorlevel 1 del "%%d"
echo %errorlevel%
then the run-time value of errorlevel is used (ie. as it changes through the operation of the loop) and the command means "if errorlevel is not (1 or greater than 1) do this..."
The findstr will set errorlevel to 0 on found, 1 on not found and 2 for file not found(IIRC) so NOT (1 or greater than 1) selects 0 only. Note that in certain esoteric circumstances, errorlevel may become negative, but after a findstr I believe 0..2 is the allowed range.

Not sure what's wrong with the code, but you can probably skip it using the && operand.
findstr /i /m /c:%a% "%%d" && del "%%d" echo %errorlevel%
Thanks to Stephan for correcting the example.

Whenever Windows command interpreter encounters ( being interpreted as begin of a command block, it parses the entire command block up to matching ) marking end of the command block and replaces all %variable% by current value of the variable.
This means in this case that before command FOR is the first time executed, everything from ( after DO up to last ) is processed already with replacing all %variable% references by current value of the appropriate variable. Then the already preprocessed block is executed one (on command IF) or more times (on command FOR).
This behavior can be seen by debugging the batch file. For debugging a batch file first #echo off must be removed or commented out with command REM or changed to #echo on. Then a command prompt window must be opened and the batch file is executed from within this command prompt window by typing its name with full path enclosed in double quotes if path or name contains a space character. The Windows command interpreter shows now all command lines and command blocks after preprocessing before executing and of course the standard messages and the error messages output by the commands or by Windows command interpreter itself in case of a syntax error in batch file.
Opening a command prompt window means running cmd.exe with option /K to Keep window open after execution of a command or a batch script. Double clicking on a batch file starts also cmd.exe for processing the batch file, but with parameter /C to Close the window automatically after batch processing terminated independent on cause - successful finished or an error occurred.
The command prompt window opened before running the batch file remains open after batch processing finished successfully or with an error except the batch file contains command EXIT without parameter /B. So experts in batch code writing test batch files always by running them from within a command prompt window instead of double clicking on them.
Delayed variable expansion is needed for variables set or modified and referenced within same command block as explained by help of command SET output on running in a command prompt window set /?.
#echo off
setlocal EnableDelayedExpansion
cd /D C:\
:a
set /P "a=Phrase that might be realted to it: "
for /r %%d in (*) do (
%SystemRoot%\System32\findstr.exe /i /m /c:"%a%" "%%d"
if "!errorlevel!" == "0" del "%%d" >nul
)
endlocal
But for checking the exit code of a previous command there is also if errorlevel syntax as explained by Microsoft in support article Testing for a Specific Error Level in Batch Files.
#echo off
setlocal EnableDelayedExpansion
cd /D C:\
:a
set /P "a=Phrase that might be realted to it: "
for /r %%d in (*) do (
%SystemRoot%\System32\findstr.exe /i /m /c:"%a%" "%%d" >nul
if not errorlevel 1 del "%%d" >nul
)
endlocal
if errorlevel X tests if exit code of previous command or application when it modifies the errorlevel variable at all is greater or equal X. By using if not errorlevel X the check is if last exit code is lower than X which is here a test if exit code is 0.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
cd /?
del /?
echo /?
for /?
if /?
set /?
And see also
Microsoft's command-line reference
SS64.com - A-Z index of the Windows CMD command line
Microsoft article about Using command redirection operators
Answer on question Single line with multiple commands using Windows batch file
How to set environment variables with spaces?

Related

Batch: How to set variable in a nested for-loop and re-use it outside of it (enabledelayedexpansion does not work)

I have a batch inside a folder whose goal is to execute all the batch files located in its sub-folders and evaluate their errorlevel. If at least one of them is equal to 1, the main batch will return an exit code equal to 1. This does not mean that the main batch have to exit with the first errorlevel equal to 1: all the sub-batch files must be executed anyway.
EDIT: all the sub-batch files return an exit code equal to 1 if they fail or equal to 0 if they pass (they are all batch files for testing purpose I wrote myself).
Problem: the exit_code variable never changes outside the loops.
I found similar problems here on SO (and a very similar one: Counting in a FOR loop using Windows Batch script) but they didn't help (probably I didn't understand... I don't know, I'm new to batch scripting).
Thanks in advance.
CODE:
#echo off
setlocal enabledelayedexpansion
set exit_code=0
for /D %%s in (.\*) do (
echo ******************** %%~ns ********************
echo.
cd %%s
for %%f in (.\*.bat) do (
echo calling %%f
call %%f
if errorlevel 1 (
set exit_code=1
)
)
echo.
echo.
cd ..
)
echo !exit_code!
exit /B !exit_code!
Let us assume the current directory on starting the main batch file is C:\Temp\Test containing following folders and files:
Development & Test
Development & Test.bat
Hello World!
Hello World!.bat
VersionInfo
VersionInfo.bat
Main.bat
Batch file Development & Test.bat contains just the line:
#dir ..\Development & Test
Batch file Hello World!.bat contains just the line:
#echo Hello World!
Batch file VersionInfo.bat contains just the line:
#ver
Batch file main.bat contains following lines:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
cls
set "exit_code=0"
for /D %%I in ("%~dp0*") do (
echo ******************** %%~nxI ********************
echo/
for %%J in ("%%I\*.bat") do (
echo Calling %%J
echo/
pushd "%%I"
call "%%J"
if errorlevel 1 set "exit_code=1"
popd
)
echo/
echo/
)
echo Exit code is: %exit_code%
endlocal & exit /B %exit_code%
A command prompt is opened in which next the following command lines are executed manually one after the other:
C:\Temp\Test\Main.bat
echo Errorlevel is: %errorlevel%
ren "C:\Temp\Test\Development & Test\Development & Test.bat" "Development & Test.cmd"
C:\Temp\Test\Main.bat
echo Errorlevel is: %errorlevel%
The first execution of Main.bat results in exit with value 1 as it can be seen in command prompt window on the line:
Errorlevel is: 1
The reason is the wrong coded dir command with the directory name not enclosed in double quotes resulting in interpreting Test as command to execute. For that reason the dir command line results in following error output:
Volume in drive C is TEMP
Volume Serial Number is 14F0-265D
Directory of C:\Temp\Test
File Not Found
'Test' is not recognized as an internal or external command,
operable program or batch file.
The exit code of this batch file is not 0 due to the error and for that reason the condition if errorlevel 1 is true and set "exit_code=1" is executed already on first executed batch file.
The processing of the other two batch files always end with 0 as exit code.
The command ren is used to change the file extension of Development & Test.bat to have the batch file next with name Development & Test.cmd resulting in ignoring it by main.bat. The second execution of Main.bat results in exit with 0 as it can be seen on the line:
Errorlevel is: 0
Please read the following pages for the reasons on all the code changes:
Syntax error in one of two almost-identical batch scripts: ")" cannot be processed syntactically here
change directory command cd ..not working in batch file after npm install
DosTips forum topic: ECHO. FAILS to give text or blank line - Instead use ECHO/
Why is no string output with 'echo %var%' after using 'set var = text' on command line?
How does the Windows Command Interpreter (CMD.EXE) parse scripts?
What are the ERRORLEVEL values set by internal cmd.exe commands?
Which cmd.exe internal commands clear the ERRORLEVEL to 0 upon success?
Single line with multiple commands using Windows batch file
Summary of the changes:
Delayed expansion is not enabled in Main.bat as not required here to process also correct directory and file names containing an exclamation mark like C:\Temp\Test\Hello World!\Hello World!.bat.
I and J are used as loop variables instead of s and f because of the latter two letters could be misinterpreted as loop variable modifiers in some cases. Therefore it is better to avoid the letters which have a special meaning for command for on referencing the loop variables.
%~dp0 is used instead of .\ to make sure that the batch file searches for non-hidden subdirectories in the directory of the batch file independent on what is the current directory on starting the batch file. This expression references drive and path of argument 0 which is the full path of currently executed batch file Main.bat. The referenced path of the batch file always ends with a backslash and for that reason %~dp0 is concatenated with * without an additional backslash.
Directory and file name arguments are enclosed in double quotes to work also for names containing a space or one of these characters &()[]{}^=;!'+,`~. %%~nxI and %%J in the two echo command lines are not enclosed in double quotes as not necessary as long as delayed expansion is not enabled. The batch file makes sure that this is not the case for Main.bat.
The usage of "%~dp0*" instead of just .\* in first FOR loop results in getting assigned to loop variable I the directory names with full path never ending with a backslash. The usage of "%%I\*.bat" makes sure to get assigned to loop variable J the full qualified file name of a non-hidden batch file. It is in general better to use full qualified directory/file names wherever possible. This helps also quite often on errors.
The two cd commands are replaced by pushd and popd and moved inside the inner FOR loop. Then it does not matter if a called batch file works only with current directory being the directory of the called batch file or works independent on current directory like Main.bat. Further it does not longer matter if a called batch file changes the current directory as with popd the initial current directory on starting Main.bat is restored as current directory which could be the directory in which files are stored to be processed by the called batch files. The usage of pushd and popd makes this batch file also working on being stored on a network resource and Main.bat is started with its UNC path.
The most important modification is on last command line:
endlocal & exit /B %exit_code%
This command line is first parsed by Windows command processor cmd.exe with replacing %exit_code% by current value of environment variable exit_code defined inside the local environment setup with setlocal EnableExtensions DisableDelayedExpansion. So the command line becomes either
endlocal & exit /B 0
or
endlocal & exit /B 1
Then Windows command processor executes command endlocal to restore the previous environment defined outside Main.bat which results in exit_code no longer defined if not defined in initial execution environment. Then the command exit with option /B to exit just processing of batch file Main.bat is executed with returning 0 or 1 to the parent process which is cmd.exe which assigns the exit code to variable errorlevel.
Well, there is one issue left with the batch file code of Main.bat. A called batch file could modify the value of environment variable exit_code of Main.bat on using the same environment variable without definition of a local environment by using command setlocal. The solution would be to use additionally the commands setlocal before and endlocal after calling a batch file.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
cls
set "exit_code=0"
for /D %%I in ("%~dp0*") do (
echo ******************** %%~nxI ********************
echo/
for %%J in ("%%I\*.bat") do (
echo Calling %%J
echo/
pushd "%%I"
setlocal
call "%%J"
endlocal
if errorlevel 1 set "exit_code=1"
popd
)
echo/
echo/
)
echo Exit code is: %exit_code%
endlocal & exit /B %exit_code%
The command endlocal does not modify errorlevel.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
cls /?
echo /?
endlocal /?
for /?
if /?
popd /?
pushd /?
set /?
setlocal /?

How to process 2 FOR loops after each other in batch?

My problem is that two FOR loops are working separately, but don't want to work one after another.
The goal is:
The first loop creates XML files and only when the creation has already been done the second loop starts and counts the size of created XML files and writes it into .txt file.
#echo off
Setlocal EnableDelayedExpansion
for /f %%a in ('dir /b /s C:\Users\NekhayenkoO\test\') do (
echo Verarbeite %%~na
jhove -m PDF-hul -h xml -o C:\Users\NekhayenkoO\outputxml\%%~na.xml %%a
)
for /f %%i in ('dir /b /s C:\Users\NekhayenkoO\outputxml\') do (
echo %%~ni %%~zi >> C:\Users\NekhayenkoO\outputxml\size.txt
)
pause
This question can be answered easily when knowing what jhove is.
So I searched in world wide web for jhove, found very quickly the homepage JHOVE | JSTOR/Harvard Object Validation Environment and downloaded also jhove-1_11.zip from SourceForge project page of JHOVE.
All this was done by me to find out that jhove is a Java application which is executed on Linux and perhaps also on Mac using the shell script jhove and on Windows the batch file jhove.bat for making it easier to use by users.
So Windows command interpreter searches in current directory and next in all directories specified in environment variable PATH for a file matching the file name pattern jhove.* having a file extension listed in environment variable PATHEXT because jhove.bat is specified without file extension and without path in the batch file.
But the execution of a batch file from within a batch file without usage of command CALL results in script execution of current batch file being continued in the other executed batch file without ever returning back to the current batch file.
For that reason Windows command interpreter runs into jhove.bat on first file found in directory C:\Users\NekhayenkoO\test and never comes back.
This behavior can be easily watched by using two simple batch files stored for example in C:\Temp.
Test1.bat:
#echo off
cd /D "%~dp0"
for %%I in (*.bat) do Test2.bat "%%I"
echo %~n0: Leaving %~f0
Test2.bat:
#echo %~n0: Arguments are: %*
#echo %~n0: Leaving %~f0
On running from within a command prompt window C:\Temp\Test1.bat the output is:
Test2: Arguments are: "Test1.bat"
Test2: Leaving C:\Temp\Test2.bat
The processing of Test1.bat was continued on Test2.bat without coming back to Test1.bat.
Now Test1.bat is modified to by inserting command CALL after do.
Test1.bat:
#echo off
cd /D "%~dp0"
for %%I in (*.bat) do call Test2.bat "%%I"
echo Leaving %~f0
The output on running Test1.bat from within command prompt window is now:
Test2: Arguments are: "Test1.bat"
Test2: Leaving C:\Temp\Test2.bat
Test2: Arguments are: "Test2.bat"
Test2: Leaving C:\Temp\Test2.bat
Test1: Leaving C:\Temp\Test1.bat
Batch file Test1.bat calls now batch file Test2.bat and therefore the FOR loop is really executed on all *.bat files found in directory of the two batch files.
Therefore the solution is using command CALL as suggested already by Squashman:
#echo off
setlocal EnableDelayedExpansion
for /f %%a in ('dir /b /s "%USERPROFILE%\test\" 2^>nul') do (
echo Verarbeite %%~na
call jhove.bat -m PDF-hul -h xml -o "%USERPROFILE%\outputxml\%%~na.xml" "%%a"
)
for /f %%i in ('dir /b /s "%USERPROFILE%\outputxml\" 2^>nul') do (
echo %%~ni %%~zi>>"%USERPROFILE%\outputxml\size.txt"
)
pause
endlocal
A reference to environment variable USERPROFILE is used instead of C:\Users\NekhayenkoO.
All file names are enclosed in double quotes in case of any file found in the directory contains a space character or any other special character which requires enclosing in double quotes.
And last 2>nul is added which redirects the error message output to handle STDERR by command DIR on not finding any file to device NUL to suppress it. The redirection operator > must be escaped here with ^ to be interpreted on execution of command DIR and not as wrong placed redirection operator on parsing already the command FOR.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
cd /?
dir /?
echo /?
for /?
And read also the Microsoft article Using command redirection operators.
You need to use the START command with the /WAIT flag when you launch an external application.
I believe it would look something like this:
START /WAIT jhove -m PDF-hul -h xml -o C:\Users\NekhayenkoO\outputxml\%%~na.xml %%a
That should cause the batch file to pause and wait for the external application to finish before proceeding.

How can I make an "are you sure" prompt in a Windows batch file?

I have a batch file that automates copying a bunch of files from one place to the other and back for me. Only thing is as much as it helps me I keep accidentally selecting that command off my command buffer and mass overwriting uncommitted changes.
What code would I need for my .bat file to make it output "Are you sure?", and make me type Y before it ran the rest of the file?
If anything other than Y is typed, it should exit execution on that line.
When I call exit, it closes cmd.exe which is not what I want.
You want something like:
#echo off
setlocal
:PROMPT
SET /P AREYOUSURE=Are you sure (Y/[N])?
IF /I "%AREYOUSURE%" NEQ "Y" GOTO END
echo ... rest of file ...
:END
endlocal
try the CHOICE command, e.g.
CHOICE /C YNC /M "Press Y for Yes, N for No or C for Cancel."
There are two commands available for user prompts on Windows command line:
set with option /P available on all Windows NT versions with enabled command extensions and
choice.exe available by default on Windows Vista and later Windows versions for PC users and on Windows Server 2003 and later server versions of Windows.
set is an internal command of Windows command processor cmd.exe. The option /P to prompt a user for a string is available only with enabled command extensions which are enabled by default as otherwise nearly no batch file would work anymore nowadays.
choice.exe is a separate console application (external command) located in %SystemRoot%\System32. File choice.exe of Windows Server 2003 can be copied into directory %SystemRoot%\System32 on a Windows XP machine for usage on Windows XP like many other commands not available by default on Windows XP, but available by default on Windows Server 2003.
It is best practice to favor usage of CHOICE over usage of SET /P because of the following reasons:
CHOICE accepts only keys (respectively characters read from STDIN) specified after option /C (and Ctrl+C and Ctrl+Break) and outputs an error beep if the user presses a wrong key.
CHOICE does not require pressing any other key than one of the acceptable ones. CHOICE exits immediately once an acceptable key is pressed while SET /P requires that the user finishes input with RETURN or ENTER.
It is possible with CHOICE to define a default option and a timeout to automatically continue with default option after some seconds without waiting for the user.
The output is better on answering the prompt automatically from another batch file which calls the batch file with the prompt using something like echo Y | call PromptExample.bat on using CHOICE.
The evaluation of the user's choice is much easier with CHOICE because of CHOICE exits with a value according to pressed key (character) which is assigned to ERRORLEVEL which can be easily evaluated next.
The environment variable used on SET /P is not defined if the user hits just key RETURN or ENTER and it was not defined before prompting the user. The used environment variable on SET /P command line keeps its current value if defined before and user presses just RETURN or ENTER.
The user has the freedom to enter anything on being prompted with SET /P including a string which results later in an exit of batch file execution by cmd because of a syntax error, or in execution of commands not included at all in the batch file on not good coded batch file. It needs some efforts to get SET /P secure against by mistake or intentionally wrong user input.
Here is a prompt example using preferred CHOICE and alternatively SET /P on choice.exe not available on used computer running Windows.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
echo This is an example for prompting a user.
echo/
if exist "%SystemRoot%\System32\choice.exe" goto UseChoice
setlocal EnableExtensions EnableDelayedExpansion
:UseSetPrompt
set "UserChoice="
set /P "UserChoice=Are you sure [Y/N]? "
set "UserChoice=!UserChoice: =!"
if /I "!UserChoice!" == "N" endlocal & goto :EOF
if /I not "!UserChoice!" == "Y" goto UseSetPrompt
endlocal
goto Continue
:UseChoice
%SystemRoot%\System32\choice.exe /C YN /N /M "Are you sure [Y/N]?"
if not errorlevel 1 goto UseChoice
if errorlevel 2 goto :EOF
:Continue
echo So you are sure. Okay, let's go ...
rem More commands can be added here.
endlocal
Note: This batch file uses command extensions which are not available on Windows 95/98/ME using command.com instead of cmd.exe as command interpreter.
The command line set "UserChoice=!UserChoice: =!" is added to make it possible to call this batch file with echo Y | call PromptExample.bat on Windows NT4/2000/XP and do not require the usage of echo Y| call PromptExample.bat. It deletes all spaces from string read from STDIN before running the two string comparisons.
echo Y | call PromptExample.bat results in YSPACE getting assigned to environment variable UserChoice. That would result on processing the prompt twice because of "Y " is neither case-insensitive equal "N" nor "Y" without deleting first all spaces. So UserChoice with YSPACE as value would result in running the prompt a second time with option N as defined as default in the batch file on second prompt execution which next results in an unexpected exit of batch file processing. Yes, secure usage of SET /P is really tricky, isn't it?
choice.exe exits with 0 in case of the user presses Ctrl+C or Ctrl+Break and answers next the question output by cmd.exe to terminate the batch job with N for NO. For that reason the condition if not errorlevel 1 goto UserChoice is added to prompt the user once again for a definite answer on the prompt by batch file code with Y or N. Thanks to dialer for the information about this possible special use case.
The first line below the batch label :UseSetPrompt could be written also as:
set "UserChoice=N"
In this case the user choice input is predefined with N which means the user can hit just RETURN or ENTER (or Ctrl+C or Ctrl+Break and next N) to use the default choice.
The prompt text is output by command SET as written in the batch file. So the prompt text should end usually with a space character. The command CHOICE removes from prompt text all trailing normal spaces and horizontal tabs and then adds itself a space to the prompt text. Therefore the prompt text of command CHOICE can be written without or with a space at end. That does not make a difference on displayed prompt text on execution.
The order of user prompt evaluation could be also changed completely as suggested by dialer.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
echo This is an example for prompting a user.
echo/
if exist "%SystemRoot%\System32\choice.exe" goto UseChoice
setlocal EnableExtensions EnableDelayedExpansion
:UseSetPrompt
set "UserChoice="
set /P "UserChoice=Are you sure [Y/N]? "
set "UserChoice=!UserChoice: =!"
if /I not "!UserChoice!" == "Y" endlocal & goto :EOF
endlocal
goto Continue
:UseChoice
%SystemRoot%\System32\choice.exe /C YN /N /M "Are you sure [Y/N]?"
if not errorlevel 2 if errorlevel 1 goto Continue
goto :EOF
:Continue
echo So you are sure. Okay, let's go ...
endlocal
This code results in continuation of batch file processing below the batch label :Continue if the user pressed definitely key Y. In all other cases the code for N is executed resulting in an exit of batch file processing with this code independent on user pressed really that key, or entered something different intentionally or by mistake, or pressed Ctrl+C or Ctrl+Break and decided next on prompt output by cmd not terminating the batch job.
For even more details on usage of SET /P and CHOICE for prompting user for a choice from a list of options see answer on How to stop Windows command interpreter from quitting batch file execution on an incorrect user input?
Some more hints:
IF compares the two strings left and right of the comparison operator with including the double quotes. So case-insensitive compared is not the value of UserChoice with N and Y, but the value of UserChoice surrounded by " with "N" and "Y".
The IF comparison operators EQU and NEQ are designed primary for comparing two integers in range -2147483648 to 2147483647 and not for comparing two strings. EQU and NEQ work also for string comparisons, but result on comparing strings in double quotes after a useless attempt to convert left string to an integer. EQU and NEQ can be used only with enabled command extensions. The comparison operators for string comparisons are == and not ... == which work even with disabled command extensions as even command.com of MS-DOS and Windows 95/98/ME supported them. For more details on IF comparison operators see Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files.
The command goto :EOF requires enabled command extensions to really exit batch file processing. For more details see Where does GOTO :EOF return to?
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
choice /?
echo /?
endlocal /?
goto /?
if /?
set /?
setlocal /?
See also:
This answer for details about the commands SETLOCAL and ENDLOCAL.
Why is no string output with 'echo %var%' after using 'set var = text' on command line?
It explains the reason for using syntax set "variable=value" on assigning a string to an environment variable.
Single line with multiple commands using Windows batch file for details on if errorlevel X behavior and operator &.
Microsoft documentation for using command redirection operators explaining the redirection operator | and handle STDIN.
Wikipedia article about Windows Environment Variables for an explanation of SystemRoot.
DosTips forum topic ECHO. FAILS to give text or blank line - Instead use ECHO/
The choice command is not available everywhere. With newer Windows versions, the set command has the /p option you can get user input
SET /P variable=[promptString]
see set /? for more info
Here a bit easier:
#echo off
set /p var=Are You Sure?[Y/N]:
if %var%== Y goto ...
if not %var%== Y exit
or
#echo off
echo Are You Sure?[Y/N]
choice /c YN
if %errorlevel%==1 goto yes
if %errorlevel%==2 goto no
:yes
echo yes
goto :EOF
:no
echo no
Here's my go-to method for a yes/no answer.
It's case-insensitive also.
This just checks for the errors given by the input and sets the choice variable to whatever you require so it can be used below in the code.
#echo off
choice /M "[Opt 1] Do you want to continue [Yes/No]"
if errorlevel 255 (
echo Error
) else if errorlevel 2 (
set "YourChoice=will not"
) else if errorlevel 1 (
set "YourChoice=will"
) else if errorlevel 0 (
goto :EOF
)
echo %YourChoice%
pause
You can also use 'Choice' command
#echo off
echo Sure?
CHOICE /C YN
IF %ERRORLEVEL% EQU 1 goto CONTINUE
IF %ERRORLEVEL% EQU 2 goto END
:END
exit
:CONTINUE
echo hi
pause
If you want to the batch program to exit back to the prompt and not close the prompt (A.K.A cmd.exe) you can use "exit /b".
This may help.
set /p _sure="Are you sure?"
::The underscore is used to ensure that "sure" is not an enviroment
::varible
if /I NOT "_sure"=="y" (
::the /I makes it so you can
exit /b
) else (
::Any other modifications...
)
Or if you don't want to use as many lines...
Set /p _sure="Are you sure?"
if /I NOT "_sure"=="y" exit /b
::Any other modifications and commands.
Hope this helps...
Here is a simple example which I use in a backup (.bat / batch) script on Windows 10, which allows me to have different options when making backups.
...
:choice
set /P c=Do you want to rsync the archives to someHost[Y/N]?
if /I "%c%" EQU "Y" goto :syncthefiles
if /I "%c%" EQU "N" goto :doonotsyncthefiles
goto :choice
:syncthefiles
echo rsync files to somewhere ...
bash -c "rsync -vaz /mnt/d/Archive/Backup/ user#host:/home/user/Backup/blabla/"
echo done
:doonotsyncthefiles
echo Backup Complete!
...
You can have as many as you need of these blocks.
You can consider using a UI confirmation.
With yesnopopup.bat
#echo off
for /f "tokens=* delims=" %%# in ('yesnopopup.bat') do (
set "result=%%#"
)
if /i result==no (
echo user rejected the script
exit /b 1
)
echo continue
rem --- other commands --
the user will see the following and depending on the choice the script will continue:
with absolutely the same script you can use also iexpYNbutton.bat which will produce similar popup.
With buttons.bat you can try the following script:
#echo off
for /f "tokens=* delims=" %%# in ('buttons.bat "Yep!" "Nope!" ') do (
set "result=%%#"
)
if /i result==2 (
echo user rejected the script
exit /b 1
)
echo continue
rem --- other commands --
and the user will see:
I would do it in the following way to make sure the testing and variables are correct during looping etc..
:: rem at the top of the script
setlocal enabledelayedexpansion
:: choice example
CHOICE /C YNC /M "Continue? Press Y for Yes, N for No or C for Cancel."
If /I "[!errorlevel!]" NEQ "[1]" ( GOTO START_OVER )
There are so many answers, but none of them seems to be simple and straight forward. This is the code I am using:
choice /M "Do you want to continue?"
if %errorlevel% EQU 1 (
... run your code lines here
)
First, open the terminal.
Then, type
cd ~
touch .sure
chmod 700 .sure
Next, open .sure and paste this inside.
#!/bin/bash --init-file
PS1='> '
alias y='
$1
exit
'
alias n='Taskkill /IM %Terminal% /f'
echo ''
echo 'Are you sure? Answer y or n.'
echo ''
After that, close the file.
~/.sure ; ENTER COMMAND HERE
This will give you a prompt of are you sure before continuing the command.
Open terminal. Type the following
echo>sure.sh
chmod 700 sure.sh
Paste this inside sure.sh
#!\bin\bash
echo -n 'Are you sure? [Y/n] '
read yn
if [ "$yn" = "n" ]; then
exit 1
fi
exit 0
Close sure.sh and type this in terminal.
alias sure='~/sure&&'
Now, if you type sure before typing the command it will give you an are you sure prompt before continuing the command.
Hope this is helpful!

How to make windows batch file pause when double-clicked?

I've written a batch file to automate some tasks. I can run it from a command window and it runs and displays results. If I double click it from explorer though, it runs and terminates immediately so I can't see the results.
Is there a way I can make batch file window stay open until I dismiss it if I started it by double-clicking the icon?
I don't want to have to pass a /nopause parameter or something when I call a batch file from the command line. I'd like a solution where I can use the batch file without having to do anything too special?
Thanks.
NOTE I don't want it to pause when running from the command line!! I may call this batch file from another batch file to carry out a load of operations. In that case I can't be sitting there to keep hitting enter.
Ideally it would be best if I can put some code in the batch file so it can work out where it was started from, and then pause or not as appropriate.
Use:
cmd /K myBatch.bat
as your shortcut.
My problem was similar - I wanted a pause at the very end if called from Windows Explorer, but no pause if in a command window. I came up with this.
At the top of every batch file, put this:
if "%parent%"=="" set parent=%~0
if "%console_mode%"=="" (set console_mode=1& for %%x in (%cmdcmdline%) do if /i "%%~x"=="/c" set console_mode=0)
and then at end
if "%parent%"=="%~0" ( if "%console_mode%"=="0" pause )
It handles nested batch calls, where you only want to pause at the end of the original batch file, not in nested batch files. In a nested batch file, %parent% is already set to original caller so it won't equal the nested %~0. If you have bat1 which calls bat2, it leaves open the option of double clicking bat2 in Explorer - in that context bat2 will pause at end, whereas if bat1 calls bat2, then bat2 won't pause at the end (only bat1 will pause).
The & statement separator helps avoid visual clutter for something which is secondary to the main function. If you don't like the look of it, just put each statement on a new line.
This approach looks for /C as one of the params in the launch command (%cmdcmdline%). It assumes your batch files don't use the /C option. If you use /C yourself, then you need to instead check if %COMSPEC%appears within %cmdcmdline% (use FINDSTR). When Explorer launches a bat file, its %cmdcmdline% includes %COMSPEC% eg C:\Windows\System32\cmd.exe /C double_clicked_batch_file_name. In a command window, %cmdcmdline% just has cmd.exe in it (no path). I use CALL mybat rather than cmd.exe /C mybat, but you may have to work with existing batch files.
Here is a solution that should work well and take into consideration the possibility that a batch-file might call another batch-file ("Nested").
You could use Find, to look for "/c" which should not be present if the batch-file is run from a "Command Prompt":
echo %cmdcmdline% | find /i "/c"
But, you could do a more "robust" test by using Find to search for a longer string, or the batch-file name.
The "Find" command will not work properly if the search string has (") double-quotes within it. To work around that, you can use environment variable substitution to "adjust" the string so it plays nice with Find:
set newcmdcmdline=%cmdcmdline:"=-%
This will typically return:
if the batch-file is run from a "Command Prompt"
newcmdcmdline=-C:\Windows\system32\cmd.exe-
if the batch-file is run by clicking on the the batch-file
(or the batch-file shortcut) from "Windows Explorer"
newcmdcmdline=cmd /c --D:\Path\test.cmd- -
Then you can use "Find" to test like:
echo %newcmdcmdline% | find /i "cmd /c --"
or
echo %newcmdcmdline% | find /i "cmd /c --%~dpf0%-"
Next, you need to decide if you want a "Nested" batch-file to behave as if you executed it in the same way as the calling batch-file, or if you want nested batch-files to always behave as if they were executed from a "Command Prompt".
Consider that if you are running in a nested batch-file, then testing for this:
echo %newcmdcmdline% | find /i "cmd /c --%~dpf0%-"
will always fail (no match) because %newcmdcmdline% contains the name of the outermost batch-file, not the nested batch-file.
So the first solution will behave the same for the calling batch-file, and all nested batch-files. Also perfect if you don't call any batch-files:
In all batch-files (calling and nested) that you care to make this test, add these lines, typically near the top of the batch-files (you may exclude the echo-statements if you wish):
if not defined withincmd call :testshell
if %withincmd% EQU 0 echo This batch-file: %~dpf0 was executed directly (from Windows Explorer, ...).
if %withincmd% EQU 1 echo This batch-file: %~dpf0 was executed from within a Command Prompt
rem if %withincmd% EQU 0 pause
Then, somewhere within each batch-file, add the testshell sub-function:
goto :EOF
:testshell
rem "Nested" batch-files won't see this because withincmd is already defined
if not defined newcmdcmdline set newcmdcmdline=%cmdcmdline:"=-%
set withincmd=1
echo %newcmdcmdline% | find /i "cmd /c --%~dpf0%-"
if %errorlevel% EQU 0 set withincmd=0
goto :EOF
You only make the conditional call to "testshell" one time, at the top of the outermost batch-file.
In some situations, you may want to have only the "outermost" batch-file behave differently if it is executed from a "Command Prompt" versus if it is run by clicking on the the batch-file (or the batch-file shortcut) from "Windows Explorer". So, batch-files called from the "outermost" batch-file will always behave the same regardless of how they are run.
For this to work, you have a few choices.
1) Save the value of "withincmd" before you call another batch-file, and restore the previous value of "withincmd" after the called batch-file returns. This is a little involved for most cases.
2) Use a "globally-unique" variable name for "withincmd" in each batch-file.
3) Execute the "Find" command each time you need to know how the current batch-file was run.
4) Increment a variable on entry to a batch-file and decrement it on batch-file exit, then only test how batch-file was run if count-variable=1
Method 3 is the easiest, but has the downside that if the outermost batch-file is called from itself (as in recursion) or another batch-file, the test variable (withincmd) will not be properly set.
Here's how to do it using method 3:
In all batch-files (calling and nested) that you care to make this test, add these lines, typically near the top of the batch-files (you may exclude the echo-statements if you wish):
call :testshell
if %withincmd% EQU 0 echo This batch-file: %~dpf0 was executed directly (from Windows Explorer, ...).
if %withincmd% EQU 1 echo This batch-file: %~dpf0 was executed from (or Nested) within a Command Prompt
rem if %withincmd% EQU 0 pause
Then, somewhere within each batch-file, add the testshell sub-function:
goto :EOF
:testshell
if not defined newcmdcmdline set newcmdcmdline=%cmdcmdline:"=-%
set withincmd=1
echo %newcmdcmdline% | find /i "cmd /c --%~dpf0%-"
if %errorlevel% EQU 0 set withincmd=0
goto :EOF
In this case, you have to call "testshell" once, at the top of the EACH batch-file, then again after you have returned from calling another batch-file (or call "testshell" each time you need to know how the current batch-file was run).
Here's how to do it using method 4:
In all batch-files (calling and nested) that you care to make this test, add these lines, typically near the top of the batch-files (you may exclude the echo-statements if you wish):
if not defined nestinglevel set nestinglevel=0
set /A nestinglevel=nestinglevel+1
call :testshell
if %withincmd% EQU 0 echo This batch-file: %~dpf0 was executed directly (from Windows Explorer, ...).
if %withincmd% EQU 1 echo This batch-file: %~dpf0 was executed from (or Nested) within a Command Prompt
rem if %withincmd% EQU 0 pause
Then, somewhere within each batch-file, add the testshell sub-function:
goto :EOF
:testshell
if not defined newcmdcmdline set newcmdcmdline=%cmdcmdline:"=-%
set withincmd=1
if %nestinglevel% GEQ 2 goto :EOF
echo %newcmdcmdline% | find /i "cmd /c --%~dpf0%-"
if %errorlevel% EQU 0 set withincmd=0
goto :EOF
Also, remember to decrement the variable when you exit one batch-file to return to the calling batch-file:
set /A nestinglevel=nestinglevel-1
In this case, you have to call "testshell" once, at the top of the EACH batch-file, then again after you have returned from calling another batch-file (or call "testshell" each time you need to know how the current batch-file was run).
In all cases, test %withincmd% to determine how the current batch-file was run, like this:
if %withincmd% EQU 0 pause
if %withincmd% EQU 1 goto :EOF
at the end of file print
pause
it will wait for anykey input
Add this at the end of your batch:
echo %CMDCMDLINE% | findstr /C:"/c">nul && pause
This will pause if run from Windows Explorer and do nothing if run from command line.
Explanation:
CMDCMDLINE contains "/c" when run from Windows Explorer.
echo %CMDCMDLINE% | will pipe contents of the CMDCMDLINE into findstr
findstr /C:"/c" checks if CMDCMDLINE contains "/c"
">nul" will discard findstr console output
&& pause will run only if findstr finds something
you can just add params to the batch call and handle conditionally pause statement in your batch. So when started from command line or dblclick the batch can pause, when called from others batches with a /nopause param don't pause.
use "pause" in the batch file at the end, and it will wait for the user input
HTH
Would the pause command work?
Microsoft Documentation on pause

Is there a way in a batch script to keep the console open only if invoked from Windows Manager?

I have a DOS batch script that invokes a java application which interacts with the user through the console UI. For the sake of argument, let's call it runapp.bat and its contents be
java com.example.myApp
If the batch script is invoked in a console, everything works fine. However, if the script is invoked from the Window Manager, the newly opened console closes as soon as the application finishes executing. What I want is for the console to stay open in all cases.
I know of the following tricks:
add a pause command at the end of the script. This is a bit ugly in case runapp.bat is invoked from the command line.
create a new shell using cmd /K java com.example.myApp This is the best solution I found so far, but leaves an extra shell environment when invoked from the command line, so that calling exit doesn't actually close the shell.
Is there a better way?
See this question: Detecting how a batch file was executed
This script will not pause if run from the command console, but will if double-clicked in Explorer:
#echo off
setlocal enableextensions
set SCRIPT=%0
set DQUOTE="
:: Detect how script was launched
#echo %SCRIPT:~0,1% | findstr /l %DQUOTE% > NUL
if %ERRORLEVEL% EQU 0 set PAUSE_ON_CLOSE=1
:: Run your app
java com.example.myApp
:EXIT
if defined PAUSE_ON_CLOSE pause
I prefer using %cmdcmdline% as posted in the comment to Patrick's answer to the other question (which I didn't find although looked). That way, even if someone decides to use quotes to call the batch script, it won't trigger the false positive.
My final solution:
#echo off
java com.example.myApp %1 %2
REM "%SystemRoot%\system32.cmd.exe" when from console
REM cmd /c ""[d:\path\script.bat]" " when from windows explorer
#echo %cmdcmdline% | findstr /l "\"\"" >NUL
if %ERRORLEVEL% EQU 0 pause
cmd /K java com.example.myApp & pause & exit
will do the job. The & will execute the command one after another. If you use && you can break if one fails.
Include this line in a batch file and double click on the batch file in explorer:
cmd /k "script commands within these quotes seperated by &&"
For example
cmd /k "cd ../.. && dir && cd some_directory"
The full complement of options to cmd can be found here
I frequently use alternate shells (primarily TCC/LE from jpsoft.com) and subshells. I've found that this code works for a wider, more general case (and it doesn't require FINDSTR):
#echo off & setlocal
if "%CMDEXTVERSION%"=="" ( echo REQUIRES command extensions & exit /b 1 ) &:: REQUIRES command extensions for %cmdcmdline% and %~$PATH:1 syntax
call :_is_similar_command _FROM_CONSOLE "%COMSPEC%" %cmdcmdline%
if "%_PAUSE_NEEDED%"=="0" ( goto :_START )
if "%_PAUSE_NEEDED%"=="1" ( goto :_START )
set _PAUSE_NEEDED=0
if %_FROM_CONSOLE% equ 0 ( set _PAUSE_NEEDED=1 )
goto :_START
::
:_is_similar_command VARNAME FILENAME1 FILENAME2
:: NOTE: not _is_SAME_command; that would entail parsing PATHEXT and concatenating each EXT for any argument with a NULL extension
setlocal
set _RETVAL=0
:: more than 3 ARGS implies %cmdcmdline% has multiple parts (therefore, NOT direct console execution)
if NOT [%4]==[] ( goto :_is_similar_command_RETURN )
:: deal with NULL extensions (if both NULL, leave alone; otherwise, use the non-NULL extension for both)
set _EXT_2=%~x2
set _EXT_3=%~x3
if NOT "%_EXT_2%"=="%_EXT_3%" if "%_EXT_2%"=="" (
call :_is_similar_command _RETVAL "%~2%_EXT_3%" "%~3"
goto :_is_similar_command_RETURN
)
if NOT "%_EXT_2%"=="%_EXT_3%" if "%_EXT_3%"=="" (
call :_is_similar_command _RETVAL "%~2" "%~3%_EXT_2%"
goto :_is_similar_command_RETURN
)
::if /i "%~f2"=="%~f3" ( set _RETVAL=1 ) &:: FAILS for shells executed with non-fully qualified paths (eg, subshells called with 'cmd.exe' or 'tcc')
if /i "%~$PATH:2"=="%~$PATH:3" ( set _RETVAL=1 )
:_is_similar_command_RETURN
endlocal & set "%~1=%_RETVAL%"
goto :EOF
::
:_START
if %_FROM_CONSOLE% EQU 1 (
echo EXEC directly from command line
) else (
echo EXEC indirectly [from explorer, dopus, perl system call, cmd /c COMMAND, subshell with switches/ARGS, ...]
)
if %_PAUSE_NEEDED% EQU 1 ( pause )
Initially, I had used if /i "%~f2"=="%~f3" in the _is_similar_command subroutine. The change to if /i "%~$PATH:2"=="%~$PATH:3" and the additional code checking for NULL extensions allows the code to work for shells/subshells opened with non-fully qualified paths (eg, subshells called with just 'cmd.exe' or 'tcc').
For arguments without extensions, this code does not parse and use the extensions from %PATHEXT%. It essentially ignores the hierarchy of extensions that CMD.exe uses when searching for a command without extension (first attempting FOO.com, then FOO.exe, then FOO.bat, etc.). So, _is_similar_command checks for similarity, not equivalence, between the two arguments as shell commands. This could be a source of confusion/error, but will, in all likelyhood, never arise as a problem in practice for this application.
Edit: Initial code was an old version. The code is now updated to the most recent version which has: (1) a swapped %COMSPEC% and %cmdcmdline% in the initial call, (2) added a check for multiple %cmdcmdline% arguments, (3) echoed messages are more specific about what is detected, and (4) a new variable %_PAUSE_NEEDED% was added.
It should be noted that %_FROM_CONSOLE% is set based specifically on whether the batch file was excecuted directly from the console command line or indirectly through explorer or some other means. These "other means" can include a perl system() call or by executing a command such as cmd /c COMMAND.
The variable %_PAUSE_NEEDED% was added so that processes (such as perl) which execute the batch file indirectly can bypass pauses within the batch file. This would be important in cases in which output is not piped to the visible console (eg, perl -e "$o = qx{COMMAND}"). If a pause occurs in such a case, the "Press any key to continue . . ." pause prompt would never be displayed to the user and the process will hang waiting for unprompted user input. In instances where user interaction is either not possible or not allowed, the %_PAUSE_NEEDED% variable can be preset to "0" or "1" (false or true respectively). %_FROM_CONSOLE% is still set correctly by the code, but the value of %_PAUSE_NEEDED% is not subsequently set based upon %_FROM_CONSOLE%. It is just passed through.
And also note that the code will incorrectly detect execution as indirect (%_FROM_CONSOLE%=0) within a subshell if that subshell is opened with a command containing switches/options (eg, cmd /x). Generally this isn't a big problem as subshells are usually opened without extra switches and %_PAUSE_NEEDED% can be set to 0, when necessary.
Caveat codor.
#echo %CMDCMDLINE% | find /I " /c " >nul && pause

Resources