I come to find some guidance on accomplishing the following:
I have a variable with content like this:
varname = asdfiuytgy12$gggsy22.oihbcxew
or
varname = oiujedc$thisisit.oiju
which $ and . are exactly my partters and I need to get what is within them so gggsy22 or thisisit.
I need to use batch to create a simple bat file. I hope someone can provide some guidance.
Edit - (from comment section)
Actually a friend of mine helped and it did work but with a quite amount of lines:
Set "sstr=$"
SET stemp=%nameVar%&SET pos=0
:loop
SET /a pos+=1
echo %stemp%|FINDSTR /b /c:"%sstr%" >NUL
IF ERRORLEVEL 1 (
SET stemp=%stemp:~1%
IF DEFINED stemp GOTO loop
SET pos=0
)
Set "pos1=%pos%"
Set "sstr=."
SET stemp=%nameVar%&SET pos=0
:loop
SET /a pos+=1
echo %stemp%|FINDSTR /b /c:"%sstr%" >NUL
IF ERRORLEVEL 1 (
SET stemp=%stemp:~1%
IF DEFINED stemp GOTO loop
SET pos=0
)
Set "pos2=%pos%"
set /a "pos2=%pos2%-%pos1%-1"
call set env=%%nameVar:~%pos1%,%pos2%%%
#echo off
set "varname=asdfiuytgy12$gggsy22.oihbcxew"
for /f "tokens=2 delims=$." %%a in ("%varname%") do set "sub=%%a"
The following works in nearly any situation. The only thing that could break the code is if the string contains a quote " followed by a poison character like &, |, etc.
#echo off
setlocal
set "str=oiujedc$thisisit.oiju"
:: Verify string exists and has the proper format
echo "%str%"|findstr "\$.*\." >nul || (echo Value not found & exit /b)
:: Extract the value
:: The extra "x" is needed in case there is no character between $ and .,
:: in which case the result should be No Value (result variable not defined)
for /f "delims=." %%A in ("x%str:*$=%") do set "val=%%A"
set "val=%val:~1%"
:: Show the result
echo value = "%val%"
A bullet proof variant can be made by incorporating delayed expansion.
Related
In the following script I call a subroutine with a string, the maximal length of a substring of that string and a 3rd non-existing variable to get the substring back.
The script should check if the next character after the string with the maximal substrings length is a space and if yes, then cut the string by that space (delete space to) and returning the substring and change the passed string by cutting the substring part. Example:
string: "Hello World Bla"
substringLength: 5
CALL :get_substring string substringLength substring
=> string: "World Bla" (no space), substringLength: 5 (no change), substring: "Hello"
This works fine without if clause but it doesn't when i use an if clause, even if i use delayed expansion.
Here is the working code without if statement:
#ECHO OFF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
SET string=Hello World wasserschutzpolizei
SET /A substringLength=5
CALL :get_substring string substringLength substring
ECHO !string!
ECHO !substring!
EXIT /B 0
ENDLOCAL
:get_substring
SETLOCAL ENABLEDELAYEDEXPANSION
SET "string=!%~1!"
SET "substringLength=!%2!"
SET nextChar=!string:~%substringLength%,1!
REM IF "!nextChar!"==" " (
SET substring=!string:~0,%substringLength%!
ECHO !substring!
SET /A cutSpaceCount=!substringLength!+1
SET string=!string:~%cutSpaceCount%!
ECHO !string!
ENDLOCAL & SET %1=%string% & SET %3=%substring% & EXIT /B 0
REM ) ELSE (
REM Some other case
REM )
EXIT /B 0
This doesn't work when I comment in the if statement:
IF "!nextChar!"==" " (
SET substring=!string:~0,%substringLength%!
ECHO !substring!
SET /A cutSpaceCount=!substringLength!+1
SET string=!string:~%cutSpaceCount%!
ECHO !string!
ENDLOCAL & SET %1=%string% & SET %3=%substring% & EXIT /B 0
) ELSE (
REM Some other case
)
Why does the script doesn't work with an if-statement?
How can fix it?
Be aware that my routine should also include an else statement which I cutted out because the problem is the same.
The problem is this line:
SET string=!string:~%cutSpaceCount%!
When this line is placed inside the IF command, then the value of cutSpaceCount is changed inside the code block (parentheses) of the IF, and hence it must be expanded via !cutSpaceCount! delayed expansion, not via a %cutSpaceCount% standard expansion.
You should use something like a "double delayed expansion", that is, similar to this construct:
SET string=!string:~!cutSpaceCount!!
Of course, this don't work, so the trick is use a for command to get the value of the first delayed expansion, and then use the FOR parameter to complete the second delayed expansion:
for /F %%c in ("!cutSpaceCount!") do SET "string=!string:~%%c!"
A similar problem happen when the final values in the subroutine are returned to the calling program. This is the final working code:
#ECHO OFF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
SET string=Hello World wasserschutzpolizei
SET /A substringLength=5
CALL :get_substring string substringLength substring
ECHO !string!
ECHO !substring!
EXIT /B 0
ENDLOCAL
:get_substring
SETLOCAL ENABLEDELAYEDEXPANSION
SET "string=!%~1!"
SET "substringLength=!%2!"
SET "nextChar=!string:~%substringLength%,1!"
IF "!nextChar!"==" " (
SET "substring=!string:~0,%substringLength%!"
ECHO !substring!
SET /A cutSpaceCount=!substringLength!+1
for /F %%c in ("!cutSpaceCount!") do SET "string=!string:~%%c!"
ECHO !string!
for /F "delims=" %%s in ("!string!") do for /F "delims=" %%b in ("!substring!") do (
ENDLOCAL & SET "%1=%%s" & SET "%3=%%b" & EXIT /B 0
)
) ELSE (
ECHO Some other case
)
PS - You don't need to expand variable values in SET /A command. Instead of:
SET /A cutSpaceCount=!substringLength!+1
you may simply use:
SET /A cutSpaceCount=substringLength+1
You seem to be very confused about the sequence of operations that occurs when delayed expansion has been invoked.
First, the value of var is substituted for %var%.
Then !var! is evaluated using the results.
The scope of this sequence of operations is one logical line, which may be one physical line or any number of physical lines continued with a terminal ^ or more commonly using a parenthesised sequence of lines.
In your mainline then,
CALL :get_substring string substringLength substring
ECHO !string!
ECHO !substring!
ENDLOCAL & SET %1=%string% & SET %3=%substring% & EXIT /B 0
Since these statements are not within the same logical line, they will be individually evaluated, so !var!==%var%.
Within your subroutine (non-IF version),
SET substring=!string:~0,%substringLength%!
ECHO !substring!
SET /A cutSpaceCount=!substringLength!+1
SET string=!string:~%cutSpaceCount%!
ECHO !string!
again are individual statements. The first set will first substitute for substringlength, and then execute SET substring=!string:~0,5! as a second operation.
Each of the echoes is a stand-alone statement, and the ! could (and preferably should) be replaced by %.
The set /a statement - well, set /a allows the current value of a variable to be used undecorated, so SET /A cutSpaceCount=substringLength+1 or SET /A cutSpaceCount=%substringLength%+1could be used here with no logical effect.
ENDLOCAL & SET %1=%string% & SET %3=%substring% & EXIT /B 0 will be evaluated according to the values established by the previous code-sequence.
However when you add the if, the code is parenthesised and thus becomes one logical statement and acts differently.
The echoes then require ! because you want to display the modified values within the code-block. Since cutSpaceCount is not set at the start of the code-block, SET string=!string:~%cutSpaceCount%! will be evaluated as SET string=!string:~!
and then ENDLOCAL & SET %1=%string% & SET %3=%substring% & EXIT /B 0 will duly substitute the values of the variables as they stood when the IFwas encountered
So, a replacement routine might be
:get_substring
SETLOCAL ENABLEDELAYEDEXPANSION
SET "string=!%~1!"
SET "substringLength=!%2!"
SET "substring=!string:~0,%substringLength%!"
SET "string=!string:~%substringLength%!"
IF "%string:~0,1%"==" " SET "string=%string:~1%"
ENDLOCAL & SET "%1=%string%" & SET "%3=%substring%"
EXIT /B 0
As others already explained, the problem is the line:
SET string=!string:~%cutSpaceCount%!
because you use immediate expansion (%) for variable cutSpaceCount which is changed in the same logical line/block of code.
A possible solution is to use call like this:
call set "string=%%string:~!cutSpaceCount!%%"
call introduces another variable expansion phase, so the sequence goes as follows:
immediate expansion phase where %% becomes %:
call set "string=%string:~!cutSpaceCount!%"
then delayed expansion occurs (let us assume a sample value of 5):
call set "string=%string:~5%"
another immediate expansion phase introduced by call to finally get %string:~5%.
(set /a "m1=1,m2=2")
for /f %%c in ("%m1%%m2%") do echo %%c
pause
The brackets else where than due to the for command are used in cases, a space key should have been added.
The echo of the for command, is 12. I used the number characters to face the set /A command with decimal Expression.
When i try the same procedure only with a set for a Shell, may also be named m1 it is just possible without comma seperation.
With set command the m1 Expression would be 1 m2 2 and not two values like with a set /A SET.
Is there a way to use set only once and not only with the set /A?
As other answers and comments already indicated, there is no way to directly do this in one command, but via a procedure. The method below is the simplest one:
#echo off
rem Define the several values
set "vars=m1=1,m2=2"
rem Do it:
set "%vars:,=" & set "%"
echo m1=%m1%
echo m2=%m2%
You may remove the #echo off command and execute this program to see what exactly is executed...
As I understand the question you want something like:
set x=1,y=2
and as a result to have two variables (like set /a). The answer is no.
Though you can iterate trough expressions with plain for :
#echo off
for %%a in (
"x=1" "a=5"
"y=2" "b=6"
"z=3" "c=7"
) do set "%%~a"
echo %x% %y% %z% %a% %b% %c%
Mind that the quotes around the items are mandatory because = is a delimiter. You can put everything on line and to use as separators , ,; ,<space>
May be with a lot of variables this can save you from some writing...?
this can be rewritten like this:
#echo off
set "vars=x=1,y=2,z=3,a=5,b=6,c=7"
for %%a in ("%vars:,=","%") do set "%%~a"
echo %x% %y% %z% %a% %b% %c%
And thus you'll need to change only the vars value.
So i'm creating a batch file, that displays your directory and thats just one feature but anyways, you'll directory is going to change while in the batch file, and the therefore the directory text is going to change...
So here's an example of what it does...
|----------------------------------|
|>>C:\Users\Joel\..................|
|----------------------------------|
When you change your directory it looks like:
|----------------------------------|
|>> C:\Users\Joel\Desktop\.................|
|----------------------------------|
How do I make it so it takes how ever many letters than takes it away from the spaces?
Please help?
You want to pad a string to a fixed length. The simple strategy is to create a variable containing your string plus more than enough pad characters to reach your limit. Then use a substring operation to trim the string to the desired length. I modified the algorithm slightly to preserve the entire string if it is already greater than or equal to the desired length.
#echo off
:: Initialize
setlocal enableDelayedExpansion
set maxLen=50
set "pad="
set "div="
for /l %%N in (1 1 %maxLen%) do (
set "pad=!pad!."
set "div=!div!-"
)
set "div=|!div!|"
:: Test the display
pushd "c:\Users\Joel"
call :displayCurrentDirectory
pushd "c:\Users\Joel\Desktop"
call :displayCurrentDirectory
exit /b
:displayCurrentDirectory
setlocal
set "txt=>>!cd!\"
if "!txt:~%maxLen%,1!" equ "" (
set "txt=!txt!!pad!"
set "txt=!txt:~0,%maxLen%!"
)
echo !div!
echo ^|!txt!^|
echo !div!
echo(
exit /b
Here is a version that uses multiple lines of fixed width to force the string to fit within the alloted horizontal space.
#echo off
:: Initialize
setlocal enableDelayedExpansion
set maxLen=15
set "pad="
set "div="
for /l %%N in (1 1 %maxLen%) do (
set "pad=!pad!."
set "div=!div!-"
)
set "div=|!div!|"
:: Test the display
pushd "c:\Users\Joel"
call :displayCurrentDirectory
pushd "c:\Users\Joel\Desktop"
call :displayCurrentDirectory
exit /b
:displayCurrentDirectory
setlocal
echo !div!
set "txt=>>!cd!\"
:loop
if "!txt:~0,%maxLen%!" neq "!txt!" (
echo ^|!txt:~0,%maxLen%!^|
set "txt=!txt:~%maxLen%!"
goto :loop
)
set "txt=!txt!!pad!"
set "txt=!txt:~0,%maxLen%!"
echo ^|!txt!^|
echo !div!
echo(
exit /b
Take a look at Improved :Format, new :FormatVar and :FormatColor functions
for a more general purpose routine to format text. It allows left and right justification of text.
I've a text file with two rows (say param.txt) which is shown below:
Mar2012
dim1,dim2,dim3,dim4
I want to read this file in batch and store the contents of first line in a variable called cube_name. When I'm reading the second line, I want to split the comma delimited string dim1,dim2,dim3,dim4 and create an array of four elements. I am planning to use the variable and the array in later part of the script.
The code which I created is shown below. The code is not working as expected.
#echo off & setlocal enableextensions enabledelayedexpansion
set /a count_=0
for /f "tokens=*" %%a in ('type param.txt') do (
set /a count_+=1
set my_arr[!count_!]=%%a
)
set /a count=0
for %%i in (%my_arr%) do (
set /a count+=1
if !count! EQU 1 (
set cube_name=%%i
)
if !count! GTR 1 (
set dim_arr=%%i:#=,%
)
)
for %%i in (%dim_arr%) do (
echo %%i
)
echo !cube_name!
I get to see the following when I run the code:
C:\Working folder>test2.bat
ECHO is off.
So this doesn't appear to work and I can't figure out what I'm doing wrong. I am fairly new to the batch scripting so help is appreciated
Your first FOR loop is OK. It is not how I would do it, but it works. Everything after that is a mess. It looks like you think arrays are a formal concept in batch, when they are not. It is possible to work with variables in a way that looks reminiscent of arrays. But true arrays do not exist within batch.
You use %my_arr% as if it is an array, but my_arr is not even defined. You have defined variables my_arr[1] amd my_arr[2] - the brackets and number are part of the variable name.
It also looks like you have a misunderstanding of FOR loops. I suggest you carefully read the FOR documentation (type HELP FOR from a command line). Also look at examples on this and other sites. The FOR command is very complicated because it has many variations that look similar to the untrained eye, yet have profoundly different behaviors. One excellent resource to help your understanding is http://judago.webs.com/batchforloops.htm
Assuming the file always has exactly 2 lines, I would solve your problem like so
#echo off
setlocal enableDelayedExpansion
set dimCnt=0
<param.txt (
set /p "cube_name=" >nul
set /p "dimList=" >nul
for %%D in (!dimList!) do (
set /a dimCnt+=1
set "dim[!dimCnt!]=%%D"
)
)
echo cube_name=!cube_name!
for /l %%I in (1 1 !dimCnt!) do echo dim[%%I]=!dim[%%I]!
One nice feature of the above solution is it allows for a varying number of terms in the list of dimensions in the 2nd line. It will fail if there are tabs, spaces, semicolon, equal, * or ? in the dimension names. There are relatively simple ways to get around this limitation if need be.
Tabs, spaces, semicolon and equal can be handled by using search and replace to enclose each term in quotes.
for %%D in ("!dimList:,=","!") do (
set /a dimCnt+=1
set "dim[!dimCnt!]=%%~D"
)
I won't post the full solution here since it is not likely to be needed. But handling * and/or ? would require replacing the commas with a new-line character and switching to a FOR /F statement.
I'm impressed of your code!
Do you try to debug or echo anything there?
You could simply add some echo's to see why your code can't work.
#echo off & setlocal enableextensions enabledelayedexpansion
set /a count_=0
for /f "tokens=*" %%a in ('type param.txt') do (
set /a count_+=1
set my_arr[!count_!]=%%a
)
echo ### show the variable(s) beginning with my_arr...
set my_arr
echo Part 2----
set /a count=0
echo The value of my_arr is "%my_arr%"
for %%i in (%my_arr%) do (
set /a count+=1
echo ## Count=!count!, content is %%i
if !count! EQU 1 (
set cube_name=%%i
)
if !count! GTR 1 (
echo ## Setting dim_arr to "%%i:#=,%"
set dim_arr=%%i:#=,%
echo
)
)
for %%i in (%dim_arr%) do (
echo the value of dim_arr is "%%i"
)
echo cube_name is "!cube_name!"
Output is
### show the variable(s) beginning with my_arr...
my_arr[1]=Mar2012
my_arr[2]=dim1,dim2,dim3,dim4
Part 2----
The value of my_arr is ""
cube_name is ""
As you can see your part2 fails completly.
In my batch file on Windows XP, I want to use %* to expand to all parameters except the first.Test file (foo.bat):
#echo off
echo %*
shift
echo %*
Call:
C:\> foo a b c d e f
Actual result:
a b c d e f
a b c d e f
Desired result:
a b c d e f
b c d e f
How can I achieve the desired result? Thanks!!
Wouldn't it be wonderful if CMD.EXE worked that way! Unfortunately there is not a good syntax that will do what you want. The best you can do is parse the command line yourself and build a new argument list.
Something like this can work.
#echo off
setlocal
echo %*
shift
set "args="
:parse
if "%~1" neq "" (
set args=%args% %1
shift
goto :parse
)
if defined args set args=%args:~1%
echo(%args%
But the above has problems if an argument contains special characters like ^, &, >, <, | that were escaped instead of quoted.
Argument handling is one of many weak aspects of Windows batch programming. For just about every solution, there exists an exception that causes problems.
That´s easy:
setlocal ENABLEDELAYEDEXPANSION
set "_args=%*"
set "_args=!_args:*%1 =!"
echo/%_args%
endlocal
Same thing with comments:
:: Enable use of ! operator for variables (! works as % after % has been processed)
setlocal ENABLEDELAYEDEXPANSION
set "_args=%*"
:: Remove %1 from %*
set "_args=!_args:*%1 =!"
:: The %_args% must be used here, before 'endlocal', as it is a local variable
echo/%_args%
endlocal
Example:
lets say %* is "1 2 3 4":
setlocal ENABLEDELAYEDEXPANSION
set "_args=%*" --> _args=1 2 3 4
set "_args=!_args:*%1 =!" --> _args=2 3 4
echo/%_args%
endlocal
Remarks:
Does not work if any argument contains the ! or & char
Any extra spaces in between arguments will NOT be removed
%_args% must be used before endlocal, because it is a local variable
If no arguments entered, %_args% returns * =
Does not shift if only 1 argument entered
Don't think there's a simple way to do so. You could try playing with the following workaround instead:
#ECHO OFF
>tmp ECHO(%*
SET /P t=<tmp
SETLOCAL EnableDelayedExpansion
IF DEFINED t SET "t=!t:%1 =!"
ECHO(!t!
Example:
test.bat 1 2 3=4
Output:
2 3=4
Another easy way of doing this is:
set "_args=%*"
set "_args=%_args:* =%"
echo/%_args%
Remarks:
Does not work if first argument (%1) is 'quoted' or "double quoted"
Does not work if any argument contains the & char
Any extra spaces in between arguments will NOT be removed
I had to do this recently and came up with this:
setlocal EnableDelayedExpansion
rem Number of arguments to skip
set skip=1
for %%a in (%*) do (
if not !position! lss !skip! (
echo Argument: '%%a'
) else (
set /a "position=!position!+1"
)
)
endlocal
It uses loop to skip over N first arguments. Can be used to execute some command per argument or build new argument list:
setlocal EnableDelayedExpansion
rem Number of arguments to skip
set skip=1
for %%a in (%*) do (
if not !position! lss !skip! (
set args=!args! %%a
) else (
set /a "position=!position!+1"
)
)
echo %args%
endlocal
Please note that the code above will add leading space to the new arguments. It can be removed like this:
How to remove trailing and leading whitespace for user-provided input in a batch file?
Yet another obnoxious shortcoming of DOS/Windows batch programming...
Not sure if this is actually better than some of the other answers here but thought I'd share something that seems to be working for me. This solution uses FOR loops rather than goto, and is contained in a reusable batch script.
Separate batch script, "shiftn.bat":
#echo off
setlocal EnableDelayedExpansion
set SHIFTN=%1
FOR %%i IN (%*) DO IF !SHIFTN! GEQ 0 ( set /a SHIFTN=!SHIFTN! - 1 ) ELSE ( set SHIFTEDARGS=!SHIFTEDARGS! %%i )
IF "%SHIFTEDARGS%" NEQ "" echo %SHIFTEDARGS:~1%
How to use shiftn.bat in another batch script; in this example getting all arguments following the first (skipped) arg:
FOR /f "usebackq delims=" %%i IN (`call shiftn.bat 1 %*`) DO set SHIFTEDARGS=%%i
Perhaps someone else can make use of some aspects of this solution (or offer suggestions for improvement).
Resume of all and fix all problems:
set Args=%1
:Parse
shift
set First=%1
if not defined First goto :EndParse
set Args=%Args% %First%
goto :Parse
:EndParse
Unsupport spaces between arguments: 1 2 3 4 will be 1 2 3 4