0

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.)

2 Answers 2

1

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.
echo Arguments to Process: %argCount%
echo Last Argument: %lastArg%


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

goto :eof


REM This subroutine gets the last command-line argument by
REM using SHIFT. It also counts the number of arguments.
:GetLastArg
set /a argCount+=1
set "lastArg=%~1"
shift
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:

fin.txt
"D:\Ref Quotes\*.pdf"
..\*.doc

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 Up.do
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:

fin.txt
"D:\Ref Quotes\*.pdf"
..\*.doc
manifesto.txt
87.docx
"C:\Plans to Rule Earth\*.ppt"
"Compromising Photos\*.jpg"
"What's Up.doc"
"Gimme All Your Lovin'.mp3"
out-of-funny-ideas.xls
5
  • 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
    Commented Aug 5, 2012 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
    Commented Aug 5, 2012 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
    Commented Aug 5, 2012 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. Commented Aug 5, 2012 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
    Commented Aug 5, 2012 at 23:54
0

Instead of using Shift we can kill two birds with one stone, we can use sort to reverse the order of the parameters and save them in a variable, then grab the 1st arguement for your purpose and then remove it from the variable and submit to your originalcode.

Or alternatively, and even easier, just use the fact that on a loop that sets a variable the variable will equal the last value set)

*Remember to surround your directory path with quotes if there are any spaces in it

EG

 @( SetLocal EnableDelayedExpansion
    Echo Off
    REM  Capture arguments to variable
    SET "_Args=%*"
 )

 REM Get Directory to use that is the last term:
 For %%A IN (%*) DO ( SET "_DirectoryPath=%%A")

 REM Remove directory from _Args variable:
 SET "_Args=!_Args:%_DirectoryPath%=!"

 REM Now for your original script use !_Args! Instead of %*

 For %%f in (!_Args!) do ( ... ) 

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .