#1 14 Apr 2008 23:31

budhax
Member
Registered: 04 Dec 2007
Posts: 45

1.Can a subroutine return a value? Yes. 2.Left/Right cut a string.

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

#2 22 Jul 2011 00:08

Tall_Bear
Member
Registered: 06 Jul 2010
Posts: 17

Re: 1.Can a subroutine return a value? Yes. 2.Left/Right cut a string.

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

#3 22 Jul 2011 02:54

RG
Member
From: Minnesota
Registered: 18 Feb 2010
Posts: 348

Re: 1.Can a subroutine return a value? Yes. 2.Left/Right cut a string.

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

#4 22 Jul 2011 12:40

jeb
Member
From: Germany
Registered: 19 Nov 2010
Posts: 107

Re: 1.Can a subroutine return a value? Yes. 2.Left/Right cut a string.

RG wrote:

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

#5 25 Jul 2011 20:53

Tall_Bear
Member
Registered: 06 Jul 2010
Posts: 17

Re: 1.Can a subroutine return a value? Yes. 2.Left/Right cut a string.

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

#6 25 Jul 2011 21:03

Tall_Bear
Member
Registered: 06 Jul 2010
Posts: 17

Re: 1.Can a subroutine return a value? Yes. 2.Left/Right cut a string.

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

#7 25 Jul 2011 22:29

Simon Sheppard
Super Administrator
Registered: 27 Aug 2005
Posts: 1,035
Website

Re: 1.Can a subroutine return a value? Yes. 2.Left/Right cut a string.

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

#8 25 Jul 2011 23:14

Tall_Bear
Member
Registered: 06 Jul 2010
Posts: 17

Re: 1.Can a subroutine return a value? Yes. 2.Left/Right cut a string.

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

#9 26 Jul 2011 00:14

Simon Sheppard
Super Administrator
Registered: 27 Aug 2005
Posts: 1,035
Website

Re: 1.Can a subroutine return a value? Yes. 2.Left/Right cut a string.

Tall_Bear wrote:

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

#10 26 Jul 2011 03:37

RG
Member
From: Minnesota
Registered: 18 Feb 2010
Posts: 348

Re: 1.Can a subroutine return a value? Yes. 2.Left/Right cut a string.

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

#11 26 Jul 2011 07:53

jeb
Member
From: Germany
Registered: 19 Nov 2010
Posts: 107

Re: 1.Can a subroutine return a value? Yes. 2.Left/Right cut a string.

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

#12 26 Jul 2011 18:12

Tall_Bear
Member
Registered: 06 Jul 2010
Posts: 17

Re: 1.Can a subroutine return a value? Yes. 2.Left/Right cut a string.

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

Board footer

Powered by FluxBB