Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have to modify a batch file (written by a former employee) that uses a for %%f in (%*) do ( ... ) loop to operate on each command-line parameter. The new requirement is that a directory name will be appended to the batch call, and this directory is to be used for each parameter with a relative path. For example:

DoJob.bat Fin.txt "D:\Ref Quotes\*.pdf" ..\*.doc "E:\Jan 2012"

should translate to:

DoJob.bat "E:\Jan 2012\Fin.txt" "D:\Ref Quotes\*.pdf" "E:\Jan 2012\..\*.doc"

Once I have the final command-line parameter in a variable, I can prefix it to each parameter that doesn't contain a colon (and thus is a relative path). But my problem is, how do I go about obtaining this final command-line parameter, and how do I make the loop above stop processing at the penultimate parameter?

(Please do not suggest alternatives to batch files; I have already enquired and the only available option is to modify the existing one.)

share|improve this question

You can use SHIFT and a loop to walk through all of the command line arguments. Since SHIFTing will consume each argument, you'll want to do it in a subroutine. The subroutine I present here also counts the arguments, so you know how many to loop through later.

@echo off

set argCount=0

call :GetLastArg %*

REM subtract 1 from arg count, so we know how many to process.
set /a argCount-=1

echo Arguments to Process: %argCount%
echo Last Argument: %lastArg%

echo Here are the other arguments:
if %argCount% == 0 goto EndArgProcessingLoop
set /a argCount-=1
echo %1
goto BeginArgProcessingLoop

goto :eof

REM This subroutine gets the last command-line argument by
REM using SHIFT. It also counts the number of arguments.
set /a argCount+=1
set "lastArg=%~1"
if not "%~1"=="" goto GetLastArg
goto :eof

Because we use SHIFT, this process works with your original command line...

batch.cmd fin.txt "D:\Ref Quotes\*.pdf" ..\*.doc "E:\Jan 2012"

Arguments to Process: 3
Last Argument: E:\Jan 2012

Here are the other arguments:

"D:\Ref Quotes\*.pdf"

and a list of arguments greater than 9 in number, which would cause problems if you were to use %1 through %9...

batch.cmd fin.txt "D:\Ref Quotes\*.pdf" ..\*.doc manifesto.txt
87.docx "C:\Plans to Rule Earth\*.ppt" "Compromising Photos\*.jpg" "What's
c" "Gimme All Your Lovin'.mp3" out-of-funny-ideas.xls "E:\Jan 2012"

Arguments to Process: 10
Last Argument: E:\Jan 2012

Here are the other arguments:

"D:\Ref Quotes\*.pdf"
"C:\Plans to Rule Earth\*.ppt"
"Compromising Photos\*.jpg"
"What's Up.doc"
"Gimme All Your Lovin'.mp3"
share|improve this answer
Thanks for the detailed answer! I actually dabbled a bit with changing the main loop after posting the query above. Thing is, the for %%f in (%*) do ( ... ) loop seems to have one huge advantage over the normal shift+goto loop, which is that the former expands all wildcards while the latter doesn't. The actual processing inside the loop (which I'm trying hard not to touch since it looks really convoluted with numerous band-aids over the years), simply accepts individual filenames and acts on them. (Contd. below since comment sizes seem to be quite restricted here.) – Karan Aug 5 '12 at 16:58
(Contd. from comment above.) The (%*) loop takes something like *.pdf and actually passes the individual filenames to the code inside, whereas it's obvious from your example above that the shift+goto loop doesn't do this. Also, the (%*) loop automatically skips over wilcards that do not expand to any existing files. That is, if nothing matches *.pdf in the specified path, the loop will simply skip over it and move on to the next parameter, which is quite handy. (Sigh... Contd. again below.) – Karan Aug 5 '12 at 17:00
(Contd. from comment above.) Moving to the shift+goto loop would I guess involve a whole lot more work, to detect when wildcards are part of a parameter and expand each in an inner loop to obtain the actual filenames, unless you can suggest an easy solution for this problem as well? If not, any way to keep the original (%*) loop intact and still get this done with minimal changes? (Phew, end of comment!) – Karan Aug 5 '12 at 17:01
To expand the paths, you would do something like "%~f1" instead of "%1". Run "for /?" and look near the end of the output. – Patrick S. Aug 5 '12 at 17:34
Expanding the paths is not the main issue, expanding the wildcards is. The (%*) loop does this automatically in the batch file, whereas with the shift+goto loop one would have to figure out which filenames match each wildcard parameter separately, introducing more loops into the equation. – Karan Aug 5 '12 at 23:54

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.