0

I am translating a shell script to windows batch. What I need to do is take all except 1,2 and last from command line arguments. join them and send to another program as argv.

@echo off

SET subject=%1
set count=%2
set candidates=""
set /a i=0
set /a c=0
FOR %%A IN (%*) DO (
  ECHO %%A
  set /a i+=1
  IF %i% geq 2 (
    set /a c+=1;
    set candidates[!c!]=%%A
  )
)
SET /a count_actual=(%i%-3)
SET /a count_expected=%count%
echo %count_expected%
echo %count_actual%
echo %subject%
echo %candidates%

I want the candidates array be argv[3..n-1]

e.g. If I write batch x 2 a b p it should pass a b to that another program

The problem is loop counter is not being incremented by += operator. If I write echo %1% inside FOR I see 0 always

Xearinox
  • 3,224
  • 2
  • 24
  • 38
Dipro Sen
  • 4,350
  • 13
  • 37
  • 50
  • 1
    To see the value of a variable which is modified inside a loop you must enable delayed expansion. Place this at the start of your script `setlocal EnableDelayedExpansion`. See `setlocal /?` for help and definitions. – David Ruhmann Jan 05 '13 at 19:07

4 Answers4

1

You should not use for %%A in (%*) as it treats %* as filename set. This may cause problems, especially if you can pass * or ? (wildcard match characters in cmd) in parameters - as they will be expanded to all files satisfying pattern. Second, batch does really know nothing about arrays - a[1] and a[2] are just a shorthand notation for humans - they are two distinct variables.

Given the problem Parse command line, take second parameter as count of parameters to concatenate into a variable, here is my take:

@echo off
setlocal

set subject=%1

shift
set exp_count=%1

if not defined exp_count (
  echo Count not specified
  exit /b 1
)
set /a "verify=%exp_count%"
if %verify% leq 0 (
  echo Count not valid /not a positive integer/
  exit /b 2
)

set real_count=0
:loop
    shift
    if "%~1"=="" goto end_params
    set /a real_count+=1
    if %real_count% leq %exp_count% set "candidates=%candidates%%~1"
    goto loop
  )

:end_params
if %real_count% lss %exp_count% (
  echo Less parameters passed than specified!
  exit /b 3
)

echo %subject%
echo %candidates%

Please note I'm not checking if there is a 'hanging' parameter (the last, not being concatenated) but it should be trivial to add that check. I left it out on purpose to make the code more flexible.

wmz
  • 3,645
  • 1
  • 14
  • 22
1

I have two answers for your question:

1- The first problem is that in IF %i% ... command the value of i variable not change (although set /a i+=1 command will correctly increment the variable) and the way to solve it is by including setlocal EnableDelayedExpansion command at beginning and enclose i in percents signs this way: IF !i! ... (as said in previous answers). However, you must note that an array variable in Batch is different than a simple variable with same name (they both can exist at same time), so array elements must always be written with subscripts and there is NO way to process an entire array in a single operation. See this topic for further details.

In your program you must transfer the elements of candidates array into a simple variable, that in the example below have the same name (just to state my point):

@echo off
setlocal EnableDelayedExpansion
SET subject=%1
set count=%2
set candidates=""
set /a i=0
set /a c=0
FOR %%A IN (%*) DO (
  ECHO %%A
  set /a i+=1
  IF !i! geq 2 (
    set /a c+=1
    set candidates[!c!]=%%A
  )
)
SET /a count_actual=(%i%-3)
SET /a count_expected=%count%
echo %count_expected%
echo %count_actual%
echo %subject%
REM Transfer "candidates" array elements into "candidates" simple variable:
set candidates=
FOR /L %%i IN (1,1,%c%) do (
   set candidates=!candidates! !candidates[%%i]!
)
REM Show "candidates" simple variable:
echo %candidates%

Note that in Batch files you may insert commas, semicolons and equal-signs as separators instead spaces in most commands. However, SET /A command have other rules at this respect, so the semicolon must be omitted.

