Iterating arrays in a batch file - arrays

I am writing a batch file (I asked a question on SU) to iterate over terminal servers searching for a specific user. So, I got the basic start of what I'm trying to do.
Enter a user name
Iterate terminal servers
Display servers where user is found (they can be found on multiple servers now and again depending on how the connection is lost)
Display a menu of options
Iterating terminal servers I have:
for /f "tokens=1" %%Q in ('query termserver') do (set __TermServers.%%Q)
Now, I am getting the error...
Environment variable __TermServers.SERVER1 not defined
...for each of the terminal servers. This is really the only thing in my batch file at this point. Any idea on why this error is occurring? Obviously, the variable is not defined, but I understood the SET command to do just that.
I'm also thinking that in order to continue working on the iteration (each terminal server), I will need to do something like:
:Search
for /f "tokens=1" %%Q in ('query termserver') do (call Process)
goto Break
:Process
for /f "tokens=1" %%U in ('query user %%username%% /server:%%Q') do (set __UserConnection = %%C)
goto Search
However, there are 2 things that bug me about this:
Is the %%Q value still alive when calling Process?
When I goto Search, will the for-loop be starting over?
I'm doing this with the tools I have at my disposal, so as much as I'd like to hear about PowerShell and other ways to do this, it would be futile. I have notepad and that's it.
Note: I would continue this line of questions on SuperUser, except that it seems to be getting more into programming specifics.

