Windows batch file - splitting a string to set variables - windows

I feel like I'm going around in circles with FOR loop options.
I'm trying to take a string (output of a command) and split it on commas, then use each value to SET, e.g.
String: USER=Andy,IP=1.2.3.4,HOSTNAME=foobar,PORT=1234
So I want to split on comma and then literally use that variable in SET. I don't know ahead of time how many many variables there will be.
I've tried things like:
FOR %%L IN (%MYSTRING%) DO ECHO %%L
but that splits on the equals sign too so I end up with
USER
Andy
IP
1.2.3.4
etc
I just want to be able to do the following so I can SET USER=Andy etc, something like:
FOR %%L IN (%MYSTRING%) DO SET %%L
What option or flags am I missing?

The default delimiters for elements in plain FOR command (no /F option) are spaces, tab, commas, semicolons and equal signs, and there is no way to modify that, so you may use FOR /F command to solve this problem this way:
#echo off
set MYSTRING=USER=Andy,IP=1.2.3.4,HOSTNAME=foobar,PORT=1234
:nextVar
for /F "tokens=1* delims=," %%a in ("%MYSTRING%") do (
set %%a
set MYSTRING=%%b
)
if defined MYSTRING goto nextVar
echo USER=%USER%, IP=%IP%, HOSTNAME=%HOSTNAME%, PORT=%PORT%
Another way to solve this problem is first taking the variable name and then executing the assignment for each pair of values in a regular FOR command:
setlocal EnableDelayedExpansion
set MYSTRING=USER=Andy,IP=1.2.3.4,HOSTNAME=foobar,PORT=1234
set varName=
for %%a in (%MYSTRING%) do (
if not defined varName (
set varName=%%a
) else (
set !varName!=%%a
set varName=
)
)
echo USER=%USER%, IP=%IP%, HOSTNAME=%HOSTNAME%, PORT=%PORT%
EDIT 2023/01/20: New method added
I know this is a very old question. However, I can't resist the temptation to post a new very interesting method to solve this old problem:
#echo off
set MYSTRING=USER=Andy,IP=1.2.3.4,HOSTNAME=foobar,PORT=1234
set "%MYSTRING:,=" & set "%"
echo USER=%USER%, IP=%IP%, HOSTNAME=%HOSTNAME%, PORT=%PORT%
If you want to know where the magic is, remove the #echo off line, execute the program and carefully review the screen...

In case your input is something like HOSTNAME:PORT and you need to split into separate variables then you can use this
#echo off
set SERVER_HOST_PORT=10.0.2.15:8080
set SERVER_HOST_PORT=%SERVER_HOST_PORT::=,%
for /F "tokens=1* delims=," %%a in ("%SERVER_HOST_PORT%") do (
set SERVER_HOST=%%a
set SERVER_PORT=%%b
)
echo SERVER_HOST=%SERVER_HOST%
echo SERVER_PORT=%SERVER_PORT%

Related

FINDSTR to find text START END of string

