Batch: Unexpected Variable Results in Subroutine

An answer to a previous question suggests this FOR line. Now I am trying to use the variable. My batch script at present:

@echo off
cls
for %%G in ("3D Objects","Documents","Downloads","Music","Pictures","Videos") do call :sub1 %%G
:sub1
echo %%G
set apath=%%~G
echo %apath%
goto :eof
:End

The output from this script is seven iterations of this:

%G
%~G

The subroutine seems to be doing something to the variable. Because when I substitute these lines:

for %%G in ("3D Objects","Documents","Downloads","Music","Pictures","Videos") do (
echo %%G
set apath=%%~G
echo %apath%
)

I get the desired result: six pairs of output of this nature:

"3D Objects"
3D Objects

where the tilde removes the quotation marks, as expected.

In the first code sample, the production of seven (not six) pairs of output lines suggests that the subroutine may be disregarding the quotation marks, rendering "3D" and "Objects" as two separate items -- which was an issue addressed in the previous question.

I starting tinkering with the subroutine approach because I was having problems achieving a certain outcome using the simpler approach presented in the second code sample (above). Possibly delayed expansion would fix that, but at the cost of additional complexity. Not to deny that the subroutine's "goto :eof" line is also a little confusing, even if it does work.

My specific question is, what's going wrong with the variable, when used in the subroutine? But if there is a better way, I would also appreciate at least a link in that direction.


Solution 1:

You are passing the value of the for loop iteration to a subroutine using call so you want to enclose that (passed variable placeholder) with a double quote and then reference the passed in variable within that subroutine as %1 or %~1 to strip out the enclosing double quotes.

Using the GOTO :EOF at the end of each called subroutine will pass control back to the original caller subroutine so it can continue to process the rest its logic if applicable after the call command.

@echo off
cls
for %%G in ("3D Objects","Documents","Downloads","Music","Pictures","Videos") do call :sub1 "%%~G"
goto :eof

:sub1
echo %1
set apath=%~1
echo %apath%
goto :eof
:End

Output

"3D Objects"
3D Objects
"Documents"
Documents
"Downloads"
Downloads
"Music"
Music
"Pictures"
Pictures
"Videos"
Videos

Supporting Resources

  • FOR
  • Command Line arguments (Parameters)

    Arguments can also be passed to a subroutine with CALL:

    CALL :my_sub 2468

    You can get the value of any argument using a % followed by it's numerical position on the command line. The first item passed is always %1 the second item is always %2 and so on

    %* in a batch script refers to all the arguments (e.g. %1 %2 %3 %4 %5 ...%255) only arguments %1 to %9 can be referenced by number.

  • CALL

    Passing by Reference

    In addition to passing numeric or string values on the command line, it is also possible to pass a variable name and then use the variable to transfer data between scripts or subroutines. Passing by reference is a slightly more advanced technique but can be particularly useful when the string contains characters that are CMD delimiters or quotes.


    CALL a subroutine (:label)

    The CALL command will pass control to the statement after the label specified along with any specified parameters. To exit the subroutine specify GOTO :eof this will transfer control to the end of the current subroutine.

  • CALL /?

    CALL command now accepts labels as the target of the CALL.  The syntax
    is:
    
        CALL :label arguments
    
    
    In addition, expansion of batch script argument references (%0, %1,
    etc.) have been changed as follows:
    
    
        %* in a batch script refers to all the arguments (e.g. %1 %2 %3
            %4 %5 ...)
    
        Substitution of batch parameters (%n) has been enhanced.  You can
        now use the following optional syntax:
    
            %~1         - expands %1 removing any surrounding quotes (")