2- Independently of the array management explained above, this is the way I would solve your problem using a list instead of an array:

@echo off
SET subject=%1
shift
set count=%1
set candidates=
set lastArg=
set i=0
:nextArg
   shift
   if "%1" equ "" goto endArgv
   set /a i+=1
   set candidates=!candidates! !lastArg!
   set lastArg=%1
   goto nextArg
:endArgv
SET /a count_actual=i-3, count_expected=count
echo %count_expected%
echo %count_actual%
echo %subject%
echo %candidates%

Antonio

Community
  • 1
  • 1
Aacini
  • 65,180
  • 12
  • 72
  • 108
0

Yes your code will not increment i. Batch variable replacement occurs when a block is parsed, not when it is executed. The entire for block is parsed once, so %i% is replaced with zero before the for block is executed.

To disable that you need to enable delayed expansion and change your variable escape characters from %'s to !'s to have the replacement made at runtime. Then you will see i incremented in the for loop.

@echo off
Setlocal EnableDelayedExpansion

SET subject=%1
set count=%2
set candidates=""
set /a i=0
set /a c=0
FOR %%A IN (%*) DO (
  ECHO %%A
  set /a i+=1
  IF !i! geq 2 (
    set /a c+=1
    set candidates[!c!]=%%A
  )
)
SET /a count_actual=(%i%-3)
SET /a count_expected=%count%
echo %count_expected%
echo %count_actual%
echo %subject%
echo %candidates%

You will also need to get rid of the ; at the end of the set /a c+=1; line and I'm not sure what you are trying to do on line set candidates[!c!]=%%A as the brackets don't mean anything in batch.

shf301
  • 31,086
  • 2
  • 52
  • 86
  • I am trying to append `%%A` to `candidates` array. actually I want to slice `argv[3..n-1]` – Dipro Sen Jan 05 '13 at 07:52
  • But Its not appending anything to that array. rather its not treating it as array at all – Dipro Sen Jan 05 '13 at 08:12
  • @DiproSen - Batch does not have arrays - the best you can do is appending: `set candidates=!candidates! %%A`. – shf301 Jan 05 '13 at 18:07
  • @shf301: What do you mean with "Batch does not have arrays"? Batch files can _manage_ arrays in an equivalent way of other programming languages! See my answer... – Aacini Jan 05 '13 at 18:26
0

While there are a bunch of answers already listed, I decided to add one more. My approach is to keep the answer as simple as possible for your specific needs. If you have any questions feel free to ask.

This will create the array as you desired [3,...,n-1] without the need for delayed expansion or fancy logic.

@echo off

:: Get the First Two Parameters    
set "subject=%1"
shift
set "count=%1"
shift

:: Loop through the rest
set "index=0"
:NextParam
set "param=%1"
shift
set "next=%1"
:: Skip the last parameter
if not defined next goto EndParam
set "candidates[%index%]=%param%"
set /a "index+=1"
goto NextParam
:EndParam

set "count_actual=%index%"
set "count_expected=%count%"

:: Show the Results
echo %count_actual%
echo %count_expected%
echo %subject%
set candidates

Here is an alternate where the candidates are stored in a space delimited string instead of seperate variables. Replace the space between the %candidates% %param% to whatever delimiter you desire.

@echo off

:: Get the First Two Parameters  
set "subject=%1"
shift
set "count=%1"
shift

:: Loop through the rest
set "index=0"
:NextParam
set "param=%1"
shift
set "next=%1"
:: Skip the last parameter
if not defined next goto EndParam
set "candidates=%candidates% %param%"
set /a "index+=1"
goto NextParam
:EndParam

set "count_actual=%index%"
set "count_expected=%count%"

:: Show Results
echo %count_actual%
echo %count_expected%
echo %subject%
echo %candidates%
David Ruhmann
  • 11,064
  • 4
  • 37
  • 47