I have string photo="999" price="10" category="1" . I want to get only 10. This means I need to the string which start price=" and ends with "
#For /F "Tokens=1*Delims==" %%A In ('FindStr /I "^price=" "C:\price.txt" 2^>NUL')Do #Set "Ver=%%~B"
#Echo(%%Ver%% = %Ver% & Pause
findstr always returns the complete line, if successful. So it's not the right tool for this task (actually, there is no tool in cmd at all that could do that this way).
But with a bit of logic, you can work around it: remove the part from the start until (including) the triggerword price (a task, the set command is happy to do), then process the rest with a for /f loop to get the desired substring:
set "string=photo="999" price="10" category="1""
echo check: %string%
echo debug: %string:*price=%
for /f tokens^=2^ delims^=^" %%a in ("%string:*price=%") do set "ver=%%~a"
echo ver=%ver%
If you are sure of the exact format of your string (in your example the searched substring is the second quoted argument, so the fourth token when splitted by ") it gets as easy as:
for /f tokens^=4^ delims^=^" %%a in ("%string%") do echo ver=%%~a
or
for /f tokens^=4^ delims^=^" %%a in (file.txt) do echo ver=%%~a
#ECHO OFF
SETLOCAL
set "string=photo="999" price="10" category="1""
:: remove quotes
set "string=%string:"=%"
for /f %%a in ("%string:* price=%") do set /a pricefound%%a
set pri
goto :eof
Since we don't have a representative sample of the file in question, we're forced to the conclusion that the requirement is to find the one and only appearance of price="anumber" in the file.
So, since findstr output, properly framed, would select this line, all we need do is process the string.
This is kind of a quick-and-dirty method; it may be adequate for OP's purpose.
First, remove the quotes from the string as they have a habit of interfering.
Next, use for /f in string-processing mode where it does its magic on the quoted string in parentheses. The string is the original string, minus quotes, so replace all characters up to "Spaceprice" with nothing and take the first token of the result, resulting in =10 assigned to %%a in the example case.
Then execute "set /a somevariablename=10" by simply concatenating the two strings.
Note that if the file contains a line like ... pricelastweek="9" ... then other measures may need to be taken.
Here's an example which tries to follow a similar methodology as your example code.
It uses FindStr to isolate any line in C:\price.txt, which includes the word price="<OneOrMoreDigits>". That line is saved as a variable named price, which is split under delayed expansion in a nested For loop, to remove everything up to, and including the first instance of the string price, leaving, in this case, ="10" category="1". The nested loop further splits that, to take the second token, using a doublequote character as the delimiter, (which should be your required value).
#For /F Delims^=^ EOL^= %%G In ('%__AppDir__%findstr.exe /IR "\<price=\"[0123456789]*\"\>" "C:\price.txt"') Do #(Set "price=%%G" & SetLocal EnableDelayedExpansion
For /F Tokens^=2^ Delims^=^" %%H In ("!price:* price=!") Do #EndLocal & Set "price=%%H")
#Echo %%price%% = %price% & Pause
Well clearly you need to match lines that contain price=" as there may be other lines.
What's unclear is if you need match 10 exactly, or just want that to be any number.
It seems likely you just want to match any number and grab it.
This is done easily with:
#For /F "Tokens=4 Delims=^= " %%A In ('
TYPE "C:\price.txt" ^| FIND /I "price="""') Do #(
Set "Ver=%%~A" & CALL SET Ver &Pause )
While is you need to match Price="10", which seems less useful, but at least one person took that meaning and your wording is a little unclear so I will add that was well:
#For /F "Tokens=4 Delims=^= " %%A In ('
TYPE "C:\price.txt" ^| FIND /I "price=""10"""') Do #(
Set "Ver=%%~A" & CALL SET Ver &Pause )
Note in all examples I left in the # symbols since I assume this is you being clever, and leaving ECHO ON and only removing the # symbols when you want to debug some specific thing you are doing.
However, in case not, it's worth pointing out that in a script it's usually easiest to place ECHO OFF at the start of the script instead of putting an # at the beginning of each statement to stop it from echoing.
Cheers! :)

How to reverse string list in batch

