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

3

23

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?

Lecher answered 10/7, 2012 at 17:52 Comment(2)
I want to return %NAME%. I was experimenting with %1 and forgot to change it back. I have changed the code back to the original now, still doesn't work.Lecher
Your error is in the line "echo/Date is: %MYD%" as you are in a for cicle you have to use !MYD! instead of %MYD%Leatherjacket
F
25

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.

Florindaflorine answered 10/7, 2012 at 18:16 Comment(1)
I suppose (almost) the only time you really want delayed expansion is if you're using endlocal at the end of a function scope and need to inline the "returning" of a value.Pushover
O
32

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
Overspend answered 8/6, 2015 at 14:32 Comment(3)
I'm not sure why, but Simon Sheppard use it as Set "%~1=filled". Anyway, this is the actual answer to the question asked in the title.Rale
To get promised output the 2nd code line should be set myvar=empty. And it is good practice for having multiple function in a batch file to finish every function with exit /b 0, not with goto.Nimitz
The problem with this approach is that ever environment variable created into :myFunction will be public. A function must be a black box, the internal variables must be internal. This is made with SetLocal/EndLocal commands.Leatherjacket
F
25

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.

Florindaflorine answered 10/7, 2012 at 18:16 Comment(1)
I suppose (almost) the only time you really want delayed expansion is if you're using endlocal at the end of a function scope and need to inline the "returning" of a value.Pushover
S
0

Would you wonderful folks validate this answer to the question? Obviously I'm very new, but I need to solve this problem too:

@echo off

REM define and print debug output for a variable
set a_variable_populated_by_a_function=not useful
@echo on
echo %a_variable_populated_by_a_function% is not useful :(
@echo off

REM call the function, pass in our variable
call :a_function a_variable_populated_by_a_function

REM pointer to where function is complete
:a_function_complete
goto :resume_code

REM function definition
SetLocal
    :a_function
        set %1=so very useful!
        goto :a_function_complete
EndLocal


REM pointer to resume life after function stuff
:resume_code

REM print debug output for variable altered by function
@echo on
echo %a_variable_populated_by_a_function% is so useful :O
@echo off

my output is

not useful is not useful :(
so very useful! is so useful :O

Would the best solution to the question "How do I return a value from a function in a batch file?" require three separate labels / pointers?

:a_function
:a_function_complete
:resume_code

Or maybe these scripts are so messy that "whatever works, works."

Shannonshanny answered 5/8 at 10:55 Comment(1)
Note: your setlocal is never executed (it's before the label), and it isn't needed at all Same with the endlocal- it's after the goto and therefore never executed. And you also don't need it. Best method to "return from a subroutine" is a simple goto :eof (the script knows where to return to). No need to do several jumps within the script. By the way - this isn't an answer to the question and therefore is bound to be downvoted and eventually deleted. If you have a question, please don't answer, but ask a new question.Insignia

© 2022 - 2024 — McMap. All rights reserved.