56

I wonder if there is a simple way to branch execution in a Windows batch file depending on the value of one single expression. Something akin to switch/case blocks in C, C++, C#, Java, JavaScript, PHP, and other real programming languages.

My only workaround is a plain if/else block where the same expression is repeatedly checked for equality against different values:

IF "%ID%"=="0" (
  REM do something
) ELSE IF "%ID%"=="1" (
  REM do something else
) ELSE IF "%ID%"=="2" (
  REM do another thing
) ELSE (
  REM default case...
)

So dumb. Is there a better solution?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
GOTO 0
  • 42,323
  • 22
  • 125
  • 158
  • 2
    Another way (although I cringe to suggest Goto!) .... you can build labels `:ID1` `:ID2` etc, and one `Goto ID%ID%` should jump to the relevant one. But the way you're already doing it isn't so very terrible, is it? I'm sure there are other languages where the whole expression is on each case/switch line. – AjV Jsy Aug 24 '13 at 23:19
  • 1
    Keep in mind that in many languages having a `switch` statement (this is true for C and C++ at least) that construct is used for speed-optimization purposes, since it avoids the repeated comparison of the if-else-if chain, which can be expensive. Usually there is no **big** readability gain in using `switch` in this case, since the chained comparisons are not that ugly (you just compare against a constant). I hope your question is prompted by curiosity and you don't really want to speed-optimize a batch file! :-) ;-) – LorenzoDonati4Ukraine-OnStrike Aug 25 '13 at 11:32
  • @LorenzoDonati I'm not complaining about the readability of the `IF`/`ELSE` construct (who came up with readability?). I just think it's not smart and error-prone to write similar code multiple times. And no, don't worry... speed is also not a concern :-) I'm just curious because I've run into this kind of problems many times while coding batch scripts. – GOTO 0 Aug 25 '13 at 13:33
  • 1
    I know readability wasn't mentioned, but I would argue that a case statement _is_ more readable. it's clear at-a-glance that it's the same variable being tested without needing to check every if-block. I have some colleagues who always use if/else and it's not as readable. Plus the switch is easier to re-factor. But it seems here the choice is if/else or a clever but somewhat dirty hack – Adam Apr 18 '17 at 12:04

9 Answers9

76

I ended up using label names containing the values for the case expressions as suggested by AjV Jsy. Anyway, I use CALL instead of GOTO to jump into the correct case block and GOTO :EOF to jump back. The following sample code is a complete batch script illustrating the idea.

@ECHO OFF

SET /P COLOR="Choose a background color (type red, blue or black): "

2>NUL CALL :CASE_%COLOR% # jump to :CASE_red, :CASE_blue, etc.
IF ERRORLEVEL 1 CALL :DEFAULT_CASE # If label doesn't exist

ECHO Done.
EXIT /B

:CASE_red
  COLOR CF
  GOTO END_CASE
:CASE_blue
  COLOR 9F
  GOTO END_CASE
:CASE_black
  COLOR 0F
  GOTO END_CASE
:DEFAULT_CASE
  ECHO Unknown color "%COLOR%"
  GOTO END_CASE