I have a problem with reversing string list in a batch script. Let say I have a list L=string1,string2,string3 I would like to obtain reversed list L=string3,string2,string1. Any ideas??
You may also use this shorter/simpler approach:
#echo off
setlocal EnableDelayedExpansion
set "L=string1,string2,string3"
echo Input =%L%
set "revL="
set "str=%L:,=" & set "revL=,!str!!revL!" & set "str=%"
set "revL=%str%%revL%"
echo Output=%revL%
This method use the same procedure of the other answers, but in less lines. If you want to know what happens here, remove the #echo off line and run it. ;)
Without knowing what your input looks like, this might be a first attempt:
#ECHO OFF
SETLOCAL EnableDelayedExpansion
SET var=abc,def,ghi
SET rev=
:LOOP
IF NOT "!var!"=="" (
FOR /F "delims=, tokens=1,*" %%F IN ("!var!") DO (
SET rev=%%F,!rev!
SET var=%%G
)
) ELSE (
SET rev=!rev:~0,-1!
GOTO ENDLOOP
)
GOTO LOOP
:ENDLOOP
ECHO reversed list is: !rev!
EDIT: As requested, here is an explanation how it works:
var is your starting list of strings separated by commas.
rev will be the reversed string. At the beginning this string is empty.
Now let's take a look at the loop:
In each iteration, we are separating our string into two parts: %%F and %%G. %%F will be everything before the first comma and %%G will be the rest of the string: FOR /F "delims=, tokens=1,*" %%F IN ("!var!"). delims=, means that we are using comma as delimiter. tokens=1,* means that the first found substring will be stored in %%F while the rest will be stored in %%G (%%F is defined for the first token so Windows command interpreter will put every token afterwards in G, H, I, and so on - as we are using *, everything will land in %%G). Finally, we take the first token of our string (%%F) and append ,!rev! to it. Then we set the remaining string list to everything behind the first comma (%%G).
In the first iteration, this loop does the following (pseudo code):
var=abc,def,ghi
rev=
split the string into %%F=abc and %%G=def,ghi
set rev to %%F,rev //means abc,
set var to var but without the first token //means def,ghi
In the second iteration:
var=def,ghi
rev=abc,
split the string into %%F=def and %%G=ghi
set rev to %%F,rev //means def,abc
set var to var but without the first token //means ghi
In the third iteration:
var=ghi
rev=def,abc
split the string into %%F=ghi %%G=
set rev to %%F,rev //means ghi,def,abc,
set var to var but without the first token //means empty string
Now, after jumping back to :LOOP, the if condition is no longer fulfilled as !var! has shrunk from formerly abc,def,ghi to now an empty string. So IF NOT !var!=="" becomes false and we are jumping to the ELSE clause.
There is one problem left: as we are constructing our reversed string by pre-appending the first token from the original list AND a comma, we will end up with a comma at the end of the reversed string list: ghi,def,abc,
SET rev=!rev:~0,-1! fixes this. It takes a "substring" from our string, starting at index 0 and finishing at "end-1". So this line simply removes the last , at the end of our string. Then we are jumping to :ENDLOOP.
Here is a batch file code assuming L=string1,string2,string3 is assigned to an environment variable:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "ListLine=L=string1,string2,string3"
for /F "tokens=1* delims==" %%I in ("%ListLine%") do (
set "LineBegin=%%I"
set "ListItems=%%J"
)
set "ReversedItems="
for %%I in (%ListItems%) do call set "ReversedItems=%%I,%%ReversedItems%%"
set "ListLine=%LineBegin%=%ReversedItems:~0,-1%"
echo %ListLine%
endlocal
Windows command interpreter interprets a comma in list of strings in a simple FOR loop like a space character as it can be seen on running this batch file without #echo off from within a command prompt window. Therefore the second FOR loop runs first with string1 assigned to loop variable I, second with string2 and third with string3.
Command CALL is used to do a double processing of the command SET to avoid the requirement to use delayed environment variable expansion as explained by help for command SET output on running set /? in a command prompt window.
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 /?
for /?
set /?
setlocal /?
Aacini, definitely has the fastest code out of all of the answers. This is some longer code that uses a similar SET trick.
#echo off
setlocal EnableDelayedExpansion
set i=1
set "x=abc,def,ghi"
set "x!i!=%x:,=" & set /A i+=1 & set "x!i!=%"
FOR /L %%G IN (%i%,-1,1) DO (
IF "%i%"=="%%G" (
set "reverse=!x%%G!"
) ELSE (
set "reverse=!reverse!,!x%%G!"
)
)
echo %reverse%
pause
Just some quick timed testing of all 4 answers. First one uses the original string 3 characters in each of the 3 comma separated fields. The second one uses 3 characters in 9 comma separated fields. Each time I tested running each one 100 times and calculated the average. The differences are negligible.
Average of 100 tries using 3x3
Aacini 0.39254
Squashman 0.39851
Michael 0.3999
Mofi 0.40434
Average 100 tries using 3x9
Aacini 0.39925
Squashman 0.40278
Michael 0.41457
Mofi 0.43397

Create new string from 1st character of each word of a multi word string

