0

I have a list of string elements.

setlocal enabledelayedexpansion

CALL SET ListOFElts[0]=First
CALL SET ListOFElts[1]=Second
CALL SET ListOFElts[2]=Third

for /l %%n in (0,1,3) do (

   CALL SET Element=%%ListOFElts[%%n]%%

   whoami /groups /fo list | find "%Element%"
)

I want to do attempts of finding each elements with the command find but the variable Element doesn't seem to be "regognized". When I try this, it outputs nothing for the Element variable :

setlocal enabledelayedexpansion

CALL SET ListOFElts[0]=First
CALL SET ListOFElts[1]=Second
CALL SET ListOFElts[2]=Third

for /l %%n in (0,1,3) do (

   CALL SET Element=%%ListOFElts[%%n]%%

   whoami /groups /fo list | find ""
)

--

I also tried :

whoami /groups /fo list | find "%%Element%%"

Output of the previous line :

whoami /groups /fo list | find "%Element%"

--

I then tried (with the use of setlocal enabledelayedexpansion):

whoami /groups /fo list | find "!Element!"

Output of the previous line :

whoami /groups /fo list | find "!Element!"

--

I want to get this (first loop for instance) :

whoami /groups /fo list | find "First"

Thank you for your assistance.

Mofi
  • 46,139
  • 17
  • 80
  • 143

3 Answers3

0

My best guess:

@Echo Off
Set "ListOfElts[0]=First"
Set "ListOfElts[1]=Second"
Set "ListOfElts[2]=Third"

For /F "Tokens=1* Delims==" %%A In ('Set ListOfElts[ 2^>Nul'
) Do WhoAmI /GROUPS /FO LIST | Find /I "%%~B"

Pause
Compo
  • 36,585
  • 5
  • 27
  • 39
  • I'm sorry but what if I have many more "ListOfElts[n]" until like n=100 ? What do I have to change ? I tried to understand the logic of the FOR /F command but I don't get it. Thank you ... – Mickaël ADRAR Jun 01 '18 at 10:57
  • @MickaëlADRAR, that's fine, apart from setting them, the code remains unchanged. – Compo Jun 01 '18 at 10:59
  • Okay thank you ! I'm just curious about the In('Set ListOfElts[ 2^>Nul') code : What does the '2' mean ? I thought I had to adjust that value according to the number of elements. – Mickaël ADRAR Jun 01 '18 at 12:20
  • In this case `^` is used only as an escape character for the hidden cmd context which runs the content within the parentheses. What is therefore being seen is `2>Nul`, this is a standard error redirection to the `NUL` device, _(effectively preventing the output of error messages)_. The idea is that should the command `Set ListOfElts[` not match any existing variables, an error message, _(`Environment variable ListOfElts[ not defined`)_, would not unnecessarily trigger the `Do` portion. – Compo Jun 01 '18 at 12:30
0

First, open a command prompt window, run set /? and read entire help output for command SET on several pages. It explains the usage of delayed expansion on an IF and a FOR example.

It would be most likely also helpful to read the answers on:
How does the Windows Command Interpreter (CMD.EXE) parse scripts?

Your batch file contains setlocal enabledelayedexpansion at top to enable delayed environment variable expansion. But no environment variable is referenced with syntax !VariableName! to really make use of delayed environment variable expansion. For that reason "%Element%" is modified by Windows command processor already on parsing entire command block starting with ( and ending with matching ) to just "" before executing the command FOR. The environment variable Element is not defined before FOR command line is reached and cmd.exe parses the entire FOR command line with the command block ending with ) at end of batch file before executing FOR.

CALL is mainly used to call a subroutine or another batch file from within a batch file. Using CALL on a simple command line can be used to get a command line parsed twice by Windows command processor to avoid usage of delayed expansion. On the first three SET lines it is absolutely not necessary to use CALL as nothing changes on parsing the SET command lines a second time.

First solution is without using delayed expansion by using CALL to double parse the two command lines in code block.

@echo off
setlocal EnableExtensions DisableDelayedExpansion

set "ListOFElts[0]=First"
set "ListOFElts[1]=Second"
set "ListOFElts[2]=Third"

for /L %%I in (0,1,2) do (
    call set "Element=%%ListOFElts[%%I]%%"
    call %SystemRoot%\System32\whoami.exe /groups /fo list | %SystemRoot%\System32\find.exe "%%Element%%"
)

endlocal

The definition of environment variable Element is not necessary in command block as its value is referenced only once. So more compact and therefore faster executed would be:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

set "ListOFElts[0]=First"
set "ListOFElts[1]=Second"
set "ListOFElts[2]=Third"

for /L %%I in (0,1,2) do call %SystemRoot%\System32\whoami.exe /groups /fo list | %SystemRoot%\System32\find.exe "%%ListOFElts[%%I]%%"

endlocal

There is no command block anymore to execute by FOR on each loop iteration, just a single command line. But it is nevertheless required to use command CALL and use %%ListOFElts[%%I]%% as the entire FOR command line is parsed already for %VariableName% references before executing FOR the first time. So "%ListOFElts[%%I]%" would be replaced already to "" before FOR is executed at all.

Third solution is with really using delayed expansion.

@echo off
setlocal EnableExtensions EnableDelayedExpansion

set "ListOFElts[0]=First"
set "ListOFElts[1]=Second"
set "ListOFElts[2]=Third"

for /L %%I in (0,1,2) do (
    set "Element=!ListOFElts[%%I]!"
    %SystemRoot%\System32\whoami.exe /groups /fo list | %SystemRoot%\System32\find.exe "!Element!"
)

Fourth solution is compact version with using delayed expansion which is similar to the FOR example output by help of command SET:

@echo off
setlocal EnableExtensions EnableDelayedExpansion

set "ListOFElts[0]=First"
set "ListOFElts[1]=Second"
set "ListOFElts[2]=Third"

for /L %%I in (0,1,2) do %SystemRoot%\System32\whoami.exe /groups /fo list | %SystemRoot%\System32\find.exe "!ListOFElts[%%I]!"

endlocal

The command FINDSTR makes it possible to specify multiple search strings.

@%SystemRoot%\System32\whoami.exe /groups /fo list | %SystemRoot%\System32\findstr.exe /L /C:"First" /C:"Second" /C:"Third"

FINDSTR outputs all lines from input stream containing any of the three literally and case-sensitive interpreted strings specified with /C:"...". This single command line in a batch file replaces all four batch files above.

For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.

  • call /?
  • echo /?
  • endlocal /?
  • find /?
  • findstr /?
  • for /?
  • set /?
  • setlocal /?
  • whoami /?

See also answer on Why is no string output with 'echo %var%' after using 'set var = text' on command line? for a detailed explanation why on all SET command lines "variable=value" is used instead of just variable=value in the batch files above.

Mofi
  • 46,139
  • 17
  • 80
  • 143
0

Unless for learning/hidden purposes,
why not simply use

for %%A in (First Second Third) Do whoami /groups /fo list | find "%%A"

or even

whoami /groups /fo list | findstr "First Second Third"