1

I have a batch file that accept the action mode as parameter and run several command in specific order according to it, I wonder is there an alternative way to handle this batch file beside having lots and lots of IF statements in it ?

This is not a real code - just showing the flow

Get MODE as Parameter
If MODE=TYPE1 or MODE=TYPE2 then
    Run Command A
endif

If MODE=TYPE1 or MODE=TYPE3 then
    Run Command B
endif

If MODE=TYPE3 then
    Run Command C
endif
Epligam
  • 741
  • 2
  • 14
  • 36
  • possible duplicate of [switch statement equivalent in Windows batch file](http://stackoverflow.com/questions/18423443/switch-statement-equivalent-in-windows-batch-file) – Lorenzo Boccaccia Jun 11 '14 at 10:29
  • How many cases are you talking about? A batch command to test in the way you have is a single line for each case. You could also have the cases and executable in a text file and check that from a batch command, but the actual details will dictate the way it can be done. – foxidrive Jun 11 '14 at 10:39

3 Answers3

2
@echo off
    setlocal enableextensions

    set "MODE=%~1"
    set "WHENMODE=call :testCase "%MODE%" "

    %WHENMODE% TYPE1 TYPE2 && (
        echo Run command A
    )

    %WHENMODE% TYPE1 TYPE3 && (
        echo Run command B
    )

    %WHENMODE% TYPE3 && (
        echo Run command C
    )

    endlocal
    exit /b

:testCase value test1 test2 ... testn
    if "%~2"==""    exit /b 1
    if "%~1"=="%~2" exit /b 0
    shift /2 & goto %0

The %WHENMODE% definition is only aesthetic. The same code without decoration

@echo off
    setlocal enableextensions

    set "MODE=%~1"

    call :testCase "%MODE%" TYPE1 TYPE2 && echo Run command A
    call :testCase "%MODE%" TYPE1 TYPE3 && echo Run command B
    call :testCase "%MODE%" TYPE3       && echo Run command C

    endlocal
    exit /b

:testCase value test1 test2 ... testn
    if "%~2"==""    exit /b 1
    if "%~1"=="%~2" exit /b 0
    shift /2 & goto %0
MC ND
  • 69,615
  • 8
  • 84
  • 126
2

First I think it prudent to show how to do it with IF statements. Batch IF does not support OR logic. The following emulates the OR:

@echo off
setlocal enableDelayedExpansion
set "mode=%~1"

set "aModes= TYPE1 TYPE2 "
set "bModes= TYPE1 TYPE3 "
set "cModes= TYPE3 "

if "!aModes:%mode%=!" neq "!aModes!" echo command A
if "!bModes:%mode%=!" neq "!bModes!" echo command B
if "!cModes:%mode%=!" neq "!cModes!" echo command C

Now for possible alternatives to IF

1) One semi-practical thing I can think of is to use the MODE as a label, but I don't like it. Unnecessary use of CALL can be significantly slower. You must ensure that the MODE values are controlled, otherwise you will get ugly error messages. Also, the commands are replicated in the code since the same MODE runs multiple commands.

@echo off
setlocal
set "mode=%~1"

call :%MODE%
:: remainder of logic
exit /b

:TYPE1
echo command A
echo command B
exit /b

:TYPE2
echo command A
exit /b

:TYPE3
echo command B
echo command C
exit /b

If command A and/or B and/or C is a complicated block of commands, then they could be made into their own labeled subroutine, and CALLed where appropriate. This would prevent replication of the complicated logic.

2) Another possibility is FINDSTR with conditional command execution, but this will be slower than IF, and it is more awkward. The only advantage is it does not require extra variables to implement the OR logic.

@echo off
setlocal
set "mode=%~1"

echo %mode%|>nul findstr "TYPE1 TYPE2" && echo command A
echo %mode%|>nul findstr "TYPE1 TYPE3" && echo command B
echo %mode%|>nul findstr "TYPE3"       && echo command C

