Print variable in batch file - windows

I am trying to print a variable in parenthesised code which is assigned a value using other variable in batch file.
Here is my code
#echo off
SETLOCAL enableDelayedExpansion
CALL initialize
CALL fun
:fun (
#echo off
Setlocal EnableDelayedExpansion
Set "SOMEVAR=!OTHERVAR!"
ECHO ..%SOMEVAR%
EXIT /B 0
)
:initialize (
set SOMEVAR=somevalue
EXIT /B 0
)
The output is just
..
How do i fix it so that i can assign value to somevar?
Edit1: If i now try to print in following way it does its job
ECHO ..!SOMEVAR!
But my script uses lot of %SOMEVAR%. Does that mean i need to change them all?
Note: Othervar is initialzed in other function and it does show proper value if it is echoed.

Since the code portion containing echo %SOMEVAR% is in between parenthesis, the variable is expanded before being set (consult this post for a good explanation).
There are the following options to avoid that:
to expand it like !SOMEVAR! (delayed expansion), or
to avoid the parenthesis:
#echo off
SETLOCAL EnableDelayedExpansion
CALL initialize
CALL fun
exit /B
:fun
Setlocal EnableDelayedExpansion
Set "SOMEVAR=!OTHERVAR!"
ECHO ..%SOMEVAR%
EXIT /B 0
:initialize
set SOMEVAR=somevalue
EXIT /B 0
Note the additional exit /B in the above code snippet after the call statements, which prevents from falling into the code below unintentionally.

Does this work any closer to your expectations? Note that SOMEVAR will not be returned to your shell environment unless an ENDLOCAL block is used.
C:>set OTHERVAR=0123456789
C:>type g2.bat
#echo off
SETLOCAL enableDelayedExpansion
CALL:initialize
CALL:fun
EXIT /B 0
:fun (
rem #echo off
Setlocal EnableDelayedExpansion
Set "SOMEVAR=!OTHERVAR!"
ECHO ..%SOMEVAR%
GOTO :EOF
)
:initialize (
set SOMEVAR=somevalue
GOTO :EOF
)
C:>g2.bat
..0123456789

Related

Return a value from a called batch file label