I have a need for a Windows NT command script to do the following:
- Read variable string from command line
- Take that variable and create new string based off 1st byte of each word
The incoming string in the Windows NT command script is %1.
The string may vary in length and number of words, but always ends with (something here) [something there].
Example - Incoming value from command line including quotes:
"This is my string for a new name (comment) [comment 2]"
The string I need to create is:
Timsfann
These characters are from the 1st letter in each of the words in the above string and stopping when it gets to the parenthesis in (comment).
The above string will be concatenated with a date/time string to create a unique filename. The finished string should be set to a system variable so that programs can find the finished value.
Any help would be appreciated.
I've been attempting using a FOR loop, but can't seem to get it to work as desired.
#Crimsyn, if it's easier for you to understand, here's another one of the few ways, which doesn't use delayed expansion or the FOR statement:
#Echo Off
Call :Loop %~1
Echo(%oStr%&Pause
Exit/B
:Loop
If "%~1"=="" GoTo :EOF
Set "iStr=%~1"
If "%iStr:~,1%"=="(" GoTo :EOF
Set "oStr=%oStr%%iStr:~,1%"
Shift&GoTo Loop
Another one! Although simpler and shorter, this method could be harder to understand... ;)
#echo off
setlocal EnableDelayedExpansion
set "str=%~1 "
set "str=%str:(= " & rem %
set "result="
set "word=%str: =" & set "result=!result!!word:~0,1!" & set "word=%"
echo %result%
Hint: execute it with echo on
Just one of a few ways you could do this.
#echo off
setlocal enabledelayedexpansion
for /f "tokens=1 delims=(" %%G IN ("%~1") do set "line=%%G"
FOR %%G IN (%line%) DO (
set tline=%%G
set code=!code!!tline:~0,1!
)
echo %code%
pause

Get a substring from a Windows batch variable

In a batch file I have a variable containing an IP. Just for example:
SET ip = 170.150.120.10
I would like to define another variable, replacing the last octet with 1, which in the example would be 170.150.120.1.
The IP may change (it is defined dynamically), varying in its length, so capturing a fixed length substring and concatenating it would not work anytime (in the example it would be SET baseIP = %ip:~0,9%.1).
How can this task be solved?
Is there some RegEx support in Windows command line?
maytham-ɯɐɥıλɐɯ has the key component of a simple solution - FOR /F. But that solution has a lot of complication that seems unrelated to the question.
The answer can be as simple as:
#echo off
set "ip=170.150.120.10"
for /f "tokens=1-3 delims=." %%A in ("%ip%") do set "new_ip=%%A.%%B.%%C.1"
echo new ip=%new_ip%
Note - You included spaces before and after the = in the SET statement in your question. That is a bad idea, as all of the spaces are significant. You have a variable name that ends with a space, and a value that begins with a space. I removed the unwanted spaces from the answer
Also, I enclosed the assignment within quotes. All characters after the last quote are ignored as long as the first quote is before the variable name. This protects against inadvertent trailing spaces in your value.
EDIT 2017-09-04
Even simpler method - treat the address as a filename, so the last node becomes the extension. Use a simple FOR and the ~n modifier to get the base name (1st 3 nodes), and then add your own extension (last node).
for %%A in (%ip%) do set "new_ip=%%~nA.1"
(Edit: added missing jump)
Here's my take. Iterates over the last four characters, looks if it is a dot, and appends the desired octet to the corresponding prefix part of the given IP. This works with any size (length) of last octet, e.g. 1.1.1.5 and 10.0.0.155
#ECHO OFF
SETLOCAL EnableDelayedExpansion
SET ip=170.150.120.10
SET new_ip_last_octet=1
ECHO Input was %ip%
FOR /L %%G IN (0,-1,-4) DO (
SET tmp=!ip:~%%G!
IF "!tmp:~0,1!" == "." (
SET new_ip=!ip:~0,%%G!.!new_ip_last_octet!
GOTO done
)
)
:done
ECHO New IP is %new_ip%
Output:
Input was 170.150.120.10
New IP is 170.150.120.1
Try this
#echo off
set ipCurrent=170.150.120.100
set ipOffsets=0.100.0.-24
#echo off
for /f "tokens=1-3 delims=. " %%a in ("%ipCurrent%") do (
set part1=%%a
set part2=%%b
set part3=%%c
)
for /f "tokens=1-3 delims=." %%a in ("%ipOffsets%") do (
set /a part1+=%%a
set /a part2+=%%b
set /a part3+=%%c
)
set ipBase= %part1%.%part2%.%part3%.1
#echo %ipCurrent% is changed to%ipBase%
EDIT
Thanks to #dbenham for input, the code above can be reduced to:
#echo off
set "ipCurrent=170.150.120.100"
#echo off
for /f "tokens=1-3 delims=. " %%a in ("%ipCurrent%") do set "ipBase=%%a.%%b.%%c.1"
#echo %ipCurrent% is changed to %ipBase%
Input any ip address range
input 170.150.120.10
or 170.150.120.110
Output 170.150.120.1
Batch resources: Link

