What is the simplest and/or most readable method to IF with AND in a CMD shell? In pseudo code:
IF EXIST file1 AND file2:
then do stuff
ELSE:
do something else
this Q has to be somewhere on SO but my search-fu isn't working for this one. sorry
Assuming you are talking about DOS/Windows batch files, I think you want something like this:
SET do_stuff=false
IF EXIST file1 IF EXIST file2 SET do_stuff=true
IF "%do_stuff%"=="true" (
REM do stuff
) ELSE (
REM do something else
)
The source of the ugliness is that DOS batch file if statements do not have and and or operators, so you have to either write nested if statements (which can lead to duplicated code in the then and else clauses), or capture the expression result into a variable and then do an if statement on its value (lots more lines). I favor the second approach to avoid duplication of code. Perhaps this is all a security feature. :)
I found some good examples here and here (SO, even). But you can also just use the help system built into the shell (help if or if /? IIRC).
An alternative to the rather nice suggestion by #Randall Cook could be going like this:
IF EXIST file1 IF EXIST file2 (
do stuff
GOTO cont
)
do something else
:cont
get on with other stuff
IF EXIST A (
IF EXIST B (
ECHO A and B exist
)
)
I've learned a bit since #RandallCook's answer was the best for me. This is what I'd use now:
#echo off
IF EXIST "File1" IF EXIST "File2" GOTO :do_stuff
GOTO :not_exist
GOTO :EOF
:do_stuff
echo File1 and File2 exist.
echo -- Doing stuff here...
goto :EOF
:not_exist
echo Condition not met, not doing stuff.
goto :EOF
:EOF a predefined label that will exit the current subroutine or script.
For those that prefer CALL over GOTO because it leads to cleaner code in longer scripts, we need to complicate things a little bit, but is still readable:
#echo off
:: Successful CD resets errorlevel to 0, in case it was already set this shell
cd
IF EXIST "File1" IF EXIST "File2" CALL :do_stuff
IF ERRORLEVEL 10 GOTO :EOF
CALL :not_exist
GOTO :EOF
:do_stuff
echo File1 and File2 exist.
echo -- Doing stuff here...
exit /b 10
goto :EOF
:not_exist
echo Condition not met, not doing stuff.
goto :EOF
set /p k="Please enter Choice : "
if "%k%" == "A" goto A
if "%k%" == "B" goto B
:A
echo "Hello from A"
:B
echo "Hello from B"
Related
Hey does anyone know if there is a command or something that allows me to have all the letters, numbers and characters in one?
:start
set input=
set /p input=[Y / N]?
if %input%==Y goto y
if %input%==N goto n
if %input%==* goto this
:y
goto end
:n
exit
:this
#echo. Only J or N
:end
Here you can already see that I tried to get everything in one with "*" which unfortunately didn't work...
Thank you for trying to help me but I would like to point out again that I need help to find something that allows me to use all the characters, letters and symbols in one.
example:
if I take y then he goes on.
if I take n then it closes. and when i use g or h he says: "can't do,
just y and n (for yes and no)"
but things like "*" or "%word%" don't work and yes, I have "choice /?" Already tried but when I do it like this it doesn't work either:
for %%i in (
D o " you " k n o w " to " h e l p " me
) do (<nul set /p "=.%bs%%%~i" & >nul ping -n 1 localhost)
# echo ?
CHOICE
[I don't care about syntax errors or something. if it works then it fits, it's just this one thing to use all characters, letters and symbols in one.]
No hate <3
if has no wildcard, but as I already commented, you don't need the third if at all. If Y and N are already handled, it can only be "anything else"
:start
set "input="
set /p "input=[Y / N]? "
if "%input%"=="Y" goto y
if "%input%"=="N" goto n
echo it's Y or N, nothing else&goto :start
The quotes makes it a little bit safer, but note this still isn't safe at all.
Cmd handles several characters (so called "poison chars" like <>|&"!) different (and even more under certain conditions).
A more secure method:
#echo off
setlocal enabledelayedexpansion
:loop
set "input="
set /p "input="
>nul 2>&1 (
echo/!input!|findstr /x "Y" >nul && goto YES
echo/!input!|findstr /x "N" >nul && goto NO
)
echo FAIL
goto :loop
:YES
echo yeah
goto :eof
:NO
echo nope
goto :eof
(Disclaimer: I'm quite sure, someone will find an input-string to make it fail too)
Note: both if and findstr support the /i switch to make them case-insensitive.
And just for the sake of showing everyone else how to perform this task using the appropriate commands:
%SystemRoot%\System32\choice.exe
If ErrorLevel 2 Exit /B
Rem The code you had under :end replaces this line
can someone tell me how can I make errorlevel stop taking value 1 or bigger? I want it to take into consideration only the exact value. If I choose 2, I want it to take the second option.
Right now if I choose '1', it executes the option for DLL file instead of LOG. I tried different versions like:
if errorlevel 1, tried using brackets with else etc. but none of them worked. Whats wrong with this code?
#echo off
cls
choice /C 12 /M "dll or log?"
if %errorlevel%=="2" dir %1\*.dll >> %2.txt
echo DLL
goto end
if %errorlevel%=="1" dir %1\*.log >> %3.txt
echo LOG
goto end
:end
exit /b
The ERRORLEVEL value is always numberic. The "compare ops" can be used. See IF /? for more infornation.
Quoting file system paths is needed in case there are SPACE or other special characters in the path. As mentioned by #Gerhard, using a TILDE in "%~3" removes any existing QUOTATION MARK characters around the %3 parameter.
BTW, this script does not check that %1, %2%, or %3 have any actual value or that the value it has is usable.
#echo off
cls
SETLOCAL ENABLEEXTENSIONS
choice /C 12 /M "dll or log?"
if %errorlevel% EQU 2 (
dir "%~1\*.dll" >> "%~2.txt"
echo DLL
goto end
)
if %errorlevel% EQU 1 (
dir "%~1\*.log" >> "%~3.txt"
echo LOG
goto end
)
:end
exit /b
this resolve:
#echo off
cls
rem choice /C 12 /M "dll or log?"
choice /C 12 /M "log or dll?"
if %errorlevel%==2 (dir %1\*.dll >> %2.txt
echo DLL
goto end)
if %errorlevel%==1 (dir %1\*.log >> %3.txt
echo LOG
goto end)
:end
exit /b
you need control the flow whith "(" ")" and the " around the errorlevel number is not necessary.
BONUS
This an alternative method that is more flexible:
#echo off
if "%~3"=="" echo lack of parameters & goto :EOF
cls
choice /C 12 /M "log or dll?"
Goto Choice%ErrorLevel%
:Choice2
dir "%~1\*.dll" >> "%~2.txt"
echo DLL in %~1
goto end
:Choice1
dir "%~1\*.log" >> "%~3.txt"
echo LOG in %~1
goto end
:end
exit /b
The ~ in the parameters remove eventual sorround double quote. Then the surrond quoted added can prevent parameters with space inside.
Basically, what is happening is my code continues to generate the same number. I have turned echo off to see the problem and the actual %random% variable is changing, but it seems like it does the math and says "No, it's really this number." Here's the code:
set /a num=(24 * %random%) / 32768 + 1
I have tested that code by itself and it works fine. However when I add my if statements* to the code, it goes all whack. So, the question is, how do I fix this and why is it happening?
*My if statements:
if "%num%"=="24" (
echo X
set /p ans=
if "%ans%"=="litin" (
echo.
echo Correct!
pause>nul
goto generate
)
)
All of my if statements look like that.
Sounds like you might have an issue with variable expansion. Assuming you are doing this stuff in a loop.
Try adding setlocal at the top of your program like this:
setlocal ENABLEDELAYEDEXPANSION
And endlocal at the end of your program:
endlocal
Now, when you need to use a variable that is changing inside a loop, use the ! syntax instead of %:
if "!num!"=="24" (
echo X
set /p ans=
if "!ans!"=="litin" (
echo.
echo Correct!
pause>nul
goto generate
)
)
You might want to use EQU for comparing numbers. So try this
if "%num%" EQU 24 (
...
)
i want to use my %variable% to manage the conditional clauses in a IF.. THEN.. ELSE in a batch file.
Something like the following:
set variable=%%homedrive%% EQU C:
if %variable% (
echo test ok
) else (
echo test fail
)
if i write on a cmd console:
set test=1 equ 1
if %test% echo OK
it works!
i'll use it in a for /f cicle:
this is my pseudo codethis is my pseudo code to correct
(
rem echo "%systemdrive%;;"
echo "%%COMPUTERNAME%% EQU [x];[some parameters1]"
echo "%%USERNAME%% NEQ [y];[some parameters2]"
echo "%%LOGONSERVER%% EQU [z];[some parameters3]"
[..]
) > "%temp%\CSG_fs.tmp"
[..]
for /f "usebackq tokens=1-2* delims=;" %%a in ("%temp%\CSG_fs.tmp") do (
set cond=%%a& set cond=!cond:~1!
set parm=%%b& set parm=!parm:~0,-1!
echo - cicle: "!cond!" --^> "!parm!"
call if !cond! call:CSG_sub_fs !parm!
echo - done
)
goto:eof
:CSG_sub_fs
[..]
goto:eof
--edit--
how can i use the variable !cond! to decide if execute the call to CSG_sub_fs?
call if !cond! call:CSG_sub_fs !parm!
does not work because it returns: "Can not find the batch label specified - IF"
and if i use
if !cond! call:CSG_sub_fs !parm!
it will say: "call:CSG_sub_fs not expected"
Well - there doesn't seem to be a question, so it's not that easy to answer.
You have a problem with
echo "^%COMPUTERNAME^% EQU [x];[some parameters1]"
because ^ does not escape % - % escapes % - use %%COMPUTERNAME%%...
(you should have been able to check this just by TYPEing "%temp%\CSG_fs.tmp"
Next problem is that
for /f "tokens=1-2* delims=; usebackq" %%a in (%temp%\CSG_fs.tmp) do (
may process the file %temp%\CSG_fs.tmp provided %temp%\CSG_fs.tmp contains no spaces, semicolons or commas. If it contains any of these deafult separators, or certain other characters with a special meaning, then you must enclose the filename in double-quotes "%temp%\CSG_fs.tmp"and use the usebackq option.
You've attempted to use usebackq but DELIMS must be the LAST option if it is used. Your code would set ";","","u","s","e","b","a","c","k" and "q" as delimiters.
Beyond that, perhaps if you explain what you intend to achieve, we'd be able to devise the appropriate code.
Try this:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
CLS
REM I'm setting these variables for testing.
REM That isn't ususally a good idea but the SETLOCAL
REM will ensure they are restored on exit
SET computername=[x]
SET logonserver=[z]
(
rem echo "%systemdrive%;;"
echo "%%COMPUTERNAME%% EQU [x];[some parameters1]"
echo "%%USERNAME%% NEQ [y];[some parameters2]"
echo "%%LOGONSERVER%% EQU [z];[some parameters3]"
) > "%temp%\CSG_fs.tmp"
for /f "usebackqtokens=1-2* delims=;" %%a in ("%temp%\CSG_fs.tmp") do (
set cond=%%a& set "cond=IF !cond:~1! CALL :csg_sub_fs "
set parm=%%b& set parm=!parm:~0,-1!
CALL :varcmd "!cond!" "!parm!"
)
GOTO :eof
:varcmd
%~1 %~2
GOTO :eof
:csg_sub_fs
ECHO parameters supplied to csg_sub_fs were: %*
GOTO :eof
I've forced the variablenames to match the conditions you've used in order to trigger the subroutine calls. Change as you need to prove your concept.
And dont worry about imperfect English. I'm sure I wouldn't do as well in your language!
The following DOS script snippet has a bug:
if not exist %MyFolder% (
mkdir %MyFolder%
if %errorlevel% GEQ 1 (
rem WARNING: the line above has a bug!
rem %errorlevel% will be the errorlevel
rem of the if statement because of the (parentheses)
echo Error: Could not create folder %MyFolder%
goto AnErrorOccurred
)
)
The fix is to use setlocal enabledelayedexpansion as follows:
setlocal enabledelayedexpansion
if not exist %MyFolder% (
mkdir %MyFolder%
if !errorlevel! GEQ 1 (
rem WARNING: the line above has a bug!
rem !errorlevel! will be the errorlevel
rem of the if statement because of the (parentheses)
echo Error: Could not create folder %MyFolder%
endlocal & goto AnErrorOccurred
)
)
endlocal
A full explanation of why is available here: Batch file fails to set environment variable within conditional statement
I want to audit my code to find instances of this bug, I figure a Regex would be an appropriate match, but haven't managed to get one working...
I think the ingredients should be:
Match an environment variable surrounded with %percentsigns%
That is inside (parentheses)
Any suggestions?
grepWin
I would use grepWin. Depending on the number of instances you have to find, you could write a regex that will give you all of them, plus some false positives.
Example: bug.bat
if not exist %MyVar% echo Hi!
if not exist %MyFolder% (
mkdir %MyFolder%
if %errorlevel% GEQ 1 (
rem WARNING: the line above has a bug!
rem %errorlevel% will be the errorlevel
rem of the if statement because of the brackets
echo Error: Could not create folder %MyFolder%
goto AnErrorOccurred
)
)
Then use a regular expression to match all lines that start with if and have an open parenthesis:
$ grep "^[ \t]*if.*(" bug.bat
if not exist %MyFolder% (
if %errorlevel% GEQ 1 (
grepWin will show you all the files that match.
Percent Symbols
By request:
grep "^[ \t]*if.*%.*%.*(" bug.bat
You can't solve it with Regex, because you can't count the parenthesis with a regular language. For example:
Stuff (
More Stuff (
Less Stuff )
A %var%
Less Stuff )
There is no ( between the last ) and the variable. Since I can't count how many `( appear before to know if there's one open, I can't do this with regular expressions.
As you can see, he is using ! rather than % even in the rootlevel of the batch.
So basically you should be able to alternate every % to an ! for your environmental variables.
One other way is to use the AND (&&) and OR (||) tests for success (errorlevel 0) or error (errorlevel > 0), as in:
if not exist %MyFolder% mkdir %MyFolder%|| (
echo Error: Could not create folder %MyFolder%
goto AnErrorOccurred
)
I hope this can help.
And by the way, you do not fail to set the variable, you fail to read it back.
What happen is, within parenthesis, using %%'s give you the states which was before you entered the said parenthesis. The only thing I knows work well is the math "set /a toto+=1" which will correctly increment the variable. Otherwise you have two options:
Either use a called function to set the variable or use the setlocal ENABLEDELAYEDEXPANSION statement, as previously stated, and use !!'s within the parenthesis.