:END_CASE
  VER > NUL # reset ERRORLEVEL
  GOTO :EOF # return from CALL
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
GOTO 0
  • 42,323
  • 22
  • 125
  • 158
  • What do you do if you want to use CASE_%COMPUTERNAME% and then you specify :CASE_COMPUTER-1? It doesn't understand the -1 after COMPUTER and throws an error. Another words it treats COMPUTER-1 as COMPUTER and -1. – yorkman Apr 17 '20 at 18:21
  • 1
    To be stressed that the `goto :END_CASE` and `exit /B` parts are important as `call :LABEL` appears to run a new instance of the current batch at the given label. The lines after call will be ran after the `call` is done, as with a regular call. – Dominic Grenier Sep 14 '21 at 12:21
  • Saving the next me some clicks: [Why `CALL` and not `GOTO`](https://stackoverflow.com/a/34402449/1028230) and [what `GOTO :EOF` does](https://stackoverflow.com/a/37518000/1028230), though, admittedly, I'm not sure why `GOTO :EOF` is in this answer without more code below it. `CALL` should jump back all by itself, right? `call :HELL\ngoto :HELL\n:HELL\necho "hello"` will echo hello twice. Swap the order of `call` and `goto` and it'll only echo hello once. That is, if you use `GOTO` instead of `CALL` here, you can dispense with the final `GOTO` and the `EXIT /B`. I think. ?? – ruffin Sep 15 '22 at 21:31
21

This is simpler to read:

IF "%ID%"=="0" REM do something
IF "%ID%"=="1" REM do something else
IF "%ID%"=="2" REM do another thing
IF %ID% GTR 2  REM default case...
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
foxidrive
  • 40,353
  • 10
  • 53
  • 68
  • 2
    Thanks! Readability is always good, but I wonder if it's possible to avoid repeating the comparison expression (here `IF "%ID%"==...`). – GOTO 0 Aug 25 '13 at 10:00
  • 1
    Calculations can be used - but they will depend on the exact task. Batch files are most often built on the exact requirements of a task and which is why very few batch files can be used in any situation without modification. – foxidrive Aug 25 '13 at 10:04
9

Compact form for short commands (no 'echo'):

IF "%ID%"=="0" ( ... & ... & ... ) ELSE ^
IF "%ID%"=="1" ( ... ) ELSE ^
IF "%ID%"=="2" ( ... ) ELSE ^
REM default case...

After ^ must be an immediate line end, no spaces.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
9

I guess all other options would be more cryptic. For those who like readable and non-cryptic code:

IF        "%ID%"=="0" (
    REM do something

) ELSE IF "%ID%"=="1" (
    REM do something else

) ELSE IF "%ID%"=="2" (
    REM do another thing

) ELSE (
    REM default case...
)

It's like an anecdote:

Magician: Put the egg under the hat, do the magic passes ... Remove the hat and ... get the same egg but in the side view ...

The IF ELSE solution isn't that bad. It's almost as good as python's if elif else. More cryptic 'eggs' can be found here.

2

I searched switch / case in batch files today and stumbled upon this. I used this solution and extended it with a goto exit.

IF "%1"=="red" echo "one selected" & goto exit
IF "%1"=="two" echo "two selected" & goto exit
...

echo "Options: [one | two | ...]

:exit 

Which brings in the default state (echo line) and no extra if's when the choice is found.

x6iae
  • 4,074
  • 3
  • 29
  • 50
Oliver
  • 29
  • 1
2

Hariprasad didupe suggested a solution provided by Batchography, but it could be improved a bit. Unlike with other cases getting into default case will set ERRORLEVEL to 1 and, if that is not desired, you should manually set ERRORLEVEL to 0:

goto :switch-case-N-%N% 2>nul || (
    rem Default case
    rem Manually set ERRORLEVEL to 0 
    type nul>nul
    echo Something else
)
...

The readability could be improved for the price of a call overhead:

call:Switch SwitchLabel %N% || (
:SwitchLabel-1
    echo One
    goto:EOF     
:SwitchLabel-2
    echo Two
    goto:EOF
:SwitchLabel-3
    echo Three
    goto:EOF
:SwitchLabel-
    echo Default case
)

:Switch
goto:%1-%2 2>nul || (
    type nul>nul
    goto:%1-
)
exit /b

Few things to note:

  1. As stated before, this has a call overhead;
  2. Default case is required. If no action is needed put rem inside to avoid parenthesis error;
  3. All cases except the default one are executed in the sub-context. If you want to exit parent context (usually script) you may use this;
  4. Default case is executed in a parent context, so it cannot be combined with other cases (as reaching goto:EOF will exit parent context). This could be circumvented by replacing goto:%1- in subroutine with call:%1- for the price of additional call overhead;
  5. Subroutine takes label prefix (sans hyphen) and control variable. Without label prefix switch will look for labels with :- prefix (which are valid) and not passing a control variable will lead to default case.
HYBRID BEING
  • 264
  • 2
  • 11
1

Try by this way. To perform some list of operations like

  1. Switch case has been used.
  2. Checking the conditional statements.
  3. Invoking the function with more than two arguments.

 @echo off
 :Start2 
    cls
    goto Start
    :Start
    echo --------------------------------------
    echo     Welcome to the Shortcut tool     
    echo --------------------------------------            
    echo Choose from the list given below:
    echo [1] 2017
    echo [2] 2018
    echo [3] Task

    set /a one=1
    set /a two=2
    set /a three=3
    set /a four=4
    set input=
    set /p input= Enter your choice:
    if %input% equ %one% goto Z if NOT goto Start2
    if %input% equ %two% goto X if NOT goto Start2
    if %input% equ %three% goto C if NOT goto Start2
    if %input% geq %four% goto N

    :Z
    cls
    echo You have selected year : 2017
    set year=2017
    echo %year%
    call:branches year

    pause
    exit

    :X
    cls
    echo You have selected year : 2018
    set year=2018
    echo %year%
    call:branches year
    pause
    exit

    :C  
    cls
    echo You have selected Task
    call:Task
    pause
    exit

    :N
    cls
    echo Invalid Selection! Try again
    pause
    goto :start2




    :branches
    cls
    echo Choose from the list of Branches given below:
    echo [1] January
    echo [2] Feburary
    echo [3] March
    SETLOCAL
    set /a "Number1=%~1"
    set input=
    set /p input= Enter your choice:
    set /a b=0
    set /a bd=3
    set /a bdd=4
    if %input% equ %b% goto N
    if %input% leq %bd% call:Z1 Number1,input if NOT goto Start2
    if %input% geq %bdd% goto N



    :Z1
    cls
    SETLOCAL
    set /a "Number1=%~1"
    echo year = %Number1%
    set /a "Number2=%~2"
    echo branch = %Number2%
    call:operation Number1,Number2
    pause
    GOTO :EOF


    :operation
    cls
    echo Choose from the list of Operation given below:
    echo [1] UB
    echo [3] B
    echo [4] C
    echo [5] l
    echo [6] R
    echo [7] JT
    echo [8] CT
    echo [9] JT
    SETLOCAL
    set /a "year=%~1"
    echo Your have selected year = %year%
    set /a "month=%~2"
    echo You have selected Branch = %month%
    set operation=
    set /p operation= Enter your choice:
    set /a b=0
    set /a bd=9
    set /a bdd=10
    if %input% equ %b% goto N
    if %operation% leq %bd% goto :switch-case-N-%operation% if NOT goto Start2
    if %input% geq %bdd% goto N




    :switch-case-N-1
    echo Januray
    echo %year%,%month%,%operation%
    goto :switch-case-end

    :switch-case-N-2
    echo Feburary
    echo %year%,%month%,%operation%
    goto :switch-case-end

    :switch-case-N-3
    echo march
    echo %year%,%month%,%operation%
    goto :switch-case-end

    :switch-case-end
       echo Task Completed
       pause
       exit
       goto :start2






    :Task
    cls
    echo Choose from the list of Operation given below:
    echo [1] UB
    echo [3] B
    echo [4] C
    echo [5] l
    echo [6] R
    echo [7] JT
    echo [8] CT
    echo [9] JT
    SETLOCAL
    set operation=
    set /p operation= Enter your choice:
    set /a b=0
    set /a bd=9
    set /a bdd=10
    if %input% equ %b% goto N
    if %operation% leq %bd% goto :switch-case-N-%operation% if NOT goto Start2
    if %input% geq %bdd% goto N




    :switch-case-N-1
    echo Januray
    echo %operation%
    goto :switch-case-end

    :switch-case-N-2
    echo Feburary
    echo %year%,%month%,%operation%
    goto :switch-case-end

    :switch-case-N-3
    echo march
    echo %year%,%month%,%operation%
    goto :switch-case-end

    :switch-case-end
       echo Task Completed
       pause
       exit
       goto :start2
Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
abd kumar
  • 11
  • 2
1

If if is not working you use:

:switch case %n%=1 
statements;
goto :switch case end
etc..

http://lallouslab.net/2016/12/21/batchography-switch-case/

Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
1

It might be a bit late, but this does it:

set "case1=operation1"
set "case2=operation2"
set "case3=operation3"

setlocal EnableDelayedExpansion
!%switch%!
endlocal

%switch% gets replaced before line execution. Serious downsides:

  • You override the case variables
  • It needs DelayedExpansion

Might eventually be usefull in some cases.

GrubHD
  • 19
  • 2