I'm trying to make a short BAT file and I'm having trouble with one of its functions. I've tried a number of different ways to do this and none of them seem to work, but being a beginner at this I can't figure out the problem. Basically, the script, as it runs, is supposed to check if a certain .BAT file exists, and if it does, the script asks if the user wants to run it. If the user indicates Y, the other BAT is called and then the original script proceeds. If the user indicates N, the script is supposed to proceed without calling the other BAT. So far the script always notices and asks about the file, but choosing Y at the prompt never works. I'm sure the solution is obvious, but it's escaping me. Here's the code:
SET /P kmname=Enter database name:
:kmstart
IF EXIST C:\Visual\area\%kmname%\%kmname%.flt (
ECHO %kmname%.flt found, will now create %kmname%.ive.
CD C:\Visual\area\%kmname%\
IF EXIST Preprocess.bat (
SET /P kmpreproc=Found Preprocess.bat. Do you want to run it now?
IF /I "%kmpreproc%" EQU "Y" (
GOTO PREPROC
) ELSE (
GOTO CONTINUE
)
)
GOTO CONTINUE
) ELSE (
ECHO C:\Visual\area\%kmname%\%kmname%.flt does not exist. Try again.
SET /P kmname=Enter database name:
GOTO kmstart
)
:PREPROC
ECHO Running Preprocess.bat.
:CONTINUE
ECHO Continuing process.
PAUSE
The problem is your variables are being evaluated before they enter the if's, which means cmd won't see any changes until they have ended.
This is causing problems for you as your variables kmpreproc and, depending on the first if result, kmname change within the if blocks.
The fix (presuming the rest of your code is working) is to enable delayed expansion and use delayed expansion instead of normal expansion, by changing the %'s to ! on your variables.
setlocal enabledelayedexpansion
SET /P kmname=Enter database name:
:kmstart
IF EXIST C:\Visual\area\!kmname!\!kmname!.flt (
ECHO !kmname!.flt found, will now create !kmname!.ive.
CD C:\Visual\area\!kmname!\
IF EXIST Preprocess.bat (
SET /P kmpreproc=Found Preprocess.bat. Do you want to run it now?
IF /I "!kmpreproc!" EQU "Y" (
GOTO PREPROC
) ELSE (
GOTO CONTINUE
)
)
GOTO CONTINUE
) ELSE (
ECHO C:\Visual\area\!kmname!\!kmname!.flt does not exist. Try again.
SET /P kmname=Enter database name:
GOTO kmstart
)
:PREPROC
ECHO Running Preprocess.bat.
:CONTINUE
ECHO Continuing process.
PAUSE
Here is below my corrected and tided up code :
#echo off
SET /P kmname=Enter database name:
:kmstart
IF EXIST C:\Visual\area\%kmname%\%kmname%.flt (
ECHO %kmname%.flt found, will now create %kmname%.ive.
CD C:\Visual\area\%kmname%\
IF EXIST Preprocess.bat (
SET /P kmpreproc=Found Preprocess.bat. Do you want to run it now?
IF /I "%kmpreproc%"=="Y" (
GOTO PREPROC
)
GOTO CONTINUE
)
)
ECHO C:\Visual\area\%kmname%\%kmname%.flt does not exist. Try again.
SET /P kmname=Enter database name:
GOTO kmstart
:PREPROC
ECHO Running Preprocess.bat.
call "cmd /c start Preprocess.bat"
pause
:CONTINUE
ECHO Continuing process.
PAUSE
If it doesn't work try writting setlocal EnableDelayedExpansion under #echo off and replacing all % with ! .
Related
So i have this simple homework of doing a batch file that shows 3 options, and an error message for the entry for the option. so far it seems simple for me. I saw a youtube video and according to what i know it suppose to run ok but the problem is that when I run it and choose option 2 it opens also option 1, and when I choose option 3 it doesn't exit the program. And when I choose a parameter that is not specify the error message do not show. I am gonna copy paste my batch file because is easy to understand.
#echo off
echo Choose an option:
echo.
echo 1)Open Disk cleanup
echo 2)Open Disk Defragmenter
echo 3)Exit
echo.
Set /p Op=Write the option:
if %op%==1 (
start %windir%\system32\cleanmgr.exe
)
else if %op%==2 (
start %windir%\system32\dfrgui.exe
)
else if %op%==3 (
exit
)
else (
cls
echo Error not defined
)
pause
Is there something that I am missing or wrote wrong please let me know. If you can copy paste and run it in your computer and tell me if it is working fine because something tell me that in the process of learning I try a few times and i think I messed up with something that suppose to run correctly the file since my instructor told me that we have to be careful when using the commands in a batch file.
Your code formatted correctly but is susceptible to command injection because of the usage of the SET /P command.
#echo off
echo Choose an option:
echo.
echo 1)Open Disk cleanup
echo 2)Open Disk Defragmenter
echo 3)Exit
echo.
Set /p Op=Write the option:
if "%op%"=="1" (
start "" "%windir%\system32\cleanmgr.exe"
) else if "%op%"=="2" (
start "" "%windir%\system32\dfrgui.exe"
) else if "%op%"=="3" (
exit /b
) else (
cls
echo Error not defined
pause
)
A best practice solution which handles invalid input options.
#echo off
echo Choose an option:
echo.
echo 1)Open Disk cleanup
echo 2)Open Disk Defragmenter
echo 3)Exit
echo.
choice /C 123 /N /M "Select an Option:"
set "op=%errorlevel%"
if "%op%"=="1" start "" "%windir%\system32\cleanmgr.exe"
if "%op%"=="2" start "" "%windir%\system32\dfrgui.exe"
if "%op%"=="3" exit /b
I did it with this code.
#echo off
echo Choose an option:
echo.
echo 1)Open Disk Cleanup Utility
echo 2)Open Disk Defragmenter
echo 3)Exit
echo.
Set /p op/3=Write the number option:
if %op/3%==1 (
start %windir%\system32\cleanmgr.exe
)
if %op/3%==2 (
start %windir%\system32\dfrgui.exe
)
if %op/3%==3 (
exit
)
else (
cls
echo Error not defined
)
pause
the only problem that I have is that the error message appears even though options 1 and 2 are selected, it executes the options but it also shows the error message. I only want that the error message to show if any of those parameters aren't chosen.
I know that else is responding because if I choose option 1 else it executes because is not number 2 or 3. What it comes to my mind is if it is possible to define the op variable in an enumeration format like a group set of options.
There are two issues with the batch below. The first is, when batch file when opened prompts the user with a "y/n" question:
Question 1
Has the check been done
If the answer to this is "y" then another "y/n" question is displayed
Question 2
Do you want to send the DOSE report
If the answer to question 1 is "n" the check function is called and another question is displayed. However, currently the line in bold is being displayed then it is going to the second part (the goodbye function). What am I doing wrong? Thank you :).
Current batch
#ECHO OFF
:: ask user
:choice
set /P c=Has the check been done [y/n]
if /i %c%==y (
set /P c=Do you want to send the DOSE report[y/n]?
) else (
if /i %c%==n goto check
)
if /i %c%==y (
"L:\NGS\HLA LAB\total quality management\QC & QA\DOSE reports\DOSE reporting form.xlsm"
) else (
if /i %c%==n goto goodbye
)
:check
set /P c=Do you want to perform the check [y/n]
if /i %c%==y (
echo "perform check and hit enter when complete"
pause goto choice
) else (
if /i %c%==n goto goodbye
:: count loop
set var1=0
:loop
set /a var1=%var1%+1
echo %var1%
if %var1% EQU 1 (
goto end
) else (
goto loop
)
:end
echo "the DOSE report has already been sent by %USERNAME% on %DATE% at %TIME%"
:goodbye
echo "goodbye"
TIMEOUT 2 /nobreak
exit
First off, the if-else statement in :choice is missing its closing )
Heres an important note on user input, never trust it. Try putting something other than y/n in the first
:choice
set /p c=Has the check been done [y/n]
if /i %c%==y (
set /p c=Do you want to send the DOSE report[y/n]?
) else (
if /i %c%==n goto check
)
If you input something invalid into the first if-else , say somebody tries typing no instead of n, it will fail to return them to :choice as it only checks for n or y
And end up running through script. In your case it fails the if statements before :check and starts :check's proccess, but in check the same issue arises, and it will run through to ::count loop and the following commands where it can mess up your data.
After each if-else statement, its VERY safe practice to add a default action, such as;
:choice
set /p c=Has the check been done [y/n]
if /i %c%==y (
set /p c=Do you want to send the DOSE report[y/n]?
) else (
if /i %c%==n goto check
)
:[ If both if statements are false, it will reach this line: ]
cls
echo Error: "%c%" is not y/n.
pause
goto :choice
Another thing to note, if nothing is input, errors will occur. You can fix this by checking if the variable is defined right after set /p c=Has the check been done [y/n] using:
if not defined c (
cls
echo Error: Input cannot be empty!
pause
goto :choice
)
So a proper way to do the first check would be:
:choice
set /p c=Has the check been done [y/n]
:[Empty check ]
if not defined c cls & echo Error: Input cannot be empty! & pause & goto :choice
if /i "%c%==y" (
set /p c=Do you want to send the DOSE report[y/n]?
) else (
if /i "%c%==n" goto check
)
:[ Invalid input ]
cls & echo Error: "%c%" is not y/n. & pause & goto :choice
To answer your question directly... well, I'm not sure what you're asking to be honest. I'm having trouble deciphering "I am trying to have the below batch file increment the counter %var1 only if the "y" and not able to get the syntax correct."
I don't see any outright syntax errors, but you do have quite a few logic issues with your script. Indeed, the value of %var1% is being incremented. Put an echo Var1 incremented: %var1% after the set /a var1+=1 and see for yourself. To fix your script, you need to address what statements get executed under what conditions.
Use setlocal. It's just good practice. Using setlocal helps you avoid contaminating the outer console thread with variables that are only relevant to the runtime of this script.
Don't trust that the user will always answer "y" or "n". What happens if they answer "w"? As your script is written, it will proceed to sections you probably didn't intend. I have two suggestions for this.
a. Don't explicitly check for an answer of "n". If it's not "y", assume it's "n". If that is not appropriate, then have a final else goto label for whatever label immediately precedes the conditional.
b. Alternatively, instead of set /P, consider using choice. Example:
choice /c YN /m "Do you want to perform the check? "
if errorlevel 2 (
rem // user chose "N"
) else (
rem // user chose "Y"
)
In your :choice section of code, what is the difference between an answer of Y then N, versus an answer of N? As written, there's no difference. Either way, the script proceeds to :check. Examine your logic here.
Instead of exit, consider using either exit /b or goto :EOF. When running your script from a cmd prompt (as opposed to double-clicking it), you should avoid exiting the parent thread.
For goodness sake, indent your code and add some line breaks! That big blob of left-justified commands has no flow, no rhythm. It's tedious to read and troubleshoot.
Before:
#ECHO OFF
:: ask user
:choice
set /P c=Has the check been done [y/n]
if /i %c%==y (
set /P c=Do you want to send the DOSE report[y/n]?
) else (
if /i %c%==n goto check
)
:check
set /P c=Do you want to perform the check [y/n]
if /i %c%==y (
set /P c=please complete the check and click enter
goto file
) else (
if /i %c%==n goto goodbye
)
It's like a slab, difficult to see at a glance which statements are ancestors and which are descendents or where the section breaks occur.
After:
#ECHO OFF
setlocal
:choice // ask user
choice /c YN /m "Has the check been done? "
if not errorlevel 2 (
choice /c YN /m "Do you want to send the DOSE report? "
if errorlevel 2 (
goto goodbye
)
)
:check
choice /c YN /m "Do you want to perform the check? "
if errorlevel 2 goto goodbye
set /P "c=Please complete the check and click enter: "
See? It's much more pleasant to read that way. By the way, you might've noticed that I combined the "ask user" comment with the :choice label. That's often done when defining functions to explain expected parameters, or just explain what's going on. You don't really need the double slashes. It just makes Stack Overflow's Ted Turner Technicolor parser demonstrate that that's a comment.
This code is part of a chat program that I am currently working on. The 'else' part of my program is the one that doesn't work. The program quits instead of going to :home
:join
cls
if not exist "C:/Users/Public/room.cmd" (
echo No room has been found.
echo.
set /p choiceretry=Do you want to retry? y/n
if "%choiceretry%"=="y" goto join
if "%choiceretry%"=="n" goto home
) else (
cls
"C:/Users/Public/room.cmd"
echo A room has been found.
pause >nul
echo Joining
set roomjoined=1
echo %roomjoined%
goto home
)
:home
echo this finally works
pause
I have tried changing the code several times starting from 'echo Joining'
Anyone know why cmd quits?...
:) :) :)
Thanks in advance
The problem is the way you run room.cmd; you must use call to return from it:
call "C:/Users/Public/room.cmd"
Otherwise, execution will not return from room.cmd to the original batch file that ran it.
Hint: Consider to use choice instead of set /P for Y/N decisions.
Firstly, please don't left justify your code blocks. It's much easier to read code that's properly indented.
Secondly, when retrieving values within a code block, you need delayed expansion. See setlocal /? in a cmd prompt for more information. This is the reason for the unexpected behavior. Your variables retrieved within the same parenthetical code block in which they were set won't contain the values you expect unless you retrieve them with delayed expansion syntax. As an alternative, you could use the choice command and if errorlevel, which would result in a bit nicer user experience I think.
Thirdly, when testing user input, you should use the /i switch in your if statements for case-insensitivity. This isn't relevant if using choice / if errorlevel though.
Fourthly, Windows paths use backslashes, not forward slashes.
I'd fix it this way:
#echo off
setlocal
:join
cls
if errorlevel 1 set /P "=Retrying... "<NUL
if not exist "C:\Users\Public\room.cmd" (
echo No room has been found.
echo.
choice /c yn /n /m "Do you want to retry? [y/n] "
if errorlevel 2 goto home
goto join
) else (
"C:\Users\Public\room.cmd"
echo A room has been found.
pause >nul
echo Joining
set roomjoined=1
)
:home
echo this finally works
pause
I would like to know why do I get an error("( was not expected at this time") in this script.
for /f "delims=" %%t in (%cd%\Multitool\Multitool.txt) do (
set /p username=
set /p password=
set /p created=
)
if %created%==accountcreated ( goto CONTINUE ) else ( goto CREATE )
I have those lines of codes wich always get me an error: "( was not expected" at this time.
In my program I want to know if the user created an account already...
When the user creates an account I echo this in %cd%\Multitool\Multitool.txt :
(
echo %username%
echo %password%
echo accountcreated
) > %cd%\Multitool\Multitool.txt
So can you tell me why I get that error and how to correct it please?
Sorry for my bad english im french...
#ECHO OFF
SETLOCAL enabledelayedexpansion
:: set up logfile and password
SET "logfile=U:\q28542185.txt"
SET "password=dummypassword"
SET "myusername=dummyusername"
:: Dummy log entry
(
echo %myusername%
echo %password%
echo accountcreated
) > %logfile%
:: This is just to ensure the three variables are empty
FOR %%t IN (myusername password created) DO SET "%%t="
for /f "delims=" %%t in (%logfile%) do (
SET "myusername=!password!"
SET "password=!created!"
SET "created=%%t"
)
if %created%==accountcreated ( goto CONTINUE ) else ( goto CREATE )
:continue
ECHO got to continue
GOTO :eof
:create
ECHO got to create
GOTO :EOF
I used a file named q28542185.txt to receive the log information for my testing.
Note that username is a magic variable initialised by the syste, so I don't like changing it, so I've substituted myusername.
The first part sets up a dummy log file for testing.
The replacement for loop reads the log file and "ripples" the lines through the variablenames using the delayedexpansion facility, so that the variables are set in appropriate sequence.
Note that if created contains Spaces then your if statement should be modified to
if "%created%"=="accountcreated" ....
to allow correct parsing sequence IF token operator token
So, how I have it done right now, is that it that it calls another bat file to update it, and then that batch file updates, and sets %ERRORLEVEL% to 1. At the start of the original program, it checks if errorlevel is 1, if yes, it goes to the main menu, but right now, it doesn't call the update file, it just goes to the menu. This is my code
Main program
IF %errorlevel% EQU 1 goto begin
call updater.bat
:begin
echo MENU
Updater
set=errorlevel 1
wget (updatelink here)
call mainprogram.bat
Right now, sometimes it works, sometimes it doesn't, which leads me to believe that some command is somehow increasing the errorlevel, but the only code before the errorlevel check is
#echo off
color 0f
cls
set currentver=v0.5.6
(check code)IF %errorlevel% EQU 1 goto begin
https://code.google.com/p/flashcart-helper/source/browse/trunk/0.6/FlashcartHelperRobocopy.bat
Here is what I have right now.
Don't play around with errorlevel. It's an internal variable. At the start of a batch, errorlevel will be 0 because all you've done is set a local variable. This will almost always ( never say never ) succeed. Also, if errorlevel is 1, and I'm reading this correctly you also seem to have an infinite loop? From what I understand of what you've said your batches are like this:
Main
#echo off
color 0f
cls
set currentver=v0.5.6
IF %errorlevel% EQU 1 goto begin
call updater.bat
:begin
echo MENU
Updater
set=errorlevel 1
wget (updatelink here)
call mainprogram.bat
As errorlevel get's overwritten each time you do anything you're asking for trouble. Change %errorlevel% to %error% and it should solve your problems. As it's a local environment variable it should also be passed between batch files. Just be careful not to use error elsewhere.
Here is a solution using Dropbox Public Folders and no wget. It uses PowerShell that in on Win7+ machines.
Update the below https://dl.dropboxusercontent.com/u/12345678/ url with your own.
It auto creates a .conf file for configuration.
Set __deploy_mode to 1 for the file on dropbox so the version file can be updated but the script not accidentally executed.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET time_start=%time%
SET time_choice_wait=20
SET script_ver=1.00
SET script_name=%~n0
SET server_url=https://dl.dropboxusercontent.com/u/12345678/
SET script_name_bat=%~dp0%script_name%.bat
SET script_name_cfg=%~dp0%script_name%.conf
SET script_name_latest_ver=%~dp0%script_name%.latest.ver
ECHO %script_name% v%script_ver%
ECHO %script_ver% > %script_name%.current.ver
IF NOT EXIST "%script_name_cfg%" CALL :SCRIPT_MISSING_CFG
FOR /f "delims=" %%x IN (%script_name%.conf) DO (SET "%%x")
IF %__deploy_mode% EQU 1 GOTO :EOF
IF %auto_update_compare% EQU 1 CALL :SCRIPT_COMPARE_VER
:SCRIPT_MAIN
REM =======================================
REM === EDIT BELOW THIS LINE ==
REM TODO Add main content
ECHO.
ECHO Waiting for content...
REM === EDIT ABOVE THIS LINE ==
REM =======================================
GOTO END
:SCRIPT_MISSING_CFG
ECHO Creating new %script_name%.conf file...
ECHO __deploy_mode=0 > "%script_name_cfg%"
ECHO repository_base_url=%server_url% >> "%script_name_cfg%"
ECHO auto_update_compare=1 >> "%script_name_cfg%"
ECHO auto_update_download=1 >> "%script_name_cfg%"
ECHO Update %script_name%.conf as needed, then save and close to continue.
ECHO Waiting for notepad to close...
NOTEPAD "%script_name_cfg%"
GOTO :EOF
:SCRIPT_COMPARE_VER
ECHO Please wait while script versions are compared...
Powershell -command "& { (New-Object Net.WebClient).DownloadFile('%server_url%%script_name%.current.ver', '%script_name_latest_ver%') }"
IF NOT EXIST "%script_name_latest_ver%" GOTO END
SET /p script_latest_ver= < "%script_name_latest_ver%"
IF %script_ver% EQU %script_latest_ver% CALL :SCRIPT_COMPARE_VER_SAME
IF %script_ver% NEQ %script_latest_ver% CALL :SCRIPT_COMPARE_VER_DIFF
GOTO :EOF
:SCRIPT_COMPARE_VER_SAME
ECHO Versions are both %script_name% v%script_ver%
GOTO :EOF
:SCRIPT_COMPARE_VER_DIFF
ECHO Current Version:%script_ver% ^| Server Version:%script_latest_ver%
IF %auto_update_download% EQU 1 GOTO SCRIPT_DOWNLOAD_SCRIPT
ECHO.
ECHO Would you like to download the latest %script_name% v%script_latest_ver%?
ECHO Defaulting to N in %time_choice_wait% seconds...
CHOICE /C YN /T %time_choice_wait% /D N
IF ERRORLEVEL 2 GOTO SCRIPT_DOWNLOAD_NOTHING
IF ERRORLEVEL 1 GOTO SCRIPT_DOWNLOAD_SCRIPT
IF ERRORLEVEL 0 GOTO SCRIPT_DOWNLOAD_NOTHING
:SCRIPT_DOWNLOAD_SCRIPT
ECHO Please wait while script downloads...
Powershell -command "& { (New-Object Net.WebClient).DownloadFile('%server_url%%script_name%.bat', '%script_name_bat%') }"
ECHO Script Updated to v%script_latest_ver%^^!
REM User must exit script. Current batch is stale.
GOTO :END
:SCRIPT_DOWNLOAD_NOTHING
GOTO :EOF
:END
SET time_end=%time%
ECHO.
ECHO Script started:%time_start%
ECHO Script ended :%time_end%
:END_AGAIN
pause
ECHO.
ECHO Please close this window
ECHO.
GOTO END_AGAIN
You can do that through these steps:
1.put two files in server,a config file, a higher version bat file which need to update; set last version num. in config file.
2.client bat should be checked update at every startup time. you can read the news version in server config file, then compared to local bat file version. if not equal, so do update, else other wise.
Do you have any problems?