You are not logged in.
Hello,
Can a DOS subroutine return a value? Yes, sending to the subroutine the variable name to catch the returned value.
See those 2 examples.
Subroutine LeftCut returns the a string cut till the first left found character.
Subroutine RightCut returns the a string cut till the first right found character.
NOTES:
1. Remove :: in "::ECHO.!P2!" to see the subroutine process/steps.
2. Remove /i in "IF /i NOT" to get subroutines case sensitive.
3. This "SET !P1!=!P2!" uses the interesting DOS ability that you can set a variable using an expression for its name.
So, in the LeftCut subroutine:
SET !P1!=!P2!
is equivalent to
SET ReturnedVar1=!P2!
4. This is a BELL char before "*** No".
Any comment?
Thanks.
@ECHO OFF
@SETLOCAL ENABLEDELAYEDEXPANSION
::-------------------------------------------------------------- LEFT CUT STRING
Call:LeftCut "ReturnedVar1" "192.168.100.11\INETPUB\Web PROJECTS" "\"
ECHO.%ReturnedVar1%
ECHO.
ECHO.
::------------------------------------------------------------- RIGHT CUT STRING
Call:RightCut "ReturnedVar2" "192.168.100.11\INETPUB\Web PROJECTS" "\"
ECHO.%ReturnedVar2%
ECHO.
ECHO.
::------------------------------------------------------------- RIGHT CUT STRING
Call:RightCut "ReturnedVar3" "192.168.100.11\INETPUB\Web PROJECTS" "x"
ECHO.%ReturnedVar3%
Pause&EXIT
:LeftCut
SET P1=%~1&::Returned Variable Name
SET P2=%~2&::String To Cut
SET P3=%~3&::Character To Find
:ContinueLCut
SET P2=!P2:~1!
::ECHO.!P2!
IF /i "!P2!"=="" (COLOR 0C&ECHO.*** No [!P3!] In The STRING&GOTO:EOF)
IF /i NOT "!P2:~,1!"=="!P3!" (GOTO:ContinueLCut) ELSE (SET !P1!=!P2!&GOTO:EOF)
:RightCut
SET P1=%~1&::Returned Variable Name
SET P2=%~2&::String To Cut
SET P3=%~3&::Character To Find
:ContinueRCut
SET P2=!P2:~,-1!
::ECHO.!P2!
IF /i "!P2!"=="" (COLOR 0C&ECHO.*** No [!P3!] In The STRING&GOTO:EOF)
IF /i NOT "!P2:~-1!"=="!P3!" (GOTO:ContinueRCut) ELSE (SET !P1!=!P2!&GOTO:EOF)
Last edited by budhax (14 Apr 2008 23:33)
Offline
Hello,
Referencing this code I am unable to retrieve the returned value. I appreciate any help with my following code:
setlocal ENABLEDELAYEDEXPANSION
@echo on
CLS
:: <><><><><><><><><><><><><><><><><><><><>
:: Set BAT Input Parameters from ECM job.
:: <><><><><><><><><><><><><><><><><><><><>
set "DB_ENV=%~1"
:: <><><><><><><><><><><><><><><>
:: Set Internal Working Fields.
:: <><><><><><><><><><><><><><><>
set bat_parm_err=N
:: <><><><><><><><><><><><><><><><><><><><><><><><>
:: Edit BAT Input Parameter DB_ENV from ECM job.
:: <><><><><><><><><><><><><><><><><><><><><><><><>
if defined DB_ENV (
set varlen=7
call :VAR_LENGTH "returnvarlength" %DB_ENV%
echo.returnvarlength after %returnvarlength%
:: Result is - returnvarlength after
) else (
set bat_parm_err=Y
@echo ERROR: ECM job var name DB_ENV on the Set tab is empty. Set DB_ENV to a BFS database environment.
)
if bat_parm_err == Y (GOTO :END)
GOTO :END
:: <><><><><><><><>
:: SubRoutines
:: <><><><><><><><>
:: <><><><><><><>
:: VAR_LENGTH
:: <><><><><><><>
:VAR_LENGTH
set P1=%~1
set Var=%~2
:: Set the variable %varlength% to the length of the variable %Var%
set #=%Var%
set varlength=0
:loop
if defined # (set #=%#:~1%&set /A varlength += 1&goto loop)
@echo Var %Var% is %varlength% characters long!
:: Result is - Var BFSDV90 is 7 characters long
set !P1!=%varlength%
:: Result is - set !P1!=7
set "%~1=%varlength%"
:: Result is - set "returnvarlength=7"
GOTO :EOF
:: <><><><>
:: END
:: <><><><>
:END
Offline
setlocal ENABLEDELAYEDEXPANSION is localizing your variables. You need to use a technique called 'tunneling'.
Add the following line after :END
ENDLOCAL & set returnvarlength=%returnvarlength%
ENDLOCAL and set must be on the same line so that the localized value of returnvarlength is obtained before localization ends and is set in the other locale
Windows Shell Scripting and InstallShield
Offline
ENDLOCAL and set must be on the same line so that the localized value of returnvarlength is obtained before localization ends and is set in the other locale
Or they should be in a parenthesis block.
:myFunc
setlocal
rem ... Do something
set length=17
(
Endlocal
set "result=%length%"
goto :eof
)
But this simple "tunneling"-technic fails with a bit more complex strings results like
:myFunc
setlocal
set text="One & two" now without quotes three ^& four
(
Endlocal
set "result=%text%"
goto :eof
)
jeb
Offline
Thanks RG and Jeb. Seems the above code from budhax is missing ENDLOCAL, at least I do not see any.
I added the following after the END:
ENDLOCAL & set returnvarlength=%returnvarlength%
@echo. ENDLOCAL returnvarlength after %returnvarlength%
The echo shows returnvarlength is 7 however
echo. returnvarlength after %returnvarlength%
still results in returnvarlength after
The echo. returnvarlength after %returnvarlength% statement gets executed before the END statement so how does placing ENDLOCAL & set returnvarlength=%returnvarlength% after the END statement effect statement already executed?
Offline
Once I get this working I will be replacing the @echo. ENDLOCAL returnvarlength after %returnvarlength% statement with the following:
if %returnvarlength% NEQ %varlen% (
set bat_parm_err=Y
@echo ERROR: ECM job var name DB_ENV on the Set tab has a length (%returnvarlength%) not equal to %varlen%. Set DB_ENV to a BFS database environment.
) else (
set env_db=%DB_ENV:~3,4%
)
Offline
This page explains the tunneling technique a bit more:
http://ss64.com/nt/syntax-functions.html
btw I think the first ever mention of the 'variable tunneling' technique is in Tim Hill's book from 1998, 'Windows NT shell scripting' (page 79)
Offline
Hi Simon,
Yes I saw that web page, which has the following code:
@ECHO OFF
SET _var1=64
SET _var2=123
CALL :myfunct3 Testing
echo %_var1%
echo %_result%
goto :eof
:myfunct3
SETLOCAL
SET _var1=%1
SET _var2="%_var1%--%_var1%--%_var1%"
ENDLOCAL & SET _result=%_var2%
The web page does not explain what "Testing" is in the statement CALL :myfunct3 Testing. Is that a parameter to myfunct3, which is picked up by SET _var1=%1, or is that a returning parameter and the main code does not show use of it?
Offline
Is that a parameter to myfunct3, which is picked up by SET _var1=%1,
Yes,
so the last line
echo %_result%
should give you
Testing--Testing--Testing
The main point of that example is that the variables _var1 and _var2 refer to completely different values inside and outside the subroutine.
Offline
Hopefully the link Simon provided makes it more clear.
The echo. returnvarlength after %returnvarlength% statement gets executed before the END statement so how does placing ENDLOCAL & set returnvarlength=%returnvarlength% after the END statement effect statement already executed?
It may also help to keep in mind that the entire line is EVALUATED or EXPANDED before it is executed. So in the example above where
ENDLOCAL & set returnvarlength=%returnvarlength%
%returnvarlength% is evaluated before the ENDLOCAL takes effect, but the actual SET occurs AFTER the ENDLOCAL takes effect.
Windows Shell Scripting and InstallShield
Offline
And as this technic can only proivde simply strings, as the percent expansion can produce problems.
You need a more powerful technic, if you try to return more complex data.
One trick is to use "FOR" to tunneling the values behind the ENDLOCAL barrier,
this works even if quotes and special characters are in the string.
:myFunc
Setlocal EnableDelayedExpansion
set localVar=123
rem ...
FOR /F "delims=" %%A in (!localVar!) DO (
Endlocal
set "result=%%A"
)
A "perfect" solution is discussed at http://www.dostips.com/forum/viewtopic.php?p=6930#p6930
The resulting code is a bit longer for a simple return from funcion ...
:lfTest
setlocal
set "NotDelayedFlag=!"
echo(
if defined NotDelayedFlag (echo lfTest was called with Delayed Expansion DISABLED) else echo lfTest was called with Delayed Expansion ENABLED
setlocal EnableDelayedExpansion
set "var=!%~2!"
rem echo the input is:
rem echo !var!
echo(
rem ** Prepare for return
set "var=!var:%%=%%~A!"
set "var=!var:"=%%~B!"
for %%a in ("!LF!") do set "var=!var:%%~a=%%~L!"
for %%a in ("!CR!") do set "var=!var:%%~a=%%~C!"
rem ** It is neccessary to use two IF's else the %var% expansion doesn't work as expected
if not defined NotDelayedFlag set "var=!var:^=^^^^!"
if not defined NotDelayedFlag set "var=%var:!=^^^!%" !
set "replace=%% """ !CR!!CR!"
for %%L in ("!LF!") do (
for /F "tokens=1,2,3" %%A in ("!replace!") DO (
ENDLOCAL
ENDLOCAL
set "%~1=%var%" !
@echo off
goto :eof
)
)
jeb
Offline
Hi Simon, RG, and Jeb,
Thank you for the replies and info. I was able to get my above code to work by:
1. Removing setlocal ENABLEDELAYEDEXPANSION
2. Removing "returnvarlength" from statement call :VAR_LENGTH "returnvarlength" %DB_ENV%
3. Replacing statements set !P1!=%varlength% and set "%~1=%varlength%" with ENDLOCAL & set returnvarlength=%varlength%
I placed this ENDLOCAL statement within the function VAR_LENGTH right before the GOTO :EOF. This worked returning returnvarlength to the main code. I then tried placing this ENDLOCAl after the END as suggested above and that did not work; resulted in returnvarlength after. Which seems to be right because once the END statement is reached the main code ends, which is too late for having & set returnvarlength=%varlength% to execute.
When time permits I need to try the budhax code above because that code does have an ENDLOCAL.
Offline