I have two labels in my batch file. The initial label MAIN shall stay in control, so it Calls the second label, which ends with exit /b.
My script's Main label Calls the other, passing it arguments, which will be used to search strings wothin a text file.
When returning to the Calling label, it slways receives an empty return string.
I think this has something to do with the variable expansion in a loop. Who knows?
Here is the Script:
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
:MAIN
call :getReturnValue "1234 0815 4321 12815" "readBackVal"
if !errorlevel! equ 0 (
echo readback=!readBackVal!
echo readback=%readBackVal%
)
pause
exit /b 0
REM Function, which checks if the give return value is in a specific textfile (line for line check)
:getReturnValue
set "myExpectedValueList=%~1"
set "retval=%~2"
set "file=textexample.txt"
for %%i in (%myExpectedValueList%) do (
for /f "tokens=*" %%a in (%file%) do (
echo %%a|findstr /r "^.*%%i$"
)
if !errorlevel! equ 0 (
(endlocal
set /a "%retval%=%%i")
)
exit /b 0
)
)
exit /b 1
Here is the sample textfile textexample.txt:
Setup returns with errorcode=0815
Here is the answer i looked for:
Hi, first i want to inform that i made some changes due to the Answer of
#OJBakker. This changes are listed at the bottom of the script.
The problem was to return a value from a called function/label to the calling function/label. The stich here is, that the magic
is done in the (endlocal...) section of the called function/label -> means the return of the variable.
Before the endlocal command is executed, the compiler replaces the variables in this section by their values and afterwards executes the command´s from left to right. Means following:
First, the compiler sees following:
(endlocal
if "%retval%" neq "" (call set /a %retval%=%%i)
)
Second, the compiler replaces the variables by their values:
(endlocal
if "readBackVal" neq "" (set /a "readBackVal"=1815)
)
Third: This command is executed
(endlocal
if "readBackVal" neq "" (set /a "readBackVal"=1815)
)
Now here is my complete script (i also fixed some other problems with it which i commented at the bottom of the script
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
:MAIN
setlocal
call :getReturnValue "1234 1815 4321 12815" "readBackVal"
if "!errorlevel!" equ "0" (
echo readback=!readBackVal!
)
pause
exit /b 0
REM Function, which checks if the give return value is in a specific textfile (line for line check)
:getReturnValue
setlocal
set "myExpectedValueList=%~1"
set "retval=%~2"
set "file=textexample.txt"
for %%i in (%myExpectedValueList%) do (
for /f "tokens=*" %%a in (%file%) do (
echo %%a|findstr /r "^.*%%i$" >NUL
)
if "!errorlevel!" equ "0" (
(endlocal
if "%retval%" neq "" (set /a %retval%=%%i)
)
exit /b 0
)
)
exit /b 1
REM Changes to initial posting:
REM Added "setlocal" keyword to the function "getReturnValue"
REM Corrected an invalid paranthesis in the (endlocal...) section
REM Changed the file "textexample.txt" -> 0815 to 1815 to remove leading zero (findstr. Problem),
REM Added check, if parameter "retval" has been passed to the called function e.g. is not empty
REM FINAL -> applied double variable expansion (call set /a ...) to return the value proper
REM to the :MAIN function.
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
:MAIN
call :getReturnValue "1234 0815 4321 12815" "readBackVal"
if %errorlevel% equ 0 (echo readback=%readBackVal%)
pause
endlocal
exit /b 0
REM Function, which checks if the give return value is in a specific textfile (line for line check)
:getReturnValue
set "myExpectedValueList=%~1"
set "retval=%~2"
set "file=textexample.txt"
for %%i in (%myExpectedValueList%) do (
for /f "tokens=*" %%a in (%file%) do (
echo %%a| >con 2>&1 findstr /r "^.*%%i$"
if !errorlevel! equ 0 (
set /a "%retval%=%%i"
exit /b 0
)
)
)
exit /b 1
rem changes:
rem endlocal moved to main.
rem check for errorlevel moved to within the commandblock of the inner for-loop.
rem 'exit /b 0' moved to within the if. This exit line stopped the for after the first item.
rem redirection added to findstr command. Now the output shows the remaining problem.
rem Invalid number. Numeric constants are either decimal (17), hexadecimal (0x11), or octal (021).
rem Findstr really does not like the value 0815, especially the starting zero.
rem I am not sure how to change the regexp so findstr won't barf at the leading zero.
rem Maybe someone else can solve this remaining problem.

How can I "break" out of a For loop?

Below, when I execute the goto command, it just hangs and I have to Control-C. I tried EXIT /b too. I'm trying to avoid goto's as much as possible. Is there a way to do what I want?
:SUB_bigRandLooper
set /a lcv=0
FOR /L %%s IN ( 0 , 0 , 1 ) DO (
set big-rand=!random:~-4!
echo big-rand is !big-rand!
set /a lcv=%lcv+1
if !big-rand! GTR 9900 goto bigRandLooperWrapup
)
:bigRandLooperWrapup
echo biggest-rand is %big-rand%
echo lcv is %lcv%
EXIT /B
.
The short answer is: No, you can't.
Since you are using for /L and establish an infinite loop and the fact that the loop is preprocessed and cached before it is executed, it cannot be interrupted by goto; goto breaks the loop context, or more correctly spoken, the (/) block context, so no more commands in the block are executed, but the loop itself is still running.
You can prove this by the following code:
for /L %%I in (1,1,100000) do (
echo %%I
if %%I equ 10 goto :SKIP
)
:SKIP
echo skipped
You will see that echo %%I is only executed for %%I from 1 to 10, but execution does not immediately continue at echo skipped but there is a notable delay, because the loop finishes iterating in the background, although no more commands are executed.
There is a work-around though: you could establish an endless loop with goto, like this:
:SUB_bigRandLooper
set /A lcv=0
:bigRangLooperLoop
set big-rand=!random:~-4!
echo big-rand is !big-rand!
set /A lcv+=1
if !big-rand! gtr 9900 goto :bigRandLooperWrapup
goto :bigRangLooperLoop
:bigRandLooperWrapup
echo biggest-rand is %big-rand%
echo lcv is %lcv%
exit /B
I know the goto loop is slower than the for /L loop, but that is the only way to create a breakable infinite loop.
A faster approach is to nest both loop methods: use for /L to iterate a few thousands times and wrap an infinite goto loop around.
An alternative work-around is to make use of the fact that the exit command can break (infinite) for /L loops. But since this also exits the cmd instance the batch file is running in, the loop needs to be placed into a separate cmd instance. Of course the environment is completely separated from the current one. A solution might look like this:
:SUB_bigRandLooper
#echo off
rem // Check for argument, interpret it as jump label if given:
if not "%~1"=="" goto %~1
rem /* Establish explicit `cmd` instance for executing the `for /L` loop;
rem the `for /F` loop implicitly creates the new `cmd` instance for the command
rem it executes, so we do not have to explicitly call `cmd /C` here; the resulting
rem values are echoed by the sub-routine and captured here by `for /F`: */
for /F "tokens=1,2" %%I in ('"%~f0" :bigRandLooperLoop') do (
rem // Assign captured values to environment variables:
set "big-rand=%%I" & set "lcv=%%J"
)
:bigRandLooperWrapup
echo biggest-rand is %big-rand%
echo lcv is %lcv%
exit /B
:bigRandLooperLoop
setlocal EnableDelayedExpansion
set /A lcv=0
for /L %%s in (0,0,1) do (
set big-rand=!random:~-4!
rem /* Explicitly redirect this output to the console window to prevent it from
rem being captured by `for /F` in the main routine too: */
> con echo big-rand is !big-rand!
set /A lcv+=1
if !big-rand! gtr 9900 (
rem // Output found values in order to be able to capture them by `for /F`:
echo !big-rand! !lcv!
rem // Break loop and leave current `cmd` instance:
exit
)
)
endlocal

The syntax of the command is incorrect. at endlocal & set [batch]

I have a function which returns value by reference in batch.
:errorCheck
setlocal
set "test_command=%~1"
set "err_code=%~2"
FOR /F "delims=" %%a IN ('%test_command% 2^>^&1 1^>NUL') DO (
set err_msg="%%~a"
)
if [%err_msg%] neq [] (
if not x%err_msg:%err_code%=%==x%err_msg% (
set "error=true"
)
)
if [%err_msg%]==[] (
set "error=false"
)
endlocal & set "%3=%error%"
exit /b
The function executes correctly and the return value is also correct but at line endlocal & set "%3=%error%" while executing set "%3=%error%" part it gives me error :
The syntax of the command is incorrect.
i am unable to comprehend why is it happening though the return value is correct.
The problem is not in the return value, but in the substring operation. Your syntax is not allowed. The expression is not evaluated as you think. The start and end of variables are
%err_msg:%err_code%=%
^........^ ^.^
var1 var2
To use a variable in a substring operation in another variable you will need to use delayed expansion. Try with
:errorCheck
setlocal enableextensions disabledelayedexpansion
set "test_command=%~1"
set "err_code=%~2"
set "error=false"
set "err_msg="
FOR /F "delims=" %%a IN ('
%test_command% 2^>^&1 1^>NUL
') DO set "err_msg=%%~a"
if defined err_msg (
setlocal enabledelayedexpansion
if not "!err_msg:%err_code%=!"=="%err_msg%" (
endlocal
set "error=true"
) else ( endlocal )
)
endlocal & set "%3=%error%"
exit /b
Now the variables seen by the parser are
!err_msg:%err_code%=!
^........^
^...................^
But as not all characters are allowed in a substrig operation, depending on the contents of err_code it is possible that it will also fail.
If it can be the case, you can change the substring operation into a piped command searching for the required error code
:errorCheck testCommand errorCode returnVariable
setlocal enableextensions disabledelayedexpansion
( %~1 2>&1 1>nul | find "%~2" > nul ) && ( set "error=true" ) || ( set "error=false" )
endlocal & set "%~3=%error%"
exit /b
That is:
execute the command (%~1) with, as in the original code, the stdout redirected to null and stderr redirected to stdout, so we read the error stream.
The output of the command is filtered with find, searching the error code. If it is found, find will not raise error level, if it is not found, errorlevel will be raised.
Using conditional execution, the error variable is set. If the previous command does not raise errorlevel, the code after the && is executed. If the command raised errorlevel, the code after the || is executed.
Environment space is restored and the return variable asigned

How do I return a value from a function in a batch file?

I have the following batch file
#echo off
setlocal EnableDelayedExpansion
for /f "delims==" %%J in (File_List.txt) do (
call :setDate %%J MYD
echo/Date is: %MYD%
)
endlocal &goto :eof
:setDate
SETLOCAL ENABLEEXTENSIONS
echo %1
echo %~2
set NAME=%1
set NAME=%NAME:~-11%
echo %NAME%
echo %~2
endlocal&set %2=%NAME%&goto :eof
but with File_List.txt containing
file2012-05.csv
I get
file2012-05.csv
MYD
2012-05.csv
MYD
Date is:
How do I actually get the function setDate to return the value I want?
As I don't understand from your script what you want to achieve, I reply (for completeness) to the original subject: returning a value from a function.
Here is how I do it:
#echo off
set myvar=
echo %myvar%
call :myfunction myvar
echo %myvar%
goto :eof
:myfunction
set %1=filled
goto :eof
Result is:
empty
filled
The batch interpreter evaluates %MYD% at parse time, and at that time it's empty. That's why you have Delayed Expansion. Change this line:
echo/Date is: %MYD%
to this:
echo/Date is: !MYD!
and it will work like you want, because then it tells the interpreter to evaluate MYD at run-time.

Errorlevel of command executed by batch for loop

The following code always displays 0 as the errorlevel, but when the copy command is done outside of the for loop command it returns a non zero errorlevel.
for /f "usebackq delims=" %%x in (`copy x y`) do (
set VAR=%%x
)
ECHO Errorlevel = %ERRORLEVEL%
ECHO VAR = %VAR%
Is is possible to get the errorlevel of the copy command executed by the for loop?
it works for me ! You only need to put the error checking within the DO parentheses
with a text file containing the copy commands (7200 lines; for example:
copy 2_97691_Scan.pdf O:\Data\Dev\Mins\PDFScan2\2011\4\2_97691_Scan.pdf),
I can run the following batch file
#echo off
setlocal EnableDelayedExpansion
for /F "delims=" %%I in (CopyCurrentPDFs.txt) do (
%%I
if !errorlevel! NEQ 0 echo %%I>>errorcopy.txt
)
I am assuming that you are copying files from one directory to another? If so, you could do something like this instead:
#echo off
setlocal EnableDelayedExpansion
set ERR=0
for %%x in (x) do (
copy %%x y
set ERR=!errorlevel!
set VAR=%%x
)
ECHO Errorlevel = %ERR%
ECHO VAR = %VAR%
The delayed expansion is required to get the actual value of errorlevel inside the loop instead of the value before the loop is entered.
If that isn't what you are trying to do, please clarify your objective.

Resources