Share via


That's a pretty shifty argument...

It's pretty easy to deal with up to 9 arguments in batch through built-in variables %1 through %9. For example:

    echoargs.cmd
@echo off
echo %1
echo %2
echo %3
echo %4
echo %5
echo %6
echo %7
echo %8
echo %9

If you feed this A B C D E F G H I, you'll get back those letters, one per line.

But, if you try this with a tenth argument, it won't work like you might have thought:

    echo10args.cmd
@echo off
echo %1
echo %2
echo %3
echo %4
echo %5
echo %6
echo %7
echo %8
echo %9
echo %10

You'll see that the last line echoes the first argument again, followed by a 0 (A0). That is, it gets parsed as "echo %1, then 0". So, how do you deal with this if you need more than 9 arguments? My first suggestion would be to see if there's a way you can deal with fewer arguments, though that's often not possible. If you must, you'll need to loop over each argument and deal with them one by one. Here's an example listing:

    echoallargs.cmd
@echo off

    :Top
    REM If there aren't any arguments to deal with, bail out
if "%1" equ "" goto :NoMoreArgs

    call :EchoArg %1
    shift

    REM Start the loop over again with a brand new %1
goto :Top

    :NoMoreArgs
echo No more arguments to deal with
goto :EOF

    :EchoArg
echo Arg: %1
goto :EOF

Let's call this script with 11 arguments: A B C D E F G H I J K

At :Top, we check to see if there are any arguments to deal with. If not, we go to :NoMoreArgs and bail out. Then (assuming we didn't bail out), we call :EchoArg with the parameter that's first in the list (in this case, A). :EchoArg takes the parameter it was passed (A) and echos Arg: A. Once it's done, we use goto :EOF to indicate that we're done with this subroutine. Control then passes back up to the line just after our call.

This next step is where all the magic happens. The shift command takes everything you've passed in and shifts them all to the left by one. That is, what used to be %3 becomes %2, %2 becomes %1, and %1 falls off. So now, since there was a tenth parameter, that one becomes %9, which means there's now a way of referring to it.

Once we've shifted everything, we go back to the :Top again. Now since our original second argument (B) has been shifted to %1, we do the same thing all over again. That gets echoed, we shift again, and C is the new %1 (and K is the new %9). We keep doing this until we run out of arguments, and then we bail out.

There are a few important points in here:

  • For anything you call, whether it's a "subroutine" like :EchoArg or another script (like call foo.cmd), the arguments passed to it are "local". That is, if you were to call :EchoArg %8 (that is, H in our case), as far as :EchoArg is concerned, that's %1. It's its own first argument.
  • The built-in variable %* (meaning all arguments passed in) is not at all affected by shift. In our

While we're on the topic of shift, you can see from shift /? that it can also take a switch that indicates where you want to start shifting. This takes a bit of wrapping your brain around, but it goes like this:

Given a list of arguments A B C D E F G H I J K, a regular shift would change that to B C D E F G H I J K.

But, we could also use shift /3 (for example) to start the shifting at the third argument. So our A B C D E F G H I J K becomes A B D E F G H I J K; the third argument (C) gets swallowed. Note that even though you can address 9 elements at a time %1 through %9, you can't do shift /9 and have the tenth argument become the ninth. You're limited to shift /8. 

Oh well, you can't have everything...

Comments

  • Anonymous
    June 30, 2005
    A for loop works well for this too, without using shift:

    for /d %%i in (%*) do (
    echo %%i
    )