Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to package all my functions in a batch file as a seperate file?

My question is related to this question. I have several bunch of actions that need to be executed from a batch file and I would like to model them as functions and call from a master sequence. From the above question, it is clear that I can do this with the call syntax

call:myDosFunc

My question is that can I place all these functions in a seperate batch file (functions.bat) and somehow 'include' that in the main batch file and call them? Another option would be to utilize the possibility to invoke functions.bat from main.bat with the call syntaxt, but I'm not sure if I can invoke that with a specific function instead of executing the whole batch file.

In short, I'm looking for something similar to the C programming world where my functions reside in a DLL and the main program contains only the high-level logic and calls the functions from the DLL.

like image 369
PermanentGuest Avatar asked Sep 11 '13 13:09

PermanentGuest


2 Answers

I think a routing function in the beginning of a batch file is not that ugly. You can use something like this at the beginning of a "libbatch.cmd"

    call:%*
    exit/b

:func1
    [do something]
    exit/b

:func2
    [do something else]
    exit/b

Now you can call func2 from another batch with:

call libbatch.cmd func2 params1 param2 ... paramN

this also preserves the errorlevel "thrown" by func2 (exit/b hands over the current errorlevel). With the second call instead of a goto you ensure that "%1"=="param1" and not func2. And call will not terminate the batch file if the label does not exist, it simply sets the errorlevel to 1 and puts an error message to 2 (errorout), which could be redirected to nul.

Explanation: %* contains all parameters, so in the example the first line translates to:

call:func2 params1 param2 ... paramN
like image 195
Thomas Avatar answered Oct 11 '22 09:10

Thomas


Here is a simple example of how it might be done.

The function script is called with the name of the function as the first argument, and function arguments as arg2, arg3, ...

Assuming it is called properly, the script shifts the arguments and performs GOTO to the original arg1. Then the function has its arguments starting with the new arg1. This means you can take already written routines and plop them in the utility without having to worry about adjusting the parameter numbers.

The script gives an error if the function argument is not supplied, or if the function argument does not match a valid label within the script.

@echo off
if "%~1" neq "" (
  2>nul >nul findstr /rc:"^ *:%~1\>" "%~f0" && (
    shift /1
    goto %1
  ) || (
    >&2 echo ERROR: routine %~1 not found
  )
) else >&2 echo ERROR: missing routine
exit /b

:test1
echo executing :test1
echo arg1 = %1
exit /b

:test2
echo executing :test2
echo arg1 = %1
echo arg2 = %2
exit /b

:test3
echo executing :test3
echo arg1 = %1
echo arg2 = %2
echo arg3 = %3
exit /b

I prefer the GOTO approach that I used above. Another option is to use CALL instead, as Thomas did in his answer.

For a working example of a usefull library of batch functions that uses the CALL technique, see CHARLIB.BAT, a library of routines for processing characters and strings within a batch file. A thread showing development of the library is available here

I wrote CharLib.bat a few years ago. Were I to write it today, I would probably use GOTO instead of CALL.

The problem with introducing a CALL is that it creates issues when passing string literals as parameters. The extra CALL means that a string literal containing % must have the percents doubled an extra time. It also means unquoted poison characters like & and | would need to be escaped an extra time. Those two issues can be addressed by the caller. But the real problem is that each CALL doubles up quoted carets: "^" becomes "^^". There isn't a good way to work around the caret doubling problem.

The problems with the extra CALL don't impact CharLib.bat because string values are passed by reference (variable name) and not as string literals.

The only down side to using GOTO with SHIFT /1 is that you cannot use %0 to get the name of the currently executing routine. I could have used SHIFT without the /1, but then you wouldn't be able to use %~f0 within a routine to get the full path to the executing batch file.

like image 33
dbenham Avatar answered Oct 11 '22 08:10

dbenham