TurdPile
TurdPile

Reputation: 1036

return variable from subroutine, not working... why?

In order to store multiple lines inside a variable from a text file, I am using the answer from this question: https://stackoverflow.com/a/10624240/1513458

Using this script inline and parsing large files gave me a maximum setlocal error. So to counter this, I put the script in a subroutine, and called it from the line(s) i needed it in.

An echo at the bottom of the subroutine before it terminates would output the expected result fine... but back at the top where it was originally called, the variable is empty. Why is that?

Here's a shortened version of the script:

setlocal EnableDelayedExpansion
set sourcefile=cc1
call :readfile %sourcefile%
echo !insert!
goto :EOF

:readfile
SETLOCAL DisableDelayedExpansion
set "insert="
FOR /F "usebackq delims=" %%a in (`"findstr /n ^^ %SOURCELOC%\%1.txt"`) do (
    set "line=%%a"
    SETLOCAL EnableDelayedExpansion
    set "line=!line:#=#S!"
    set "line=!line:*:=!"
    for /F "delims=" %%p in ("!insert!#L!line!") do (
        ENDLOCAL
        set "insert=%%p"
    )
)
SETLOCAL EnableDelayedExpansion
if defined insert (
set "insert=!insert:~2!"
set ^"insert=!insert:#L=^

!"
set "insert=!insert:#S=#!"
)
rem A echo !insert! here would output the expected result
goto :EOF

Thanks so much for all your help. Batch is clearly not meant for me, but i need to tough this one through.

Upvotes: 1

Views: 395

Answers (2)

dbenham
dbenham

Reputation: 130819

Your :readfile routine has SETLOCAL at the top that localizes environment changes. When a routine ends, there is an implicit ENDLOCAL executed for every active SETLOCAL that was instantiated within the routine. So the environment is restored to the state that existed prior to the call.

There is no need for a called subroutine. Your earlier code must have had SETLOCAL without an ENDLOCAL within the FOR loop. That would cause the max SETLOCAL error. Now your FOR loop has paired SETLOCAL/ENDLOCAL, so no more problem.

It is possible to transport any value accross the ENDLOCAL barrier, but it uses an advanced technique.

Some simple restructuring avoids the need to do that. But be careful - no batch variable can contain more than 8192 characters.

setlocal disableDelayedExpansion
set sourcefile=cc1

set "insert="
for /f "delims=" %%a in ('findstr /n "^" "%SOURCELOC%\%~1.txt"') do (
  set "line=%%a"
  setlocal EnableDelayedExpansion
  set "line=!line:#=#S!"
  set "line=!line:*:=!"
  for /f "delims=" %%p in ("!insert!#L!line!") do (
    endlocal
    set "insert=%%p"
  )
)
setlocal EnableDelayedExpansion
if defined insert (
set "insert=!insert:~2!"
set ^"insert=!insert:#L=^

!"
set "insert=!insert:#S=#!"
)
echo !insert!


EDIT

Here is a solution that demonstrates returning any value across the ENDLOCAL border (except it does not support carriage return. There is a simple extension that supports carriage return). The routine will give the correct result, regardless whether it was called with delayed expansion enabled or disabled. The technique was developed by DosTips user jeb here:http://www.dostips.com/forum/viewtopic.php?f=3&t=1839, and here: http://www.dostips.com/forum/viewtopic.php?p=6930#p6930. There you will find some explanation as to how it works, but it is not for the faint of heart.

@echo off
setlocal enableDelayedExpansion
set sourceloc=.
set sourcefile=test
set ^"LF=^

^"
call :readFile "%sourcefile%"
echo(!insert!
exit /b

:readFile
setlocal
set "notDelayed=!"

setlocal disableDelayedExpansion
set "insert="
for /f "delims=" %%a in ('findstr /n "^" "%SOURCELOC%\%~1.txt"') do (
  set "line=%%a"
  setlocal EnableDelayedExpansion
  set "line=!line:%%=%%J!"
  set "line=!line:*:=!"
  for /f "delims=" %%p in ("!insert!%%~L!line!") do (
    endlocal
    set "insert=%%p"
  )
)
setlocal enableDelayedExpansion
if defined insert (
  set "insert=!insert:"=%%~K!"
  if not defined notDelayed set "insert=!insert:^=^^^^!"
)

if defined insert if not defined notDelayed set "insert=%insert:!=^^^!%" Do not remove!
set "replace=%% """"
for %%L in ("!LF!") do for /f "tokens=1,2" %%J in ("!replace!") do (
  endlocal
  endlocal
  endlocal
  set "insert=%insert%" Do not remove!
)
exit /b

Upvotes: 3

Endoro
Endoro

Reputation: 37569

try this:

setlocal EnableDelayedExpansion
set sourcefile=cc1
call :readfile insert
echo %insert%
goto :EOF

:readfile
SETLOCAL DisableDelayedExpansion
set "insert="
FOR /F "delims=" %%a in ('findstr /n "^" "%SOURCELOC%\%1.txt"') do (
     set "line=%%a"
     SETLOCAL EnableDelayedExpansion
     set "line=!line:#=#S!"
     set "line=!line:*:=!"
     for /F "delims=" %%p in ("!insert!#L!line!") do (
          ENDLOCAL
          set "insert=%%p"
     )
)
SETLOCAL EnableDelayedExpansion
if defined insert (
set "insert=!insert:~2!"
set ^"insert=!insert:#L=^

!"
set "insert=!insert:#S=#!"
)
rem A echo !insert! here would output the expected result
SET "%1=!insert!"
ENDLOCAL 
goto :EOF

Upvotes: 0

Related Questions