Ok, those are quite a few questions/issues/etc. in one :-)
And I still don't quite get where exactly you're headed with that script.
First of all, the syntax for the set command is
set <variable name>=<value>
If you do just
set <variable name>
then it will list all environment variables starting with <variable name>. If there are none, then it will output the error message you're seeing.
If you want to define a variable without actually caring about its value, you still need to provide a value. I usually use 1 for such flags, since it's then more an on/off switch than an actual variable holding a value:
set Foo=1
In your case you probably want something else, though. There are no arrays per se in batch files, you can mimic them by creating a number of variables and holding a count somewhere. I've written about that once before (a little outdated by now, but still valid).
In your case you want to iterate over a number of servers and for each server over a number of users. You can do that with a nested loop:
for /f "tokens=1" %%Q in ('query termserver') do (
for /f "tokens=1" %%U in ('query user ... /server:%%Q' do (
...
)
)
As for your two questions there:
No, the loop variable is only valid inside the loop, not when calling a subroutine. You can pass it to the subroutine, however:
for ... in (...) do call Process %%Q
You can then access it with %1 in the subroutine. Honestly, though, in most cases I think the nested loops are easier to read.
Yes.
Another error (one that will bite you): As mentioned before, the set syntax is
set variable=value
Note that there is no space around the = sign. If there is, then you have a space at the end of the variable name or at the start of the value:
> set foo = bar
> echo %foo%
%foo%
> echo %foo %
bar

Related

How to turn multiple parameters into a single parameter in Batch?

This topic is really hard to explain, so try to understand it. I'm working on a project called RoSOX that uses a URI to join a server (on another end)
However, this batch program requires at least 3 parameters. If you were to run it under the Windows Command Prompt, you would run it like this RoSOXLauncher.bat "IPHere" "PortHere" "UsernameHere". This is the code that obtains those values (Not the entire program though)
#echo off
set ip=%~1
set port=%~2
set username=%~3
Unfortunately, I found another post mentioning that URI Protocols (aka Registry Entries that allow starting a program from your web browser, such as "mailto:example#example.com") only accept 1 parameter regardless of the program. My theory to bypass this is to have the input connected by commas so it's considered one parameter. It would be written like this: RoSOXLauncher.bat IPHere,PortHere,UsernameHere An example would be this. The logarithm would be
Disassemble the values from the commas (The FOR command is probably used)
Take each value and put it into its own variable
Continue the rest of the script
Does anyone know how this logarithm would be wrote in Batch? This is the only thing preventing me from having my program run correctly.
It seems very unclear as to what you want, but if I understand your requirement correctly then:
#echo off
set ip=%~1
set port=%~2
set username=%~3
Will still work if you do:
RoSOXLauncher.bat IPHere,PortHere,UsernameHere
Simply because the delimiters excepted are whitespace, comma and semicolon. You can try this and see for yourself.
RoSOXLauncher.bat IPHere;PortHere;UsernameHere
RoSOXLauncher.bat IPHere PortHere UsernameHere
Will do the same.
On the other hand, your program which you send the parameters from might also delimit by whitespace, command or semicolon, then you might simply use aother common delimiter like - or : see this:
#echo off
for /f "tokens=1-3 delims=-" %%i in ("%~1") do (
echo ip %%i
echo port %%j
echo username %%k
)
Which you can run using:
RoSOXLauncher.bat IPHere-PortHere-UsernameHere
or
#echo off
for /f "tokens=1-3 delims=:" %%i in ("%~1") do (
echo ip %%i
echo port %%j
echo username %%k
)
which you can run with:
RoSOXLauncher.bat IPHere:PortHere:UsernameHere

Using batch to robocopy to computer addresses stored in a txt file

So basically I wanted to write a batch so I could type a bunch of computer names into a text file, have the batch pull the names and use them as variables to set the destination for a robocopy.
So something like:
for (i = line of file.txt)
if i != null then
var(i) = dest
robocopy src dest /e
so far I've plagiarized paraphrased some stuff and I've got the following
for /f "delims=" %%a in (c:\Users\test_list1.txt)
do set var=%%a&call :process
if [%%a] == [] do (
Set src=(c:\folder\source.txt)
Set dest=%%a
robocopy %src% %dest% /e
:process
echo var=%var%
goto :eof
I used to be reasonably awful at Java, but I'm kind of lost with scripting this.
Not that it's awfully relevant, but for information's sake. I'm copying minecraftedu to some 30+ machines in a school. I'd rather use another method, but the nature of the setup here means I'm limited to manually going machine to machine with a flash drive or using xcopy/robo.
Set "src=c:\folder\source.txt"
for /f "delims=" %%a in (c:\Users\test_list1.txt) do echo %%a&xcopy "%src%" "%%a\" /s /e
should be equivalent.
You don't say why or whether you are using the var array you are establishing in your pseudocode, so I dropped it.
The quotes in the set statement ensure that stray terminal spaces are not included in the value assigned. Those in the xcopy are to combine the strings into two identifiable tokens in case one or the other contains a separator like space.
Your problems are:
the do keyword and its following ( must be on the same physical line as the for (there are circumstances where this is not so, but the rules and regulations are too complex for a simple explanation)
The %%a in the if statement is out-of-scope.
%%a will never be nothing so testing it is superfluous in any case.
There is no do in an if statement. Your code would attempt to find an executable, and execute do ( where ( is simply a character.
That (, even if the do was removed is unterminated.
There seems no reason to repeatedly set src to the same value, and the parentheses there are significant and part of the value assigned.
If the lines following the unterminated ( up to the :label are expected to be a code-block, then the values assigned within the code block are inaccessible without delayedexpansion.
Having executed the mainline, batch assigns no special meaning to a label. It's simply a marker - it does not signify the end of a "procedure". You'd need to execute a goto :eof just before the label to skip the procedure code, else the procedure code will be executed by "fall-through"

How to extract text and set as variable Batch Code CMD

I would like to extract what users are online based on this text file:
https://minecraft-statistic.net/en/server/167.114.43.185_25565/json/
I will save it as a text file. Inside that there is something:
"players_list":["Raskhol"]["Lukaka"],"map":...etc
I would like to extract all the text between "_list": and ,"map" and set it as a variable. So that when I call the variable %Playerlist%, it would say:
["Raskhol"]["Lukaka"]
similar to #geisterfurz007's answer, this one assumes the you are after the first instance of "players_list": before the first instance of ,"map"
#Echo Off
Set/P var=<some.json
Set var=%var:,"map"=&:%
Set var=%var:*"players_list":=%
Echo=%var%
Timeout -1
Not tested due to beeing on phone.
#echo off
For /f "delims=: tokens=2" %%g in (PathTo\file.txt) do (
Set var=%%g
Goto:next
)
:next
Set var=%var:,map=%
echo %var%
Assumes players are the first to be listed.
Reads the file, takes the part after the first : up to the second one, stores it in var.
Then ,map gets replaces with nothing to result in just the players beeing echoed in the end.
Feel free to ask questions if something is unclear! Might take a while as I am currently mobile though.

Is it possible to get a variable from a string in batch?

I've recently started to learn batch for the sake of writing batch sims for a game that I've been playing. I was wondering if its possible to somehow iterate through like named variables (since I can't seem to find anything about a list?). Also I'm not sure if I can put a label to call to as a variable passed.
Code Example:
:: Enemy Fortress level.
SET EFORTLVL=4
:: Don't mess with anything below here only the variables above.
:: Enemy Fortress that will be simmmed against. Note this batch sim is only built to run against one tower, as this is what you should be doing.
SET EFORTRESS1="Foreboding Archway-%EFORTLVL%"
SET EFORTRESS2="Illuminary Blockade-%EFORTLVL%"
SET EFORTRESS3="Tesla Coil-%EFORTLVL%"
SET EFORTRESS4="Minefield-%EFORTLVL%"
SET EFORTRESS5="Forcefield-%EFORTLVL%"
call :sim 1
:sim
SET /a "COUNTER=1"
SETLOCAL enabledelayedexpansion
SET times=!ITERATIONS%1!
ENDLOCAL & SET TIMES=%times%
:whilesim
SETLOCAL enabledelayedexpansion
SET fort=!EFORTRESS%COUNTER%!
ENDLOCAL & SET FORT=%fort%
tuo.exe %DECK0% %ENEMY% surge random -v efort %FORT% yfort %YFORTRESSES% climb %TIMES% >> %PATH%\WarDefClimbData%DECK0%.txt
SET /a "COUNTER=COUNTER+1"
if %COUNTER% leq 5 GOTO :whilesim else GOTO :eof
The result that I get for the line on the console:
RESOLVED:
What I want to do is get a value from a variable that holds a string name that relates to the variable in question. (Ex when the for loop passes 1 I want to get EFORTRESS1 value, 2 I want EFORTRESS2 value etc).
E:\Programs\Tyrant Unleashed Optimizer>tuo.exe oconzer "VetDecks" surge random -v efort EFORTRESS1 yfort "Inspiring Altar #2" climb ITERATIONS1 1>>"e:\Programs\Tyrant Unleashed Optimizer\BatchSimResults"WarDefClimbDataoconzer.txt
Error: unrecognized fortress EFORTRESS1
Now I understand why its saying the error, what I don't understand is why its not getting the value from the string that is contained in FORT.
RESOLVED
Getting an endless loop, where the iteration variable isn't updating.
:sim
SETLOCAL ENABLEDELAYEDEXPANSION
SET "FORT=!EFORTRESS%1!"
ENDLOCAL&SET "fort=%fort%"
SET TIMES=ITERATIONS%2
SET LABEL=%3
The issue is to get the contents of (the contents of a variable), often call "indirection".
This is probably the easiest way. It uses setlocal enabledelayedexpansion which places cmd in delayedexpansion mode, where !var! is evaluated after its contents.
The drawback is that it must be invoked using setlocal, which establishes a local environment. The loacl environment must eventually be closed (you cannot keep opening more) and at that time, all changes to the environment are discarded and it is restored to its state at the point of executing setlocal.
The endlocal&... uses a parsing trick to transfer the changes out of the setlocal/endlocal bracket.
As for the other questions - yes, you can goto a variable (and the contents of the variable need not have a leading colon). It is quite possible to use goto somewhere%1 for instance, and supply %1 as a parameter as you've done. The text somewhere would be simply prepended to the value %1.
BTW - it would appear that you are changing path. This is not a good idea. path is the variable that contains a ;-separated list of directories which are searched for an executable if that executable is invoked and not found in the current directory. Best left well alone. Same goes for tmp and temp (which point to a temporary directory) and date and time and random and cd (the current date, time, a random number and the current directory)

Batch- Hiding Text/Disabling or Enabling Values?

I'm making an RPG game in Batch and I need to make it so that if the player hasn't bought a certain weapon it doesn't show up as an option to use. Currently, I have a battle system in which you press a key to use a weapon you have. However, I don't know how to make it so that if you don't own a certain weapon, you can't use it.
I also want it so that even if the text doesn't show, the user still can't use it if they don't have it. For example, if you don't have a sword but you know that to use it you press s, I don't want it to work unless you have it. That way people can't just cheat.
Please help!
Here is the code:
if %sword1% equ true echo 2-Tachi
set /p e="Weapon to equip:"
pause
I don't want the user to just type 2 and have the Tachi. I want it so that they have to
have the weapon to use it.
#echo off
setlocal enabledelayedexpansion
set w1=sword
set w2=bow
set w3=axe
set w4=machinegun
set w5=warbow
set w1?=true
set w2?=false
set w3?=true
set w4?=false
set w5?=false
:loop
echo ----------
for /L %%i in (1,1,9) do (
if !w%%i?!==true echo %%i - !w%%i!
)
set /p e="Weapon to equip: "
if !w%e%?!==true (
echo you have choosed: !W%e%!
) else (
echo you don't have one.
goto :loop
)
echo now, that you have a !W%e%!, go and fight!
echo Bamm - Wush - Ouch
if %random% lss 10000 (
echo Congratulations, you have found a %w4%.
set "w4?=true"
)
pause
goto :loop
EDIT (explanations to the code)
the first block of set just sets the names of the weapons.
The second block of set sets it they are available to the player. (in gameplay, usually there is only one available in the beginning, others can be found or bought during the game).
The ? in the variable names is nothing special, only a char like any letter. If it confuses you, you can also use something like B (like boolean): set w5B=false
The for-loop is the heart of this code. It displays all available weapons (it tries w1 to w9. You can adapt it to process hundreds or even thousands of items (it starts from 1, increments by 1 until 9))
If it is available (true), it echoes the number (%%i) and the weapon-name.
!w%%i?! is a bit tricky. %%i is the "loop-counter" (1,2,3,...,9), so w%%i? will translate to (for example) w4? (and w%%i to w4.
Because you need the variable with that name, you'll have to put it between %, but as %%i is already part of the variable, the interpreter would get confused. For that, we use delayed expansion (search for it in stackoverflow, there are more findings than you can read) and therefore can use ! instead of %, so the complete variable is named !w%%i?! (remember: the ? is just a letter). (don't be depressed, if you don't get it the first time, just play around with it and it will make sense to you some day).
The if after the set /p e="Weapon to equip: " checks, if the weapon is available (true) if yes, fine, if no then error/try again.
The if %random%... part shows you how to make an item available (true). You can also make one unavailable by setting the flag to false.) Wheather you do that like me as a finding in a fight, or in a shop (buy/sell), the doing is the same.
I'm sure, you already guessed, that you'll have to replace echo Bamm - Wush - Ouch with some code for a fight.
I find it hard to come up to a solution since the user will be able to edit the .bat file anyway? Wouldn't it be better to write it in C++ or C#?

Resources