views:

73

answers:

2

Reference Iterating arrays in a batch file

I have the following:

for /f "tokens=1" %%Q in ('query termserver') do (
    if not ERRORLEVEL (
        echo Checking %%Q
        for /f "tokens=1" %%U in ('query user %UserID% /server:%%Q') do (echo %%Q)
    )
)

When running query termserver from the command line, the first two lines are:

Known
------------------------- 

...followed by the list of terminal servers. However, I do not want to include these as part of the query user command. Also, there are about 4 servers I do not wish to include. When I supply UserID with this code, the program is promptly exiting. I know it has something to do with the if statement. Is this not possible to nest flow control inside the for-loop?

I had tried setting a variable to exactly the names of the servers I wanted to check, but the iteration would end on the first server:

set TermServers=Server1.Server2.Server3.Server7.Server8.Server10

for /f "tokens=2 delims=.=" %%Q in ('set TermServers') do (
    echo Checking %%Q
    for /f "tokens=1" %%U in ('query user %UserID% /server:%%Q') do (echo %%Q)
)

I would prefer this second example over the first if nothing else for cleanliness.

Any help regarding either of these issues would be greatly appreciated.

A: 

NT shell/batch language is not smart enough to accept IF NOT ERRORLEVEL (... -- you need to do an explicit comparison, like this:

if not %ERRORLEVEL%==0 (
...
ewall
Plain wrong. And using the `%errorlevel%` pseudo-variable can be harmful as it will be overshadowed if a variable of the same name exists.
Joey
+1  A: 

Again, there are multiple things to note here.

if errorlevel

The help for if says:

IF [NOT] ERRORLEVEL number command

as syntax for the if errorlevel condition. That is, you must provide a number to compare against. Keep in mind that if errorlevel n evaluates to true if the exit code was at least n.

So

if errorlevel 1 ...

catches any error (that is signaled through the exit code), while

if errorlevel 0 ...

simply is always true.

Anyways, you probably want a

if not errorlevel 1 ...

here, since that condition is true if no error occurred.

Skipping lines

The for /f command has an argument skip=n which can be used to skip lines at the start. If your output starts with two lines you don't want, then you can just do

for /f "skip=2 tokens=1" %%Q in ('query termserver') do

Iterating over multiple known values in for /f

The problem with your second code snippet is that for iterates line-wise. So when you give it a single environment variable it will tokenize it (and put the tokens into different variables), but the loop runs only once per line. Also note that using set here is a bit error-prone as you might get more back than you want. Something like

for /f ... in ("%TermServers%") ...

would have been easier. Still, that doesn't solve the original problem. The easiest way to solve this would probably be something like the following:

rem space-separated list of servers
set TermServers=Server1 Server2 Server3 Server7 Server8 Server10

rem call the subroutine with the list of servers
call :query_servers %TermServers%

rem exit the batch file here, to prevent the subroutine from running again afterwards
goto :eof

rem Subroutine to iterate over the list of servers
:query_servers

  rem Process the next server in the list
  rem Note the usage of %1 here instead of a for loop variable
  echo Checking %1          
  for /f "tokens=1" %%U in ('query user %UserID% /server:%1') do (echo %%Q)

  rem Remove the first argument we just processed
  shift

  rem if there is still another server to be processed, then do so
  rem we're mis-using the subroutine label as a jump target here too
  if not [%1]==[] goto query_servers

rem This is kind of a "return" statement for subroutines
goto :eof

(untested, but should work.)

ETA: Gah, and once again I miss the most obvious answer:

set TermServers=Server1 Server2 Server3 Server7 Server8 Server10
for %%S in (%TermServers%) do (
    for /f "tokens=1" %%U in ('query user %UserID% /server:%1') do (echo %%Q)
)

Note that this is simply for, not for /f and it will dutifully iterate over a list of values. I don't know how I missed that one, sorry.

Joey
+1: once again your knowledge of batch commands astounds me and your explanations are top notch - have you thought about writing reference material?
dboarman
@dboarman: I did; though I never really got around it. I'm not that much of a writer (as I currently painfully notice in my studies). Also I've still got much to learn—at least while I'm not able to reproduce the tricks on other batch file resource sites (I have my own, though; but with less practical value).
Joey
@dboarman: Read the edit at the bottom. I tend to think way too complicated, sometimes, sorry ...
Joey