2

Basically, the script below can be used in three different ways: call foo

  1. .bat boot_up "path"
  2. .bat halt "path"
  3. .bat ssh "path" "command"

I cannot guarantee that the path or the command does not have spaces.

When I use foo.bat for executing my subroutine ssh, then everything works. Instead, when I try to call my subroutines boot_up or halt, an error apprears:

( was unexpected at this time.

But, if I add a third argument to boot_up or halt, then everything works again.

So my question is, how do I manage call of batch files with variable lenght of arguments?

:main
    echo Argument 1: (%1)
    echo Argument 2: (%2)
    echo Argument 3: (%3)

    call :set_cygwin_env || exit /b 1

    if not "%1"=="" if not %2=="" (
        if "%1"=="boot_up" (
            call :boot_up %2
        ) else if "%1"=="halt" (
            call :halt %2
        ) else if "%1"=="ssh" if not %3=="" (
            call :ssh %2 %3
        ) else (
            call :show_help || exit /b 1
        )
    ) else (
        call :show_help || exit /b 1
    )
:exit
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
lucaboni
  • 2,334
  • 2
  • 29
  • 41

2 Answers2

5

The source of your error is ) else if "%1"=="ssh" if not %3=="" ( - When you don't pass a 3rd argument, your code expands to ) else if "halt"=="ssh" if not =="" (, which is invalid syntax. The entire compound statement must have valid syntax, even the branches that don't fire. You must make sure the left side of the comparison has at least one character. Enclosing quotes are typically used, because they protect against poison characters like & and |, and token delimiters like space, comma, equal, tab.

In general, you should use if "%~1"=="someValue" ... when doing comparisons with command line arguments. The ~ removes any existing enclosing quotes, and then you explicitly add your own. It is important to first remove the quotes because you cannot anticipate whether the user added their own quotes or not. A value like "this&that" could be passed in, so "%1" would expand to ""this&that"", and the & would no longer be quoted. "%~1" expands to the desired "this&that". This strategy is not fool proof, but it is about as good as it gets without doing crazy batch programming that you probably don't want to get into.

So your fixed code should look like

:main
    echo Argument 1: (%1)
    echo Argument 2: (%2)
    echo Argument 3: (%3)

    call :set_cygwin_env || exit /b 1

    if not "%~1"=="" if not "%~2"=="" (
        if "%~1"=="boot_up" (
            call :boot_up %2
        ) else if "%~1"=="halt" (
            call :halt %2
        ) else if "%~1"=="ssh" if not "%~3"=="" (
            call :ssh %2 %3
        ) else (
            call :show_help || exit /b 1
        )
    ) else (
        call :show_help || exit /b 1
    )
:exit
Community
  • 1
  • 1
dbenham
  • 127,446
  • 28
  • 251
  • 390
2

you could pre-process your arguments to remove passed quotes as follows (I simplified your code to create an independent test)

@echo off

:main    
    set ARG1=%1
    set ARG2=%2
    set ARG3=%3

    if x%ARG1%==x goto skarg1
    if ^%ARG1:~0,1%==^" set ARG1=%ARG1:~1,-1%
:skarg1
    if x%ARG2%==x goto skarg2
    if ^%ARG2:~0,1%==^" set ARG2=%ARG2:~1,-1%
:skarg2
    if x%ARG3%==x goto skarg3
    if ^%ARG3:~0,1%==^" set ARG3=%ARG3:~1,-1%
:skarg3

    echo Argument 1: (%ARG1%)
    echo Argument 2: (%ARG2%)
    echo Argument 3: (%ARG3%)

        if "%ARG1%"=="boot_up" if not "%ARG2%"=="" (
            echo bootup "%ARG2%"
        )
:exit
  • if argument is empty, just leave it as-is
  • if argument starts with quotes, just remove the first & last char
  • use %ARGx% (quoted) from now on instead of %1,%2

quick test using only argument 2:

L:\>args boot_up
Argument 1: (boot_up)
Argument 2: ()
Argument 3: ()
L:\>args boot_up arg2
Argument 1: (boot_up)
Argument 2: (arg2)
Argument 3: ()
bootup "arg2"
L:\>args boot_up "arg2 space"
Argument 1: (boot_up)
Argument 2: (arg2 space)
Argument 3: ()
bootup "arg2 space"
L:\>args boot_up "arg2"
Argument 1: (boot_up)
Argument 2: (arg2)
Argument 3: ()
bootup "arg2"
Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
  • 4
    Better would be using `%~1` ... first argument with surrounding double quotes removed if present at all. Explained by help of command __CALL__ output in a command prompt window on running `call /?`. – Mofi Nov 18 '16 at 10:30