I've just started learning Batch, although I can't figure out the syntax for branching if-statements inside other if-statements. As in,
#echo off
set first=
set second=
set /P first=first?: %=%
if /I "%first%"=="y" (
echo a.1
set /P second=second?: %=%
if /I "%second%"=="y" (
echo b.1
) else if %second%=="n" (
echo b.2
) else (
echo b.3
)
) else if /I "%first%"=="n" (
echo a.2
) else (
echo a.3
)
pause
Any help with that would be appreciated.
Your IF logic is perfectly fine. Your problem is you need delayed expansion. %second% is expanded when the statement is parsed, and the entire complex IF logic is parsed in a single pass. So %second% expands to the value that existed before the IF statement is executed, which in your case is an empty string.
The solution is to enable delayed expansion and then use !second! to expand the value. Delayed expansion occurs after the statement is parsed.
#echo off
setlocal enableDelayedExpansion
set first=
set second=
set /P first=first?: %=%
if /I "%first%"=="y" (
echo a.1
set /P second=second?: %=%
if /I "!second!"=="y" (
echo b.1
) else if "!second!"=="n" (
echo b.2
) else (
echo b.3
)
) else if /I "%first%"=="n" (
echo a.2
) else (
echo a.3
)
pause
Related
I write script like this:
#ECHO OFF
setlocal EnableDelayedExpansion
set "remove=ABC"
echo. %remove%
Set FILENAME="456_789_ABC00011092_789_EFGHIK_56893.mpg"
for %%a in (%FILENAME:_=" "%) do (
set TEN=%%a
echo. %AB%
set "remove_1=ABC"
echo. %remove_1%
Set _TEN=!TEN:%remove%=!
echo. %_TEN%
Set i=0
IF !_TEN! NEQ !TEN! (
set /A i+=1
set "String[!i!]=%%~a"
)
)
pause
exit
Why echo. %AB% echo. %remove_1% result is
I replace % by !. It's work fine but command Set _TEN=!TEN:!remove_1!=! not run
Edit - (from the additional question currently posted as an answer)
When I use FindStr command like this:
for %%a in (%FILENAME:_=" "%) do (
echo %%a | findstr /I /R /C:"ABC" >nul
ECHO %errorlevel%
if "%errorlevel%" equ "0" (
set /A i+=1
set "String[!i!]=%%~a"
)
)
Why errorlevel always = 0
%AB% has not been defined within your posted script, so as it has no value will not be echoed, you will just get an empty line due to the . after echo. Because remove_1 is being set within the loop, (code block), you should be using the delayed expansion syntax, Echo !remove_1!. It is the same for echo. %_TEN%, i.e. Echo !_TEN!, and would have been Echo !AB! had it previously been defined. In order to get the double expansion needed to Set your _TEN variable, you could use a pseudo Call:
#Echo Off
SetLocal EnableDelayedExpansion
Set "FILENAME=456_789_ABC00011092_789_EFGHIK_56893.mpg"
For %%A In ("%FILENAME:_=" "%") Do (
Set "TEN=%%A"
Echo. !AB!
Set "remove_1=ABC"
Echo !remove_1!
Call Set "_TEN=!TEN:%%remove_1%%=!"
Echo !_TEN!
Set "i=0"
If "!_TEN!" NEq "!TEN!" (
Set /A i+=1
Set "String[!i!]=%%~A"
)
)
Pause
Exit /B
In your second related question, initially posted as an answer and now added as an edit to your original question; because the error level is being set within the loop, (code block), you should be using the delayed expansion syntax, !errorlevel!
#Echo Off
SetLocal EnableDelayedExpansion
Set "FILENAME=456_789_ABC00011092_789_EFGHIK_56893.mpg"
For %%A In ("%FILENAME:_=" "%") Do (
Echo %%A | FindStr /IRC:"ABC" >Nul
Echo !errorlevel!
If "!errorlevel!"=="0" (
Set /A i+=1
Set "String[!i!]=%%~A"
)
)
Set String[
Pause
Exit /B
Or if you don't need to Echo each error level to the screen, you can use a conditional statement &&:
#Echo Off
SetLocal EnableDelayedExpansion
Set "FILENAME=456_789_ABC00011092_789_EFGHIK_56893.mpg"
For %%A In ("%FILENAME:_=" "%") Do (
Echo %%A | FindStr /IRC:"ABC" >Nul && (
Set /A i+=1
Set "String[!i!]=%%~A"
)
)
Set String[
Pause
Exit /B
Simply asked, I need to check if a variable is numerical. I'm aware of the ability of:
set /a variable1=%variable%
setting non numerical strings to 0, but i need to be able to have 0 as an intiger as well as negative numbers.
This will be run very often, so a fast script is preferred. I've tried to echo the variable into a .txt, and use a for loop to scan through and return an error if anything other than 0-9 is detected, but the script is excessively long running, and frankly is a mess.
You could do something to this affect. Remove all numbers. If anything is left over it is not an integer. Not saying this is perfect but it is a step in the right direction.
set "tempvar="
FOR /F "tokens=* delims=-0123456789" %%G IN ("%variable1%") DO SET "tempvar=%%G"
IF DEFINED tempvar echo NOT AN INTEGER
As mentioned in question17584282
The easiest for digits should be:
IF %1 NEQ +%1 echo Notnumeric!
If negative numbers (hyphen) are also to be considered, this will work
SET number=%1
if %1 EQU +%1 echo positive number
if %1==-%number:-=% echo negative number
Learned from https://www.itprotoday.com/compute-engines/jsi-tip-9692-how-can-batch-script-determine-if-variable-or-parameter-integer
#echo off
:isInterer input [returnVar]
setlocal enableDelayedexpansion
set "input=%~1"
if "!input:~0,1!" equ "-" (
set "input=!input:~1!"
) else (
if "!input:~0,1!" equ "+" set "input=!input:~1!"
)
for %%# in (1 2 3 4 5 6 7 8 9 0) do (
if not "!input!" == "" (
set "input=!input:%%#=!"
)
)
if "!input!" equ "" (
set result=true
) else (
set result=false
)
endlocal & if "%~2" neq "" (set %~2=%result%) else echo %result%
try this.Some special symbols like ! and ^ could cause trouble though.
You can also use findstr:
#echo off
:isIntererFindstr input [returnVar]
setlocal enableDelayedexpansion
set "input=%~1"
if "!input:~0,1!" equ "-" (
set "input=!input:~1!"
) else (
if "!input:~0,1!" equ "+" set "input=!input:~1!"
)
echo !input!|findstr /r "[^0-9]" >nul 2>&1
if %errorlevel% equ 0 (
set result=false
) else (
set result=true
)
endlocal & if "%~2" neq "" (set %~2=%result%) else echo %result%
[simple program that recieves an integer as input and prints if that number is trivial or not]
when i run this i get an error "( was unexpected at this time"
#echo off
set /a i=2
set /p input="enter an integer: "
set /a n=input
set /a t=n/2
:loop1
if %t% LSS %i% (
goto trivial
) else (
set /a t0=n%i
if %t0%==0 (
goto notTrivial
) else (
set /a i=i+1
goto loop1
)
)
:trivial
echo %n% is trivial
goto endd
:notTrivial
echo %n% is not trivial
:endd
pause > nul
but when I remove else statement in loop1 (which is btw unnecessary (because of goto command in if block)) it works
:loop1
if %t% LSS %i% (
goto trivial
)
set /a t0=n%i
if %t0%==0 (
goto notTrivial
) else (
set /a i=i+1
goto loop1
)
(how) is this possible?
When you remove the else clause, the code inside it is now out of any block.
Why does it matter? Because in batch files, lines or blocks of lines (code inside parenthesis) are first parsed and then executed. While parsed variable read operations are removed, being replaced with the value inside the variable at parse time, before starting to execute the command (more here).
So, in this code
) else (
set /a t0=n%i
if %t0%==0 (
goto notTrivial
) else (
set /a i=i+1
goto loop1
)
)
you change the value of the variable t0, but you can not retrieve this changed value inside the same block. But if you remove the else clause the code is not inside a block and everything works as intended (except syntax errors, try with set /a "t0=n %% i").
Firstly, you need to state the modulo operator % as %% in batch files.
Secondly, just move the command set /a t0=n%%i up before the if block begins, then it will work:
:loop1
set /a t0=n%%i
if %t% LSS %i% (
goto trivial
) else (
if %t0% EQU 0 (
goto notTrivial
) else (
set /a i+=1
goto loop1
)
)
So the change of variable t0 is moved outside of a command block ().
Alternatively, you could also enable delayed expansion:
setlocal EnableDelayedExpansion
rem INITIAL CODE PORTION...
:loop1
if %t% LSS %i% (
goto trivial
) else (
set /a t0=n%%i
if !t0! EQU 0 (
goto notTrivial
) else (
set /a i+=1
goto loop1
)
)
rem REMAINING CODE PORTION...
endlocal
You will notice the !t0! type expansion which, in contrast to %t0%, will expand t0 at execution time rather than parse time.
See also setlocal /? and endlocal /? for more information about these commands.
So, I've the following batch script:
#echo off
set /p name=
rem a random number, don't care about it.
set complete_name=%name%.Creepy
Goto STEP1
:STEP1
echo %complete_name%|findstr /C:"9000" >nul 2>&1
if not errorlevel 1 (
goto 9000
) else (
GOTO CHECK2
)
:CHECK2
echo %complete_name%|findstr /C:"930" >nul 2>&1
if not errorlevel 1 (
goto 930
) else (
GOTO CHECK3
)
:CHECK3
echo %complete_name%|findstr /C:"310" >nul 2>&1
if not errorlevel 1 (
goto 310
) else (
ECHO PROBLEM
)
:9000
ECHO 9000
PAUSE
:930
ECHO 930
PAUSE
:310
ECHO 310
PAUSE
I want it to check if "9000" is in the variable or not, same for "930" and "310". And if none of these numbers are in the variable Echo problem. But everytime i run this script it goes to ECHO PROBLEM even if 9000/920/310 is in %complete_name%. So, is this the right way to check if a variable is in another one or there is an easier way to do it?
So I've tried this code:
#echo off
set name=310
set complete_name=%name%.Creepy
Goto STEP1
:STEP1
setlocal
if "%complete_name:9000=%"=="%complete_name%" (
if "%complete_name:930=%"=="%complete_name%" (
if "%complete_name:310=%"=="%complete_name%" (
echo PROBLEM
) else (
goto 9000
)
) else (
goto 930
)
) else (
goto 310
)
goto :eof
but I'm stuck at echo problem...
Save next code snippet, possibly named blabla.bat:
#Echo OFF
setlocal
set complete_name=%1
if "%complete_name:9000=%"=="%complete_name%" (
if "%complete_name:930=%"=="%complete_name%" (
if "%complete_name:310=%"=="%complete_name%" (
echo problem
) else (
echo valid 310
)
) else (
echo valid 930
)
) else (
echo valid 9000
)
Exit /B
and watch the output from
blabla x9000y
blabla x930y
blabla x310y
blabla x9a0b0c0y
That's a way of 1. nested IF ... ( ... ) ELSE ( ... ) and 2. using "Edit/Replace" a variable
Or, if GOTOs considered necessary, there is something similar with For loop (a good thought topic as well..).
#Echo OFF
setlocal EnableDelayedExpansion
set complete_name=%1
for %%G in (9000 930 310) DO (
if /I "!complete_name:%%G=!" neq "%complete_name%" GOTO :%%G
)
echo problem %complete_name%
GOTO :commonEnd
:9000
Echo valid 9000 %complete_name%
GOTO :commonEnd
:930
Echo valid 930 %complete_name%
GOTO :commonEnd
:310
Echo valid 310 %complete_name%
GOTO :commonEnd
:commonEnd
How can I check whether a directory is writable by the executing user from a batch script?
Here's what I've tried so far:
> cd "%PROGRAMFILES%"
> echo. > foo
Access is denied.
> echo %ERRORLEVEL%
0
Ok, so how about...
> copy NUL > foo
Access is denied.
> echo %ERRORLEVEL%
0
Not that either? Then what about...
> copy foo bar
Access is denied.
0 file(s) copied.
> echo %ERRORLEVEL%
1
This works, but it breaks if the file doesn't exist.
I've read something about internal commands not setting ERRORLEVEL, but copy obviously seems to do so in the last case.
Definitely running a command against it to find if its denied is the easy way to do it. You can also use CACLS to find exactly what the permissions are or aren't. Here's a sample.
In CMD type CACLS /?
CACLS "filename" will give you what the current permissions is allowed on the file.
R = Read, W = Write, C = Change (same as write?), F = Full access.
EDIT: You can use directory name as well.
So to do a check, you would:
FOR /F "USEBACKQ tokens=2 delims=:" %%F IN (`CACLS "filename" ^| FIND "%username%"`) DO (
IF "%%F"=="W" (SET value=true && GOTO:NEXT)
IF "%%F"=="F" (SET value=true && GOTO:NEXT)
IF "%%F"=="C" (SET value=true && GOTO:NEXT)
SET value=false
)
ECHO This user does not have permissions to write to file.
GOTO:EOF
:NEXT
ECHO This user is able to write to file.
You can write copy %0 foo to copy the batch file itself.
This will always exist.
Remember to delete the file afterwards, and to make sure that you aren't overwriting an existing file by that name.
There ought to be a better way to do this, but I don't know of any.
EDIT: Better yet, try mkdir foo.
In case the batch file is running off a network (or if it's very large), this may be faster.
set testdir=%programfiles%
set myguid={A4E30755-FE04-4ab7-BD7F-E006E37B7BF7}.tmp
set waccess=0
echo.> "%testdir%\%myguid%"&&(set waccess=1&del "%testdir%\%myguid%")
echo write access=%waccess%
i found that executing copy within the batch file echoed an error to STDERR, but left %ERRORLEVEL% untouched (still 0). so the workaround was to combine the command with a conditional execution of set.
copy /Y NUL "%FOLDER%\.writable" > NUL 2>&1 && set WRITEOK=1
IF DEFINED WRITEOK (
rem ---- we have write access ----
...
) else (
rem ---- we don't ----
...
)
this is tested on XP and 7 and seems to work reliably.
An extension to Mechaflash's answer, and solves the problem of overwriting the file by generating a unique filename for the "testing" file.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "a=%~1"
SET "b="
SET "g=0"
:a
SET "c= `1234567890-=qwertyuiop[]asdfghjkl;'zxcvbnm,.~!##$%%^&()_+QWERTYUIOP{}ASDFGHJKLZXCVBNM"
SET /A "d=0, e=1"
:b
IF "!c!" NEQ "" (
IF "!c:~%d%,1!" NEQ "" (
IF EXIST "!a!\!b!!c:~%d%,1!" (
SET "c=!c:~0,%d%!!c:~%e%!"
) ELSE (
SET /A "d=!d!+1, e=!e!+1"
)
GOTO :b
)
)
IF "!c!" EQU "" (
SET "c= `1234567890-=qwertyuiop[]asdfghjkl;'zxcvbnm,.~!##$%%^&()_+QWERTYUIOP{}ASDFGHJKLZXCVBNM"
:c
IF "!c!" NEQ "" (
IF "!c:~%d%,1!" NEQ "" (
SET /A "d=!d!+1"
GOTO :c
)
)
SET /A "d=!d!-1"
SET /A "f=%RANDOM%*!d!/32768"
SET "b=!b!!c:~%f%,1!"
GOTO :a
) ELSE (
SET /A "d=!d!-1"
SET /A "f=%RANDOM%*!d!/32768"
SET "b=!b!!c:~%f%,1!"
)
((ECHO EXIT>"!a!\!b!" && SET "g=1") & IF EXIST "!a!\!b!" DEL /F "!a!\!b!") >NUL 2>&1
ENDLOCAL & (SET "a=%g%")
IF "%a%" EQU "1" ECHO TRUE
(%~1 is the input directory)
EDIT: If you want a more safe option
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "a=%~1"
SET "b="
SET "g=0"
:a
SET "c= `1234567890-=qwertyuiop[]asdfghjkl;'zxcvbnm,.~!##$%%^&()_+QWERTYUIOP{}ASDFGHJKLZXCVBNM"
SET /A "d=0, e=1"
:b
IF "!c!" NEQ "" (
IF "!c:~%d%,1!" NEQ "" (
IF EXIST "!a!\!b!!c:~%d%,1!" (
SET "c=!c:~0,%d%!!c:~%e%!"
) ELSE (
SET /A "d=!d!+1, e=!e!+1"
)
GOTO :b
)
)
IF "!c!" EQU "" (
SET "c= `1234567890-=qwertyuiop[]asdfghjkl;'zxcvbnm,.~!##$%%^&()_+QWERTYUIOP{}ASDFGHJKLZXCVBNM"
:c
IF "!c!" NEQ "" (
IF "!c:~%d%,1!" NEQ "" (
SET /A "d=!d!+1"
GOTO :c
)
)
SET /A "d=!d!-1"
SET /A "f=%RANDOM%*!d!/32768"
SET "b=!b!!c:~%f%,1!"
GOTO :a
) ELSE (
SET /A "d=!d!-1"
SET /A "f=%RANDOM%*!d!/32768"
SET "b=!b!!c:~%f%,1!"
)
IF EXIST "!a!\!b!" (
SET "b=!b:~0,-1!"
GOTO :a
) ELSE (
((ECHO EXIT>"!a!\!b!" && SET "g=1") & IF EXIST "!a!\!b!" DEL /F "!a!\!b!") >NUL 2>&1
)
ENDLOCAL & (SET "a=%g%")
IF "%a%" EQU "1" ECHO TRUE