I want to uppercase just the first character in my string with the windows batch file.
set foo="bar"
//uppercase first character
echo %foo%;
Expected Output: Bar
#ECHO OFF
SETLOCAL
SET "foo=bar"
SET foo
:: get first character
SET "c1=%foo:~0,1%"
:: make it uppercase. Note that the string-substitution is case-insensitive on the
:: "from" string, but replaces literally.
:: Obviously, A B C should be the full alphabet, which I assign as a user-defined environment string
:: bizarrely called "alphabet"
FOR %%s IN (A B C) DO CALL SET "c1=%%c1:%%s=%%s%%"
SET "foo=%c1%%foo:~1%"
SET foo
GOTO :EOF
Assigning quoted strings to variables make the variables hard to combine logically. Inserting quotes as needed is far simpler.
The syntax SET "var=value" (where value may be empty) is used to ensure that any stray trailing spaces are NOT included in the value assigned.
The magic is in the line
FOR %%s IN (A B C) DO CALL SET "c1=%%c1:%%s=%%s%%"
which will attempt to execute "substitute literal %%s for each %%s (case-insensitive) found in the string c1. In this case, I chose to invoke the call of the set in a sub-shell to avoid the complexities of the delayed expansion saga.
With the help of #Magoo answer, I have written a simple logical script to achieve the same.
#echo off & SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
SET STR=bar
:: Taking the rest of the string as it is as we don't need to change that.
set "restStr=%STR:~1,20%"
:: Taking the first character of the string and doing it to upper case.
set upper=
set "str=%STR:~0,1%"
for /f "skip=2 delims=" %%I in ('tree "\%str%"') do if not defined upper set "upper=%%~I"
set "upper=%upper:~3%"
:: Printing the result by concating both of them.
echo %upper%%restStr%
pause
Output is : Bar
Related
I'm trying to remove an asterisk from an environmental variable string, but can't seem to do it.
I'm creating an m3u file based around search strings, so for instance I if I want to make an m3u file containing every song with the word love in it, I would enter:
m3u *Love*
And m3u.bat would create the file:
xLovex.m3u
But the regular method of replacing characters does not work with an asterisk. (Though I don't have that problem with the question mark.)
set nam=%nam:*=x%.m3u
Instead creates the filename
x.m3u
The easy answer is no.
The problem that you're encountering stems from the fact that the asterisk * is a special character when used with the SET search and replace method. It matches multiple characters in a limited, but still useful, way. You can learn about that here.
The hard answer is Yes!
I will provide you with two solutions. One an incomplete solution but elegent,
the other complete and inelegent.
Both methods will search for * and replace it with an x.
Both methods will both search and modify the following string:
*love*
The first method that comes to mind is using a 'FOR /L' statement, and requires that you know how many characters long the environmental variable is.
::Major Edit::
I thought I knew the various maximum size strings of environmental variables, but dbenham has taken me to school, shown me a kick-in-the-behind length function, and in the mean time completely reversed my opinions of the two solutions I'm presenting.
Other than for the Windows 95/98/ME limitation of a 256 Character maximum environmental variable size. It seems that all versions of Windows using CMD.EXE have a limitation of 8,192 characters, well below what the documentation suggests.
Both versions require delayed environmental variable expansion, but for two different reasons. One because I'm operating inside a FOR statement. The other because you cannot put a % pair inside another % pair because the command processor matches the second % that it encounters to the first one it encounters, but we need to use a variable inside another variable expression. (You'll see.)
This solution uses the strLen function (in line 3) from DosTips.com that can be found Here. Just slap it into a file called strLen.bat and be amazed at it's speed!
Solution 1: (FOR /L Solution) :: Preferred Solution ::
setlocal ENABLEDELAYEDEXPANSION
set nam=*love*
rem calling strLen
call :strLen nam len
for /l %%x in (0,1,%len%) do if not "!nam:~%%x,1!"=="" if "!nam:~%%x,1!"=="*" (
set /a plusone=%%x+1
for /l %%y in (!plusone!, 1, !plusone!) do (
set nam=!nam:~0,%%x!x!nam:~%%y!
)
)
echo %nam%
ENDLOCAL
I think this is a quick and elegant solution It could be sped up by adding the contents of strLen.bat to the routine, but I wanted no confusion as to the author.
If you, for some reason, do not wish to use strLen, then the next quickest method would probably use a GOTO loop.
Solution 2: (Goto Solution)
setlocal ENABLEDELAYEDEXPANSION
set nam=*love*
set num=0
:loop
set /a plusone=%num%+1
if "!nam:~%num%,1!"=="*" set nam=!nam:~0,%num%!x!nam:~%plusone%!
set /a num=%num%+1
if not "!nam:~%num%,1!"=="" goto :loop
echo %nam%
EndLocal
Special thanks to dbenham for pointing out the strLen function. It works faster than any batch based function has a right to!
Although there were already some very good and robust ways explained here, I'd still like to add another option for the sake of completion.
It's not as good as the other options but I personally use it in some cases where I'd like to keep the code clean and where I know that it will suffice:
The way it works is by using for /f's delims to cut the string into two parts, which are then put back together, getting rid of the * in the process:
for /f "tokens=1,* delims=*" %%a in ("a*b") do (set string=%%a%%b)
>>> string=ab
Obviously, the downside to this is that it can only be used to remove one *.
To remove more, we can either just use more tokens...
for /f "tokens=1-3,* delims=*" %%a in ("a*b*c*d") do (set string=%%a%%b%%c%%d)
>>> string=abcd
... or we can put the first line in a for /l-loop:
setlocal enableDelayedExpansion
set string=a*b*c*d
for /l %%a in (1, 1, 3) do (
for /f "tokens=1,* delims=*" %%b in ("!string!") do (set string=%%b%%c)
)
>>> string=abcd
Another thing to note is that you can define more than one character in delims, and they will all be removed at once:
for /f "tokens=1,* delims=+-*/" %%a in ("a*-/+b") do (set string=%%a%%b)
>>> string=ab
Another solution to the stated problem is to use a PowerShell replace command within your batch script.
set var=*Love*
echo %var%>var.txt | powershell -command "((get-content var.txt) -replace '[\x2A]','x') -replace '.{1}$' | set-content var.txt"
set /p var=<var.txt
set var=%var%.m3u
echo %var%
In the above code, the second line
writes your string into a text file
calls a PowerShell command to get the contents of that file
replaces the * character with null
overwrites the text file with the new value
Once that is done, you read the value back into your variable.
To further explain the replace command, the first single quotes is what you are searching for. We are using square brackets to identify the * character as a hex character (\x2A is the hex value for *). After the comma, the second set of single quotes contains no value so that the searched object is removed. To prevent a space between xLovex and the .m3u, we have to use -replace '.{1}$' before writing the result to the text file.
Once you are done with the text file, enter a line to delete it.
if exist var.txt del var.txt
Here is an approach that does not walk through all characters of a string, but it uses a for /F loop to split the string at every occurrence of a (sequence of a) certain character. The actual functionality is packed into a sub-routine for easy reuse, so the main section of the following script just contains some code to test:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
::This is the main routine of the script holding code for test and demonstration:
rem // Definition of some sample text to test (note that `%%` becomes one literal `%`):
set "DATA=some text,"^&"&;0'#%%~#`$:wild**card*?.re<dir>=|+([{parens}])-^/equal==to=!_"
echo/
call :REPL_CHAR TEXT DATA "*" "?"
setlocal EnableDelayedExpansion
echo(In: !DATA!
echo(Out:!TEXT!
echo/
echo(In: !TEXT!
call :REPL_CHAR TEXT TEXT "=" "/"
echo(Out:!TEXT!
endlocal
endlocal
exit /B
:REPL_CHAR
::This function replaces in a string every occurrence of a sequence of a certain character
::by another character or a string. It even correctly handles the characters `*` and `=`.
:: USAGE:
:: call :REPL_CHAR ref_output_string ref_input_string val_search_char val_replace_char
:: PARAMETERS:
:: ref_output_string reference to (name of) variable to receive the resulting string;
:: ref_input_string reference to variable that holds the original string; if empty
:: (`""`), the variable referenced by `ref_output_string` is used;
:: val_search_char single character that is to be replaced;
:: val_replace_char character or string to replace every sequence of `val_search_char`
:: with; this may even be empty;
rem // Localise environment and detect whether delayed expansion is enabled (needed later):
setlocal & set "$NDX=!"
setlocal DisableDelayedExpansion
rem // Fetch arguments and verify them:
set "#RET=%~1" & if not defined #RET endlocal & endlocal & exit /B 2
set "#STR=%~2" & if not defined #STR set "#STR=%#RET%"
set "CHR=%~3"
if not defined CHR endlocal & endlocal & exit /B 1
set "RPL=%~4"
setlocal EnableDelayedExpansion
rem // Initialise several auxiliary variables:
set "TST=!%#STR%!" & set "CHR=!CHR:~,1!" & set "INS="
if "!CHR!"=="_" (set "BUF=#" & set "WRK=!TST!#") else (set "BUF=_" & set "WRK=!TST!_")
:REPL_CHAR_LOOP
rem // Check whether the end of the string has been reached:
if not defined TST set "BUF=!BUF:~1,-1!" & goto :REPL_CHAR_NEXT
rem // Split the string at the next sequence of search characters:
for /F tokens^=1*^ delims^=^%CHR%^ eol^=^%CHR% %%S in ("!BUF!!INS!!WRK!") do (
rem // Store the portions before and after the character sequence:
endlocal & set "BUF=%%S" & set "TST=%%T" & set "WRK=%%T" & setlocal EnableDelayedExpansion
)
rem // Loop back and find the next character sequence:
set "INS=!RPL!" & goto :REPL_CHAR_LOOP
:REPL_CHAR_NEXT
rem // Return the resulting string with all special characters properly handled:
if not defined $NDX if defined BUF set "BUF=!BUF:"=""!^"
if not defined $NDX if defined BUF set "BUF=!BUF:^=^^^^!"
if not defined $NDX if defined BUF set "BUF=%BUF:!=^^^!%" !
if not defined $NDX if defined BUF set "BUF=!BUF:""="!^"
for /F "delims=" %%S in (^""!BUF!"^") do endlocal & endlocal & endlocal & set "%#RET%=%%~S" !
exit /B
The input and output data of this script (let us call it repl_char_demo.bat) are:
>>> repl_char_demo.bat
In: some text,"&"&;0'#%~#`$:wild**card*?.re<dir>=|+([{parens}])-^/equal==to=!_
Out:some text,"&"&;0'#%~#`$:wild?card??.re<dir>=|+([{parens}])-^/equal==to=!_
In: some text,"&"&;0'#%~#`$:wild?card??.re<dir>=|+([{parens}])-^/equal==to=!_
Out:some text,"&"&;0'#%~#`$:wild?card??.re<dir>/|+([{parens}])-^/equal/to/!_
This is a script that uses for /L loops to walk through all characters of the string, to check each character against a predefined one and replaces it as specified. This method replaces every single matching character rather than sequences. Again the functionality is put into a sub-routine (the main section is dismissed this time):
:REPL_CHAR
::This function replaces in a string every occurrence of one certain character by another
::character or a string. It even correctly handles the characters `*` and `=`, as well as
::sequences of search characters so that every single one becomes replaced.
:: USAGE:
:: call :REPL_CHAR ref_output_string ref_input_string val_search_char val_replace_char
:: PARAMETERS:
:: ref_output_string reference to (name of) variable to receive the resulting string;
:: ref_input_string reference to variable that holds the original string; if empty
:: (`""`), the variable referenced by `ref_output_string` is used;
:: val_search_char single character that is to be replaced;
:: val_replace_char character or string to replace every single `val_search_char`
:: with; this may even be empty;
rem // Localise environment and detect whether delayed expansion is enabled (needed later):
setlocal & set "$NDX=!"
setlocal DisableDelayedExpansion
rem // Fetch arguments and verify them:
set "#RET=%~1" & if not defined #RET endlocal & endlocal & exit /B 2
set "#STR=%~2" & if not defined #STR set "#STR=%#RET%"
set "CHR=%~3"
if not defined CHR endlocal & endlocal & exit /B 1
set "RPL=%~4"
setlocal EnableDelayedExpansion
rem // Initialise several auxiliary variables:
set "WRK=!%#STR%!" & set "CHR=!CHR:~,1!" & set "BUF="
rem // Loop through all characters and check for match:
if defined WRK for /L %%J in (0,1,63) do for /L %%I in (0,1,127) do (
set /A "POS=%%J*64+%%I" & for %%P in (!POS!) do (
set "TST=!WRK:~%%P,1!" & if not defined TST goto :REPL_CHAR_QUIT
rem // Store character or replacement depending on whether there is a match:
if "!TST!"=="!CHR!" (set "BUF=!BUF!!RPL!") else (set "BUF=!BUF!!TST!")
)
)
:REPL_CHAR_QUIT
rem // Return the resulting string with all special characters properly handled:
if not defined $NDX if defined BUF set "BUF=!BUF:"=""!^"
if not defined $NDX if defined BUF set "BUF=!BUF:^=^^^^!"
if not defined $NDX if defined BUF set "BUF=%BUF:!=^^^!%" !
if not defined $NDX if defined BUF set "BUF=!BUF:""="!^"
for /F "delims=" %%S in (^""!BUF!"^") do endlocal & endlocal & endlocal & set "%#RET%=%%~S" !
exit /B
There are actually two nested for /L loops rather than a single one, both of which become broken as soon as the end of the string is reached, using the goto command. Breaking a for /L loop means that it completes iterating in the background although its body is no longer executed. Therefore, using a single loop takes much more time to finish after being broken rather than two nested ones.
The input and output data of this script (with the same main section as above) are:
>>> repl_char_demo.bat
In: some text,"&"&;0'#%~#`$:wild**card*?.re<dir>=|+([{parens}])-^/equal==to=!_
Out:some text,"&"&;0'#%~#`$:wild??card??.re<dir>=|+([{parens}])-^/equal==to=!_
In: some text,"&"&;0'#%~#`$:wild??card??.re<dir>=|+([{parens}])-^/equal==to=!_
Out:some text,"&"&;0'#%~#`$:wild??card??.re<dir>/|+([{parens}])-^/equal//to/!_
See this answer, and with set-ast.bat you'll want to put set-ast nam "x" in your file where needed.
set-ast takes the parameters <variable-to-modify> <string-to-replace-asterisks-with>
I have a parameter string read from a properties file. One of the properties is as below:
"CUSTOM_JAVA_OPTIONS=-Dhttp.proxyHost=webcache.example.com -Dhttp.proxyPort=8080"
I need to split this string on the first occurrence of "=" and set a parameter with the value:
-Dhttp.proxyHost=webcache.example.com -Dhttp.proxyPort=8080
I am trying to split the string first on the = token and then remove the fist sub-string token from the original string.
In the below code %%G will be set to "CUSTOM_JAVA_OPTIONS" and I am trying to remove this from the original string "TESTSTR"
#echo off
set "TESTSTR=CUSTOM_JAVA_OPTIONS=-Dhttp.proxyHost=webcache.example.com
-Dhttp.proxyPort=8080"
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /F "tokens=1,2 delims==" %%G IN ("%TESTSTR%") DO (
echo Name=%%G
echo Value=%%H
set removestr=%%G
echo TESTSTR=!TESTSTR!
echo removestr=!removestr!
set "str=!TESTSTR:%removestr%=!"
echo str=!str!
)
pause
The above does not seem to work, it produces:
Name=CUSTOM_JAVA_OPTIONS
Value=-Dhttp.proxyHost
TESTSTR=CUSTOM_JAVA_OPTIONS=-Dhttp.proxyHost=webcache.example.com -Dhttp.proxyPort=8080
removestr=CUSTOM_JAVA_OPTIONS
str=TESTSTR:=
Expected result needs to be:
str=-Dhttp.proxyHost=webcache.example.com -Dhttp.proxyPort=8080
This could be simplified to:
#echo off
set "TESTSTR=CUSTOM_JAVA_OPTIONS=-Dhttp.proxyHost=webcache.example.com -Dhttp.proxyPort=8080"
FOR /F "tokens=1* delims==" %%G IN ("%TESTSTR%") DO set "str=%%H"
echo TESTSTR=%TESTSTR%
echo.str=%str%
pause
There are 2 tokens:
1. Text up to 1st delimiter
2. Everything else after first delimiter (*)
Note that by echoing the variables outside of the FOR loop you don't need to enable delayed expansion.
Your code fails entirely because %removestr% is expanded when the command is initially parsed, and your entire loop (code block) is parsed all at once. So %removestr% expands to the value that existed before your loop was entered. In your case, the variable is undefined. So !TESTSTR:%removestr%=! becomes !TESTSTR:=!, which finally becomes TESTSTR:=.
You get closer if you use %%G directly, instead of assigning an environment variable.
set str=!TESTSTR:%%G=! yields =-Dhttp.proxyHost=webcache.example.com -Dhttp.proxyPort=8080
You can then use set str=!str:~1!" to remove the leading =.
set str=!TESTSTR:%%G==! will not work because the search strings stops at the first occurrence of =, so the result is ==-Dhttp.proxyHost=webcache.example.com -Dhttp.proxyPort=8080
The RGuggisberg answer is the most convenient method to get your desired result. (You may want both %%G and %%H).
However, it technically does not break at the first =. It actually breaks at the first string of contiguous = because FOR /F does not parse empty tokens.
So for /f "tokens=1* delims==" %%G in ("A==B==C") yields A for %%G (correct), and B==C (incorrect) for %%H. The correct value should be =B==C.
If the first character after the = character is always a -, then the following method may also work for you:
#Echo Off
Set "TESTSTR=CUSTOM_JAVA_OPTIONS=-Dhttp.proxyHost=webcache.example.com -Dhttp.proxyPort=8080"
Set "REST=-%TESTSTR:*-=%"
Set "FIRST=%TESTSTR:-="&:"%"
Set "FIRST=%FIRST:~,-1%"
Echo [%FIRST%] [%REST%] & Pause
The bottom line is simply to show you the information.
How to replace the string 1234567890 by !##$%^&*() in Windows batch file?
set temp_mm=1234567890
echo befor:%temp_mm%
set temp_mm=%temp_mm:1=!%
set temp_mm=%temp_mm:2=#%
set temp_mm=%temp_mm:3=#%
set temp_mm=%temp_mm:4=$%
set temp_mm=%temp_mm:5=^%%
set temp_mm=%temp_mm:6=^^%
set temp_mm=%temp_mm:7=^&%
set temp_mm=%temp_mm:8=^*%
set temp_mm=%temp_mm:9=^(%
set temp_mm=%temp_mm:0=^)%
echo after:%temp_mm%
#ECHO Off
SETLOCAL
set temp_mm=12345678901234567890
echo befor:%temp_mm%
set "temp_mm=%temp_mm:1=!%"
set "temp_mm=%temp_mm:2=#%"
set "temp_mm=%temp_mm:3=#%"
set "temp_mm=%temp_mm:4=$%"
set "temp_mm=%temp_mm:6=^%"
set "temp_mm=%temp_mm:7=&%"
set "temp_mm=%temp_mm:8=*%"
set "temp_mm=%temp_mm:9=(%"
set "temp_mm=%temp_mm:0=)%"
SETLOCAL enabledelayedexpansion
FOR %%a IN (%%) DO set "temp_mm=!temp_mm:5=%%a!"
ENDLOCAL&SET "temp_mm=%temp_mm%"
echo after:"%temp_mm%"
SET temp_
GOTO :EOF
You appear to have non-ANSI characters in your post.
Personally, I'd use sed since actually using the variable thus-converted may be problematic.
The substitute construct targets the nominated string and replaces it with the second string. This is all well and good for most characters, but fails for some specials (like %) because the syntax doesn't distinguish between % being a substitute character (even when escaped by the normal %) and being end-of-replacement-string. The syntax of "temp_mm=%temp_mm:a=%%" and "temp_mm=%temp_mm:a=%%%" is ambiguous - is it replace with "%" or replace with
escaped "%" or replace with *nothing*, then add "%", "escaped-%" or "%%"?
By using the setlocal/endlocal bracket, you can use two-stage replacement, the first stage sets the replacement string to % as that is the content of %%a, but the initial parsing has then been completed, so % is no longer a special character. The second parsing phase occurs using % as the replacement.
Then all you need is to return the modified variable to the original context.
I have a string which ends with \.. All I want is the string before \.
Ex.:
My String is: D:\Work\WelCome\.
Output String should be: D:\Work\WelCome
Please excuse if you find this question very general. I have search on net of splitting string by special characters but did not get the expected result.
I am not very new to batch scripting but not enough expert.
Being a file/folder path, it is easier to let the os solve it for you than having to split the string (if you only need the left part)
for %%a in ("D:\Work\WelCome\.") do echo %%~fa
In the theoretical case that the ending dot could include aditional data (but no more backslashes), this can be converted to
rem The string with more data
set "myString=D:\Work\WelCome\.blah"
for %%a in ("%myString%.#\..") do echo %%~fa
The aditional # ensures that there is always aditional information (that way the previous dot ending case will still work), the for command will see a file reference and we just need to get the reference to the parent folder.
But note this is only a way to clean a file/folder reference, not a way to split the string on an precise character.
For an easy solution to split the string (or to be more precise, remove the suffix)
#echo off
setlocal enableextensions disabledelayedexpansion
rem The string
set "myString=D:\Work\WelCome\.test"
rem Do the split
set "myString=%myString:\.="&rem %"
set "myString=%myString%"
rem Show data
echo %myString%
It replaces the splitting substring with a command concatenation and a comment start (rem). So
set "myString=D:\Work\WelCome\.test"
is converted into
set "myString=D:\Work\WelCome"& rem test"
This way, in the next line, when the variable is assigned to itself, the unneeded part will be removed (hidden by the rem).
I have tested this code and it works:
setlocal enabledelayedexpansion
set p=D:\Work\WelCome\.
set v=
:: v is the output
for %%a in (%p:.= %) do (
set "v=!v!.%%~a"
set q=%%~a
if "!q:~-1!"=="\" goto :end
)
:end
set v=%v:~1%
set v=%V:~0,-1%
echo %v%
Here are cases I used:
C:\> split.bat C:\1\2\3\.
C:\1\2\3
C:\> split.bat C:\1\2\3\.4\5\
C:\1\2\3
C:\> split.bat C:\1\2\3\.4\5\.6\7\8
C:\1\2\3
C:\> split.bat C:\1\2.\ .3.\...4\.5
C:\1\2.\ .3.
C:\> split.bat C:\.1\2\3\4\5\6\7\8\9\
C:
Which is what you were looking for. If you want to do more with the variable then just echo it simply refer to %v% following that line.
#ECHO OFF
SETLOCAL
SET "string=D:\Work\WelCome\."
:: Classic solution if the string is in an environment variable
ECHO %string:~0,-2%
:: solution if the string is in a metavariable
FOR %%z IN ("%string%") DO ECHO %%z becomes %%~dpnz
GOTO :EOF
which should be self-explanatory.
my %%x are
a=23
b=56
c=89
in a .txt called hi.txt
for %%x in (hi.txt) do (echo %%x:~0,1 %%x)
but it failed....
thank you
i want to echo "the first character" in each line, but i cant use %%x:~0,1...
I'm not exactly sure what you are trying to do... I'm assuming that you want to parse the file hi.txt and pull out the variables (a,b,c) and the number they are equal to..
#echo off
setlocal ENABLEEXTENSIONS
for /f "tokens=1,2 delims==" %%i in (hi.txt) do (
echo %%i has value %%j
)
this will output
a has value 23
b has value 56
c has value 89
See "help for" if you need more info.
Also note, my example is parsing on the = sign, so it doesn't matter how wide each variable is. You could have a line like "MyText=100" and it would work too. Your ~0,1 assumes each value is only 1 character wide.
I hope this helps.
If all you need to do is print the first character of a line, then the following script should work. I believe your original issue may have been with Delayed Variable Expansion. See "help setlocal" and "help set" for more information.
This version of the script reads each line into a temp variable and snips the first character into another variable named firstCharacter. The ! are used for the delayed variable expansion feature of CMD scripts.
#echo off
setlocal ENABLEEXTENSIONS,ENABLEDELAYEDEXPANSION
for /f %%i in (hi.txt) do (
set wholeLine=%%i
set firstCharacter=!wholeLine:~0,1!
echo First Character is !firstCharacter!
)
endlocal
and the output is...
First Character is a
First Character is b
First Character is c
I wasn't able to find a way to use %%i directly for this, hence the temporary variable wholeLine. I added the variable firstCharacter for clarity, but it doesn't need to exist for this to work. I could have echoed !wholeLine:~0,1! directly.
Have fun.