Batch For Loop Treating Space-delineated Input as Single Token

I'm working on a batch script that is suppose to prompt the user for a list of projects, and then process each of those projects in turn. My thought was that this could be done with a for loop, but it's not working. For some reason it's treating the entire string entered by the user (CanalyzerIF CanoeIF CometIF) as a single token.
echo Enter the names of the projects, deliniating each with a space:
set /P PROJECT_LIST=
echo.
echo DEBUG: PROJECT_LIST is %PROJECT_LIST%
echo These are the projects you specified:
for /F "tokens=*" %%i in ("%PROJECT_LIST%") do (
echo %%i
)
My script output looks like this...
DEBUG: PROJECT_LIST is CanalyzerIF CanoeIF CometIF
These are the projects you specified:
CanalyzerIF CanoeIF CometIF
...when what I expect/want to see is this:
DEBUG: PROJECT_LIST is CanalyzerIF CanoeIF CometIF
These are the projects you specified:
CanalyzerIF
CanoeIF
CometIF
It doesn't seem to matter if I use percent signs (%) or exclamation marks (!) to wrap PROJECT_LIST. Anyone know how to fix this?
try with (plain FOR can be used for itration):
echo Enter the names of the projects, deliniating each with a space:
set /P "PROJECT_LIST="
echo.
echo DEBUG: PROJECT_LIST is %PROJECT_LIST%
echo These are the projects you specified:
for %%i in (%PROJECT_LIST%) do (
echo %%i
)
In addition to #npocmaka's perfect solution of the problem using for without /F, I want to provide a solution with the /F option, mainly for demonstration purpose.
First let's take a look at for /F without option string, so using default options, which are like "tokens=1 delims= _" (_ stands for a tab here). This means to take the first space- or tab-separated token and assign it to a given variable like %%i (that is, the first project in your list) and to ignore the rest.
To get multiple items, you need to specify exactly which ones you want to extract. For instance, to get tokens 2,3,4 & 6, state "tokens=2-4,6".
The first of these tokens is assigned to the given variable %%i, the other ones to %%j, %%k, %%l (which constitute implicitly defined variables).
The special token * means to pass all remaining tokens to a single variable, or, in other words, to treat them as a single token (for example, "tokens=1,*" passes the first token to %%i and all the rest to %%j).
Understanding all this leads us to the main problem using for /F: you need to know how many tokens are available. But for this application, we don't know that.
The following uses option string "tokens=1,*" to extract the first token and all the rest; there is a kind of while-loop wrapped around (composed by if and goto) that is intended to reprocess all the rest until nothing is available any more (herein I explicitly defined the space to be the only acceptable delimiter):
echo Enter the names of the projects, deliniating each with a space:
set /P PROJECT_LIST=
echo.
echo DEBUG: PROJECT_LIST is %PROJECT_LIST%
echo These are the projects you specified:
set PROJECT_TEMP=%PROJECT_LIST%
:LOOP
if not defined PROJECT_TEMP goto :NEXT
for /F "tokens=1,* delims= " %%i in ("%PROJECT_TEMP%") do (
echo. %%i
set PROJECT_TEMP=%%j
)
goto :LOOP
:NEXT
So the main problem in the original code is the option string "tokens=*", which defines to pass all tokens to the variable %%i.
Type for /? for more details on all this.

Resources