Skip to content

Latest commit

 

History

History
executable file
·
288 lines (210 loc) · 6.5 KB

File metadata and controls

executable file
·
288 lines (210 loc) · 6.5 KB

🔁 Functions and Subroutines in Batch Scripting Using CALL

  • Batch scripting, while not as modern or powerful as Bash or PowerShell, still serves as a go-to tool for many Windows automation tasks.

    One of the most underrated and misunderstood aspects of batch scripting is modular programming using functions and subroutines.

    🧠 What is a Subroutine in Batch?

    • In batch files, functions are often referred to as subroutines.

    • Unlike modern programming languages, batch scripting doesn't support true functions with return values or parameter scopes,

      but it does support labels and the CALL command, which together simulate the behavior of subroutines.

      A subroutine:

      • Is defined using a :label
      • Is invoked using CALL :label (optionally with parameters)
      • Returns control back to the calling code using the EXIT /B command

    📜 Basic Syntax

    CALL :SubroutineName arg1 arg2 ...
    ...
    :SubroutineName
    :: some code here
    EXIT /B

    💡 Syntax

    @ECHO OFF
    ECHO Calling the subroutine...
    CALL :MySubroutine
    ECHO Subroutine finished.
    GOTO :EOF
    
    :MySubroutine
    ECHO Inside the subroutine.
    EXIT /B

    🔍 Explanation

    • CALL :MySubroutine — This tells CMD to jump to :MySubroutine and come back after it's done.
    • EXIT /B — Exits the subroutine and returns control to the caller. Always end with EXIT /B to return control back to the caller.
    • GOTO :EOF — Ends the script cleanly, preventing accidental execution of the subroutine.

  • 🔹 Passing Parameters to Subroutines

    You can pass parameters just like command-line arguments: %1, %2, etc.

    @ECHO OFF
    CALL :GreetUser "Omkar"
    CALL :Add 10 20
    GOTO :EOF
    
    :GreetUser
    ECHO Hello, %1!
    EXIT /B
    
    :Add
    SET /A result=%1 + %2
    ECHO Sum: %result%
    EXIT /B
  • 🔹 Returning Values from Subroutines

    Since you can't directly return values, you typically use global variables or environment variables:

    @ECHO OFF
    CALL :Multiply 6 7
    ECHO Product is: %result%
    GOTO :EOF
    
    :Multiply
    SET /A result=%1 * %2
    EXIT /B

    🔁 Remember: All variables are global unless explicitly localized using SETLOCAL.

  • 🔹 Using SETLOCAL and ENDLOCAL for Scoped Variables

    To prevent variable pollution and manage memory properly:

    @ECHO OFF
    CALL :CalcSquare 5
    ECHO Square is: %square%
    GOTO :EOF
    
    :CalcSquare
    SETLOCAL
    SET /A squareVal=%1 * %1
    ENDLOCAL & SET square=%squareVal%
    EXIT /B

    Here, we capture the local variable using the ENDLOCAL & SET var=value trick.


Examples 🎯

  1. Reusing Subroutines: Real-World Example

    Let's say you're checking disk space and writing logs. You can modularize this with functions.

    @ECHO OFF
    CALL :LogMessage "Starting disk check..."
    CALL :CheckDisk C:
    CALL :LogMessage "Done."
    GOTO :EOF
    
    :CheckDisk
    ECHO Checking disk %1
    CHKDSK %1 >NUL
    EXIT /B
    
    :LogMessage
    ECHO [%DATE% %TIME%] %1 >> script.log
    EXIT /B
  2. 🔹 Returning Error Codes

    You can pass error codes using EXIT /B n and then access them via %ERRORLEVEL%.

    @ECHO OFF
    CALL :Divide 10 0
    IF %ERRORLEVEL% NEQ 0 (
        ECHO Error occurred in Divide subroutine!
    ) ELSE (
        ECHO Division succeeded!
    )
    GOTO :EOF
    
    :Divide
    IF %2==0 (
        ECHO Cannot divide by zero!
        EXIT /B 1
    )
    SET /A quotient=%1 / %2
    ECHO Quotient: %quotient%
    EXIT /B 0
  3. 🔹 Nested Subroutine Calls

    You can call subroutines from within subroutines:

    @ECHO OFF
    CALL :Main
    GOTO :EOF
    
    :Main
    CALL :Add 5 3
    CALL :PrintResult "Sum" %result%
    EXIT /B
    
    :Add
    SET /A result=%1 + %2
    EXIT /B
    
    :PrintResult
    ECHO %1: %2
    EXIT /B
  4. 🔂 Subroutines in Loops

    @ECHO OFF
    FOR %%X IN (2 4 6 8) DO CALL :Square %%X
    GOTO :EOF
    
    :Square
    SET /A sq=%1 * %1
    ECHO Square of %1 = %sq%
    EXIT /B

🧪 Tips for Writing Maintainable Batch Subroutines

  1. Always use EXIT /B to avoid falling through unintended labels.
  2. Use SETLOCAL/ENDLOCAL for variable isolation.
  3. Quote your parameters when passing strings.
  4. Use CALL :label only, avoid GOTO :label unless absolutely necessary (as it doesn’t return).
  5. Avoid deep nesting — batch isn’t optimized for recursion or complex logic.
  6. Prefix subroutine labels (:fn_) - Avoid accidental label name clashes.

🚫 Common Pitfalls

1. ❌ Forgetting EXIT /B

:Greet
ECHO Hello
:: Missing EXIT /B – will fall through to next code block

2. ❌ Forgetting GOTO :EOF

:: Main logic
CALL :Process
:: Falls into :Process if GOTO :EOF is missing

🧪 Example: Chain Subroutine Calls

@ECHO OFF
CALL :Start
CALL :Middle
CALL :End
GOTO :EOF

:Start
ECHO Starting...
EXIT /B

:Middle
ECHO In the middle...
EXIT /B

:End
ECHO Ending...
EXIT /B

⚙️ Advanced: Passing More than 9 Arguments

You can only use %1 to %9 — but with SHIFT, you can rotate through arguments:

@ECHO OFF
CALL :List %*
GOTO :EOF

:List
IF "%1"=="" GOTO :EOF
ECHO %1
SHIFT
GOTO List

📦 Real-World Use Case

@ECHO OFF
CALL :DeployApp MyApp "C:\\Builds\\MyApp"
CALL :CleanUp
GOTO :EOF

:DeployApp
ECHO Deploying %1 from %2 ...
:: Simulated deployment logic
EXIT /B

:CleanUp
ECHO Cleaning up temp files...
EXIT /B