Is there methods to do the subject? For example, we can't simply replace equal sign usual way through substring replacing syntax %variable:substring1=substring2%, because substring1 can't contain equal sign.
3 Answers
What about a simple loop that walks through the string and checks every character against =?
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "STRING=%~1" & rem // (string from first command line argument)
set "SEARCH==" & rem // (specify a single character here)
set "REPLAC=" & rem // (specify an arbitrary string here)
rem // Check search string for validity (one character):
if not defined SEARCH ((>&2 echo ERROR: no search string defined!) & exit /B 1)
setlocal EnableDelayedExpansion
if not "!SEARCH:~1!"=="" ((>&2 echo ERROR: search string too long^^!) & exit /B 1)
rem // Loop through each character of the string:
set "RESULT="
:LOOP
if not defined STRING goto :QUIT
rem // Compare current character with search string:
set "CHAR=!STRING:~,1!"
if "!CHAR!"=="!SEARCH!" (
rem // Match found, so replace character:
set "RESULT=!RESULT!!REPLAC!"
) else (
rem // No match found, so keep character:
set "RESULT=!RESULT!!CHAR!"
)
rem // Remove processed character from (remaining) string:
set "STRING=!STRING:~1!"
goto :LOOP
:QUIT
rem // Return result here finally:
echo(!RESULT!
endlocal
endlocal
exit /B
This should be a bit better in terms of performance, because there are less string manipulations:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "STRING=%~1" & rem // (string from first command line argument)
set "SEARCH==" & rem // (specify a single character here)
set "REPLAC=" & rem // (specify an arbitrary string here)
rem // Check search string for validity (one character):
if not defined SEARCH ((>&2 echo ERROR: no search string defined!) & exit /B 1)
setlocal EnableDelayedExpansion
if not "!SEARCH:~1!"=="" ((>&2 echo ERROR: search string too long^^!) & exit /B 1)
rem // Loop through each character of the string:
set /A "INDEX=0" & set "RESULT="
if not defined STRING goto :QUIT
:LOOP
rem // Compare currently indexed character with search string:
set "CHAR=!STRING:~%INDEX%,1!"
if not defined CHAR goto :QUIT
if "!CHAR!"=="!SEARCH!" (
rem // Match found, so replace character:
set "RESULT=!RESULT!!REPLAC!"
) else (
rem // No match found, so keep character:
set "RESULT=!RESULT!!CHAR!"
)
rem // Increment character index:
set /A "INDEX+=1"
goto :LOOP
:QUIT
rem // Return result here finally:
echo(!RESULT!
endlocal
endlocal
exit /B
5 Comments
green
Why you use delayed expansion? Is it necessary? If yes, where it can not be replaced with regular
%reference% and why?aschipfl
I'm using it mainly to avoid trouble in case a
" appears in the input string; with normal (immediate) expansion, several command lines became compromised...Taederias
@green In batch, the rule of thumb is that you should always use delayed expansion unless you have an explicit reason not to. Way too many ugly things can happen otherwise if the variable happens to contain any special characters, since the contents are further parsed after expansion. There are a couple use cases where you need to utilize percent expansion, but in general you'd better be damn sure it is safe before doing so.
aschipfl
@Taederias, generally enabling delayed expansion may lead to other issues, namely problems with
! and ^ characters in strings; so the best way is to enable delayed expansion only when it is really needed (so when an environment variable is to be expanded), and to disable it otherwise (like during expansion of for meta-variables, for instance). Anyway, there is no perfect way…Taederias
@aschipfl Well, my preferred method for any script on the complicated side is to store the command line arguments in variables with delayed expansion disabled, and then enable it. Of course,
for variables may indeed still necessitate switching it off (temporarily or otherwise), and even just storing the command line arguments is problematic if they may contain both quoted and naked special characters (though that would become an issue anyway at whatever point you were going to use them). So yeah, batch does not exactly make it easy to write perfect scripts…here's one way (the idea is to split it with for /f and replace the equal signs with same amount of replacements calculating their length with strlen function):
@echo off
rem ===== testing the function =======
set "eqs====abcd=abcd===abcdabcd======~*"
set replace_with=X1
echo %eqs%
call :eqreplacer "%eqs%" %replace_with% res
echo %res%
exit /b %errorlevel%
rem ===============================
:eqreplacer String Replacer [RtnVar]
setlocal
rem the result of the operation will be stored here
set "result=#%~1#"
set "replacer=%~2"
call :strlen0 result wl
call :strlen0 replacer rl
:start
set "part1="
set "part2="
rem splitting the string on two parts
for /f "tokens=1* delims==" %%w in ("%result%") do (
set "part1=%%w"
set "part2=%%x"
)
rem calculating the count replace strings we should use
call :strlen0 part1 p1l
call :strlen0 part2 p2l
set /a iteration_end=wl-p1l-p2l
rem creating a sequence with replaced strings
setlocal enableDelayedExpansion
set "sequence="
for /l %%i in (1,1,%iteration_end%) do (
set sequence=!sequence!%replacer%
)
endlocal & set "sequence=%sequence%"
rem adjust the string length
set /a wl=wl+iteration_end*(rl-1)
rem replacing for the current iteration
set result=%part1%%sequence%%part2%
rem if the second part is empty the task is over
if "%part2%" equ "" (
set result=%result:~1,-1%
goto :endloop
)
goto :start
:endloop
endlocal & if "%~3" neq "" (set %~3=%result%) else echo %result%
exit /b
:strlen0 StrVar [RtnVar]
setlocal EnableDelayedExpansion
set "s=#!%~1!"
set "len=0"
for %%N in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!s:~%%N,1!" neq "" (
set /a "len+=%%N"
set "s=!s:~%%N!"
)
)
endlocal&if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b
here you can find more solutions.Take a look at dbenham's post.