Note that if your modes were something like BC and ABCD, then the above will not work because BC is contained within ABCD. This could be handled by introducing some brackets.

@echo off
setlocal
set "mode=%~1"

echo {%mode%}|>nul findstr "{TYPE1} {TYPE2}" && echo command A
echo {%mode%}|>nul findstr "{TYPE1} {TYPE3}" && echo command B
echo {%mode%}|>nul findstr "{TYPE3}"         && echo command C

Finally, you could use MC ND's idea of a simple macro to make the above look a bit more elegant.

@echo off
setlocal
set "mode=%~1"

set "whenMode=echo {%mode%}|>nul findstr"

%whenMode% "{TYPE1} {TYPE2}" && echo command A
%whenMode% "{TYPE1} {TYPE3}" && echo command B
%whenMode% "{TYPE3}"         && echo command C

3) You could get really fancy and use an advanced technique of batch macros with arguments. Here is a good primer to the technology and development of batch macros with arguments.

It takes a significant bit of arcane code to set up the macro, but once defined, it is very easy to use, and extremely fast. The best part is it can be used against any variable. In my example below, I use it with MODE1 and MODE2.

@echo off
setlocal disableDelayedExpansion
set "mode1=%~1"
set "mode2=%~2"

:: define LF as a Line Feed (newline) character
set ^"LF=^

^" Above empty line is required - do not remove

:: define a newline with line continuation
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"

:: define a when macro that takes arguments
set when=%\n%
for %%# in (1 2) do if %%#==2 (setlocal enableDelayedExpansion^&for /f "tokens=1*" %%1 in ("!args!") do (%\n%
  set "test= %%~2 "%\n%
  for /f %%M in ("!%%1!") do (%\n%
    if "!test:%%M=!" neq "!test!" (%\n%
      endlocal%\n%
      endlocal%\n%
      (call )%\n%
    ) else (%\n%
      endlocal%\n%
      endlocal%\n%
      (call)%\n%
    )%\n%
  )%\n%
)) else setlocal^&set args=

:: ------- End of Initialization ---------------------

(%when% mode1 "TYPE1 TYPE2") && echo command A
(%when% mode1 "TYPE1 TYPE3") && echo command B
(%when% mode1 "TYPE3")       && echo command C

(%when% mode2 "TYPE1 TYPE2") && echo command X
(%when% mode2 "TYPE1 TYPE3") && echo command Y
(%when% mode2 "TYPE3")       && echo command Z
dbenham
  • 127,446
  • 28
  • 251
  • 390
  • Nice how you create the LF, with the quotes and only one empty line, that was new for me – jeb Jun 11 '14 at 15:13
1

I think the clearest and fastest solution to this problem is via an array:

@echo off
setlocal EnableDelayedExpansion

rem Define the array of equivalences from MODE values vs Command
set numCommands=0
for %%a in ("TYPE1|TYPE2=Command A"
            "TYPE1|TYPE3=Command B"
            "TYPE3=Command C") do (
   set /A numCommands+=1
   set "command[!numCommands!]=%%~a"
)

rem Get MODE as parameter:
set "MODE=%~1"

rem Execute the commands in order
for /L %%i in (1,1,%numCommands%) do (
   for /F "tokens=1,2 delims==" %%a in ("!command[%%i]!") do (
      set "options=|%%a|"
      if "!options:|%MODE%|=!" neq "!options!" ECHO Run: %%b
   )
)

EDIT: Or in a shorter way, with no array:

@echo off
setlocal EnableDelayedExpansion

set "MODE=%~1"

for %%i in ("TYPE1|TYPE2=Command A"
            "TYPE1|TYPE3=Command B"
            "TYPE3=Command C") do (
   for /F "tokens=1,2 delims==" %%a in (%%i) do (
      set "options=|%%a|"
      if "!options:|%MODE%|=!" neq "!options!" ECHO Run: %%b
   )
)
Community
  • 1
  • 1
Aacini
  • 65,180
  • 12
  • 72
  • 108