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
8
  • if not %3=="" => if not "%3"=="". You already done that for first arg... Commented Nov 18, 2016 at 9:49
  • I put away the two quotations because when I call the batch file, I pass the arguments between "". Commented Nov 18, 2016 at 9:52
  • do you really have to pass them between quotes? I mean there are no spaces so what's the point? Commented Nov 18, 2016 at 9:55
  • I cannot guaranty that the path or the command do no have spaces. On Windows that's quite usual. Commented Nov 18, 2016 at 10:02
  • 1
    if not "%~3"=="" is the only reliable way (note the ~ modifier)... Commented Nov 18, 2016 at 12:48

2 Answers 2

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
Sign up to request clarification or add additional context in comments.

Comments

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"

1 Comment

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 /?.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.