Batch - Variable giving different outputs at ECHO and IF - windows

I am currently implementing a postprocess in batch, and after some changes a few code snipplets simply don't work anymore....
What I am trying to do:
#echo off & setlocal
ECHO %netUse% //For debugging!
IF NOT "%netUse%" == "" ( //Double checking if Variable is set
IF %netUse% EQU 1 (
IF %netUsePW% EQU 1 (
NET USE %netUseLetter% %netPath% %pw% /user:%netUser%
)
IF %netUsePW% EQU 0 (
NET USE %netUseLetter% %netPath%
)
)
)
When I run that code, this is the output:
C:\Program Files (x86)\ID GmbH\PRODETTE\DAX\ISDN>ECHO 0
0 <--- That's right! It is set to ZERO via a "configuration" Batch
"1" kann syntaktisch an dieser Stelle nicht verarbeitet werden. <-- ???
C:\Program Files (x86)\ID GmbH\PRODETTE\DAX\ISDN> IF EQU 1 ( <-- Why isn't it comparing the variable?
And just for completionists sake, I am loading all of these variables form another batch file, "Settings.bat"
I am running that file via
CALL :postCopySettings.bat
contents:
SET netUse=0
SET netUsePW=
SET netUseLetter=
SET netPath=
SET netUser=
SET pw=
SET configLoaded=1
(Since netUse is 0, I don't need to populate the other variables... right?)
TL:DR: Batch doesn't work, but I don't know why.

The problem is that variable netUsePW is not set.
When netUse is set to 0, and netUsePW, netUseLetter, netUsePath, pw are all empty, your code...:
#echo off & setlocal
ECHO %netUse%
IF NOT "%netUse%" == "" (
IF %netUse% EQU 1 (
IF %netUsePW% EQU 1 (
NET USE %netUseLetter% %netPath% %pw% /user:%netUser%
)
IF %netUsePW% EQU 0 (
NET USE %netUseLetter% %netPath%
)
)
)
...expands to...:
#echo off & setlocal
ECHO 0
IF NOT "0" == "" (
IF 0 EQU 1 (
IF EQU 1 (
NET USE /user:0
)
IF EQU 0 (
NET USE
)
)
)
As you can see, there are two syntax issues, which cause the error:
IF EQU 1
IF EQU 0
Although netUse is set to 0 and you therefore expect the statements after IF %netUse% EQU 1 not to be executed, they are still parsed, because the entire code block is parsed before any commands are executed.
To overcome this, there are several ways:
Preset variables with non-empty values:
#echo off & setlocal
ECHO %netUse%
IF "%netUse%" == "" (set "netUse=-1" & set "setUsePW=-1")
IF NOT "%netUse%" == "" (
IF %netUse% EQU 1 (
IF %netUsePW% EQU 1 (
NET USE %netUseLetter% %netPath% %pw% /user:%netUser%
)
IF %netUsePW% EQU 0 (
NET USE %netUseLetter% %netPath%
)
)
)
Avoid code blocks by changing the logic a bit and using goto:
#echo off & setlocal
ECHO %netUse%
IF "%netUse%" == "" goto :SKIP
IF %netUse% NEQ 1 goto :SKIP
IF %netUsePW% EQU 1 (
NET USE %netUseLetter% %netPath% %pw% /user:%netUser%
)
IF %netUsePW% EQU 0 (
NET USE %netUseLetter% %netPath%
)
:SKIP
Avoid code blocks by moving some code into subroutines and using call to call them:
#echo off & setlocal
ECHO %netUse%
IF NOT "%netUse%" == "" call :TEST "%netUse%" "%netUsePW%" "%netUseLetter%" "%netPath%" "%pw%"
exit /B
:TEST
IF %~1 EQU 1 call :TESTPW "%~1" "%~2" "%~3" "%~4" "%~5"
exit /B
:TESTPW
IF %~2 EQU 1 (
NET USE %~3 %~4 %~5 /user:%~1
)
IF %~2 EQU 0 (
NET USE %~3 %~4
)
exit /B
Enable and apply delayed expansion to do the variable expansion during execution rather than while parsing:
#echo off & setlocal EnableDelayedExpansion
ECHO %netUse%
IF NOT "%netUse%" == "" (
IF !netUse! EQU 1 (
IF !netUsePW! EQU 1 (
NET USE %netUseLetter% %netPath% %pw% /user:!netUser!
)
IF !netUsePW! EQU 0 (
NET USE %netUseLetter% %netPath%
)
)
)

IF NOT "%netUse%" == "" ( //Double checking if Variable is set
IF %netUse% EQU 1 (
IF %netUsePW% EQU 1 (
No point in testing "netuse" then complaining that "netusePW" is not set.

Related

Validating Input in Batch File XY######## - Letters and Numbers Combination

I've created a batch/cmd file which executes correctly, but I would like to add a "validation" layer to check for correct input.
The input should be in the format of LETTER-LETTER-######## (Eight Numbers)
I'm more of a Bash person myself so I'm a little lost.
Here is a basic version of what I am using.
echo Please Input like so XY########
set /P INPUT=Type input: %=%
Ok. Here we go...
There is no way to do what you want if you read the input via set /P command. In this case you may test the input afterwards and repeat it until be correct...
To check the input at same time it is being typed you need to read and test each character individually. There are several ways to do that. The simplest one is based on choice command:
#echo off
setlocal EnableDelayedExpansion
echo Please Input like so XY########
set /P "=Type input: " < NUL
set "INPUT="
rem Get two *UPPERCASE* letters
set "letter= ABCDEFGHIJKLMNOPQRSTUVWXYZ"
for /L %%i in (1,1,2) do (
choice /C %letter% /N > NUL
for %%l in ("!errorlevel!") do set "INPUT=!INPUT!!letter:~%%~l,1!"
set /P "=!INPUT:~-1!" < NUL
)
rem Get eight digits
set "digit= 0123456789"
for /L %%i in (1,1,8) do (
choice /C %digit% /N > NUL
for %%l in ("!errorlevel!") do set "INPUT=!INPUT!!digit:~%%~l,1!"
set /P "=!INPUT:~-1!" < NUL
)
echo/
echo INPUT = "%INPUT%"
In this code:
Any letter pressed is converted to uppercase. This behavior may be cancelled including both upcase and lowcase letters in letter variable and adding /CS switch to choice command.
The last character input can not be deleted.
The input is automatically completed after the last character is input. No final ENTER key is needed.
If you want not this behavior and need more precise control on the input characters, then you must use another method; for example, reading the keys via xcopy trick. The link posted by Squashman above is an ample example on how to do that...
Aacini's answer also forces valid user input vs validating retroactilvey, his answer is less code and more "readable" IMO. I will be using his answer, but I figured I'd share this method.
I asked to "retroactively" validate user input, but #Squashmans commented URL forces valid user input in the first place.
https://www.dostips.com/forum/viewtopic.php?t=5775
Here is the "sanitized code" that I ended up with.
#echo off
#cls
::::START -- Section Blocks User from Inputting Invalid Data::::
setlocal
set "thisFile=%~F0"
call :ReadFormattedLine INPUT="__########" /M "Enter Input in Form XY########: "
echo/
:ReadFormattedLine var="mask" [/M "message"] [/P] [/F /W /A]
if "%~2" equ "" echo ERROR: Missing parameters & exit /B 1
setlocal EnableDelayedExpansion
set "var=%~1"
set "mask=%~2"
shift & shift
set "message="
if /I "%1" equ "/M" set "message=%~2" & shift & shift
set "password="
if /I "%1" equ "/P" set "password=1" & shift
set "switch=%~1"
set quote="
set "digit= 0 1 2 3 4 5 6 7 8 9 "
set "letter= A B C D E F G H I J K L M N O P Q R S T U V W X Y Z "
set "alphaNum=%digit%%letter%"
set "fmt=#_+?#"
set "show=$/\()[]:;,.- %digit: =%%letter: =%"
for /F %%a in ('copy /Z "%thisFile%" NUL') do set "CR=%%a"
for /F %%a in ('echo prompt $H ^| cmd') do set "BS=%%a" & set "SP=.%%a "
< NUL (
set /P "=%message%"
for /F "eol=| delims=" %%a in ('cmd /U /C echo !mask!^| find /V ""') do (
if "!fmt:%%a=!" neq "%fmt%" (
set /P "=Û"
) else if "%%a" neq " " (
set /P "=%%a"
) else (
set /P "=!SP!"
)
)
set /P "=!SP!!CR!%message%"
)
set "input="
set /A i=0, key=0
goto checkFormat
:nextKey
set "key="
for /F "delims=" %%a in ('xcopy /W "%thisFile%" "%thisFile%" 2^>NUL') do if not defined key set "key=%%a"
if "!key:~-1!" neq "!CR!" goto endif
if /I "%switch%" equ "/A" goto nextKey
if /I "%switch%" neq "/F" goto check/W
:nextField
set "format=!mask:~%i%,1!"
if "%format%" equ "" goto endRead
if "!fmt:%format%=!" equ "%fmt%" goto checkFormat
set /P "=Û" < NUL
set "input=%input% "
set /A i+=1
goto nextField
:check/W
if /I "%switch%" neq "/W" goto checkEmpty
if %i% equ 0 goto endRead
if "%format%" equ "" goto endRead
goto nextKey
:checkEmpty
if %i% gtr 0 goto endRead
goto nextKey
:endif
set "key=!key:~-1!"
if "!key!" equ "!BS!" (
if %i% gtr 0 (
if "%format%" equ "" (
set /P "=!SP!!BS!!BS!Û!BS!" < NUL
) else (
set /P "=Û!BS!!BS!Û!BS!" < NUL
)
set "input=%input:~0,-1%"
set /A i-=1
if !i! equ 0 set key=0
)
goto checkFormat
)
if "%format%" equ "" goto nextKey
if "!key!" equ "=" goto nextKey
if "!key!" equ "!quote!" goto nextKey
if "%format%" equ "#" ( rem Any digit
if "!digit: %key% =!" equ "%digit%" goto nextKey
) else if "%format%" equ "_" ( rem Any letter
if "!letter: %key% =!" equ "%letter%" goto nextKey
) else if "%format%" equ "+" ( rem Any letter, convert it to uppercase
if "!letter: %key% =!" equ "%letter%" goto nextKey
for %%a in (%letter%) do if /I "!key!" equ "%%a" set "key=%%a"
) else (
rem Rest of formats are alphanumeric: ? #
if "!alphaNum: %key% =!" equ "%alphaNum%" goto nextKey
if "%format%" equ "#" ( rem Convert letters to uppercase
for %%a in (%letter%) do if /I "!key!" equ "%%a" set "key=%%a"
) else if "%format%" neq "?" echo ERROR: Invalid format in mask: "%format%" & exit /B 2
)
)
if defined password (
set /P "=*" < NUL
) else (
set /P "=%key%" < NUL
)
set "input=%input%%key%"
:nextFormat
set /A i+=1
:checkFormat
set "format=!mask:~%i%,1!"
if "%format%" equ "" (
if /I "%switch%" equ "/A" goto endRead
if /I "%switch%" equ "/M" goto endRead
goto nextKey
)
if "!show:%format%=!" neq "%show%" (
if "!key!" equ "!BS!" (
if "%format%" neq " " (
set /P "=%format%!BS!!BS!Û!BS!" < NUL
) else (
set /P "=!SP!!BS!!BS!Û!BS!" < NUL
)
set "input=%input:~0,-1%"
set /A i-=1
if !i! equ 0 set key=0
goto checkFormat
) else (
if "%format%" neq " " (
set /P "=%format%" < NUL
) else (
set /P "=!SP!" < NUL
)
set "input=%input%%format%"
goto nextFormat
)
)
if "%input:~-1%!key!" equ " !BS!" (
set /P "=Û!BS!!BS!" < NUL
set "input=%input:~0,-1%"
set /A i-=1
goto checkFormat
)
goto nextKey
:endRead
echo/
endlocal & set "%var%=%input%"
echo %INPUT%
pause
exit /B

How do i check if input is any integer?

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%

Windows batch if-else not working

[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.

Batch script not running

can you help me please?
This piece of script is not running and i can't figure why.
#echo off
ver | findstr /i "5\.1\."
if %ERRORLEVEL% EQU 0 (
set os_ver="xp"
)
ver | findstr /i "6\.1\." > nul
if %ERRORLEVEL% EQU 0 (
set os_ver="7"
)
if %os_ver% == "xp" (
set os_bits="32"
)
if %os_ver% == "7" (
if %PROCESSOR_ARCHITECTURE% == "x86" (
set os_bits="32"
) else (
set os_bits="64"
)
echo %os_bits%
)
pause
it doesn't echo anything despite "ECHO IS DEACTIVATED" or "ECHO IS ACTIVATED"
update:
I posted the entire code, beacause people are saying that it is working
Update2:
I'm on a Windows 7 64 bits
The batch script appears to work fine. It will not echo if your machine is not Windows 7 though.
Try the following:
#echo off
set os_ver="unknown!"
set os_bits="unknown!"
ver | findstr "5\.1" > nul
if %ERRORLEVEL% EQU 0 (
set os_ver="xp"
)
ver | findstr "6\.1" > nul
if %ERRORLEVEL% EQU 0 (
set os_ver="7"
)
if %os_ver% == "xp" (
set os_bits="32"
)
if %os_ver% == "7" (
if %PROCESSOR_ARCHITECTURE% == "x86" (
set os_bits="32"
) else (
set os_bits="64"
)
)
echo os_ver = %os_ver%
echo os_bits = %os_bits%
pause
Update: commenter eryksun has provided the correct reason why this code works while the ops does not even though he is on Windows7. (Good catch)
In the OP's version echo %os_bits% is executed within the same command that sets os_bits. At the time the command is parsed and dispatched os_bits doesn't exist (unless it was already set), so it's just executing echo, which prints whether echo is on or off. – eryksun
Try this:
#echo off
setlocal
if "%PROCESSOR_ARCHITECTURE%" equ "x86" (
set os_bits="32"
) else (
set os_bits="64"
)
echo %os_bits%

Best way to check if directory is writable in BAT script?

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

Resources