Replace a substring in batch script - batch-file

I have three variables: string, search, replace. I wish to substitute the %search% in %string% with %replace%.
This works but needs hard characters.
SET modified=%string:morning=evening%
This seems to be answer in the forums but does not work. It simply stores the entire line at %modified%
SET modified=!string:%search%=%replace%!

The ! format is doing delayed expansion--the % variables get expanded immediately, but the ! variable gets expanded only when it's needed. I believe that only works in a batch file, so if you're experimenting directly at the command line you won't get the same behaviour as if you are running a batch file.
Make sure to enable delayed expansion in your batch file before using the ! notation, like this:
SETLOCAL ENABLEDELAYEDEXPANSION
SET string=This morning
SET search=morning
SET replace=evening
SET modified=!string:%search%=%replace%!
ECHO %modified%
ENDLOCAL
This will echo This evening.

Related

Using batch echo to write a vbe file (characters are not escaped)

So I'm currently writing a tutorial about security and for that reason I have to write a vbe file (encoded script written in VBScript) using a batch file.
So, I just have to write this to a file:
##~^mgAAAA==6 P3MDKDP"+k;:PH+XY~###&fks~D;EdO{6k^+SPhnk/Co8WX~~AMkYnm6ks+B~T+O|wmYtBPDn:a{2lDtS~6kxms{alY4~###&s+k/Con8K6~',h/T4GavJKndDJ~~8BPEwlDlV,2M.WMJbP###&2zEAAA==^#~#
(Note: There are some characters that cannot be printed above).
But the problem is that I never managed to write it successfully, I tried escaping all the characters using instructions from http://www.robvanderwoude.com/escapechars.php and it didn't work.
I tried using DelayedExpansion like this:
SET "foo=##~^mgAAAA==6 P3MDKDP"+k;:PH+XY~###^&fks~D;EdO{6k^+SPhnk/Co8WX~~AMkYnm6ks+B~T+O|wmYtBPDn:a{2lDtS~6kxms{alY4~###&s+k/Con8K6~',h/T4GavJKndDJ~~8BPEwlDlV,2M.WMJbP###^&2zEAAA==^#~# "
setlocal EnableDelayedExpansion
(
echo !foo!
) > test.vbe
And it did not work either, I have problems with characters that are not escaped.
Any ideas?? Thanks!!
The reason is obvious, that is a quotation mark after [...P3MDKDP]. Since you assign the variable "foo" to jumble characters with a open and a close quotation mark, like so SET "foo=...", batch think you stop assigning "foo" after [...P3MDKDP]. This leaves [+k;:PH+XY~.....] alone, without assigning to a variable or working with commands. Batch can't recognize it, and so the command prompt quit automatically.
What you can do is, assign the part after the quotation mark to another variable, I named it "foo2" in the following example:
#echo off
setlocal enabledelayedexpansion
SET "foo=##~^mgAAAA==6 P3MDKDP""
SET "foo2=+k;:PH+XY~###^&fks~D;EdO{6k^+SPhnk/Co8WX~~AMkYnm6ks+B~T+O|wmYtBPDn:a{2lDtS~6kxms{alY4~###&s+k/Con8K6~',h/T4GavJKndDJ~~8BPEwlDlV,2M.WMJbP###^&2zEAAA==^#~# "
echo !foo!!foo2!>test.vbe
pause >nul
And also, if you add another quotation mark before / after the quotation mark, like so [P3MDKDP ""], even though you did not assign the second part to a new variable, it still work, but it output an extra quotation mark in the string.
maybe this little trick helps you:
#echo off
for /f "delims=[]" %%n in ('find /n "REM DATA:" "%~dpnx0"') do set /a n=%%n
more +%n% "%~dpnx0">test.vbe
REM rest or your batchfile
goto :eof
REM DATA:
##~^mgAAAA==6 P3MDKDP"+k;:PH+XY~###^&fks~D;EdO{6k^+SPhnk/Co8WX~~AMkYnm6ks+B~T+O|wmYtBPDn:a{2lDtS~6kxms{alY4~###&s+k/Con8K6~',h/T4GavJKndDJ~~8BPEwlDlV,2M.WMJbP###^&2zEAAA==^#~#
(this trick avoids any character escaping or splitting the string. Can also be used to write a multiline text)

Batch replace with special characters not working

I'm trying to d a simple batch replace of doublequotes to singlequotes.
The teststring must containg special characters, at most: "<LF>"
I cannot replace the double quotes there, as the batch just exists with Syntaxerror. Do you know why, or how to overcome this?
SET TEST="<LF>","<HT>"
SET modified=%TEST:"='% <-- Syntaxerror
ECHO %modified%
Use delayed expansion:
setlocal enabledelayedexpansion
SET TEST="<LF>","<HT>"
SET modified=!TEST:"='! <-- Syntaxerror
ECHO !modified!
As Mr Fuzzy Button notes, the problem is that the shell interprets < and > as redirection. Delayed expansion (using ! instead of %) expands variables after parsing and thus does not affect redirection.
You can solve the SET without delayed expansion by enclosing the argument in quotes:
SET "modified=!TEST:"='!"
But the ECHO would still be problematic, then.
While delayed expansion is a good thing, it's not necessary to answer this question. There are also times when setlocal isn't available. I ran into one.
In the windows shell, string matching for quotes works on outermost match pairs (in most cases, but not all), not innermost.
SET TEST="<LF>","<HT>"
echo %TEST%
SET "modified=%TEST:"='%"
echo.|set /p "___=%modified%"
Workaround: If echo can't print something, set can. Go figure.
This works for me...
SET TEST="LF","HT"
echo %TEST%
SET modified=%TEST:"='%
ECHO %modified%
The problem lies in your <s and >s being interpreted as input/output pipes in the Echo

How to split string without for loop in batch file

I want to split a string in two parts, without using any for loop.
For example, I have the string in a variable:
str=45:abc
I want to get 45 in a variable and abc in another variable. Is it possible in batch file?
pattern is like somenumber:somestring
You could split the str with different ways.
The for loop, you don't want use it.
The trailing part is easy with the * (match anything until ...)
set "var2=%str:*:=%"
The leading part can be done with a nasty trick
set "var1=%str::="^&REM #%
The caret is needed to escape the ampersand,
so effectivly the colon will be replaced by "&REM #
So in your case you got the line after replacing
set "var1=4567"&REM #abcde
And this is splitted into two commands
set "var1=4567"
REM #abcde`
And the complete code is here:
set "str=4567:abcde"
echo %str%
set "var1=%str::="^&REM #%
set "var2=%str:*:=%"
echo var1=%var1% var2=%var2%
Edit 2: More stable leading part
Thanks Dave for the idea to use a linefeed.
The REM technic isn't very stable against content with quotes and special characters.
But with a linefeed trick there exists a more stable version which also works when the split argument is longer than a single character.
#echo off
setlocal enableDelayedExpansion
set ^"str=456789#$#abc"
for /F "delims=" %%a in (^"!str:#$#^=^
!^") do (
set "lead=%%a"
goto :break
)
:break
echo !lead!
Solution 3: Adpated dbenhams answer
Dbenham uses in his solution a linefeed with a pipe.
This seems a bit over complicated.
As the solution uses the fact, that the parser removes the rest of the line after an unescaped linefeed (when this is found before or in the special character phase).
At first the colon character is replaced to a linefeed with delayed expansion replacement.
That is allowed and the linefeed is now part of the variable.
Then the line set lead=%lead% strips the trailing part.
It's better not to use the extended syntax here, as set "lead=%lead%" would break if a quote is part of the string.
setlocal enableDelayedExpansion
set "str=45:abc"
set ^"lead=!str::=^
!"
set lead=%lead%
echo "!lead!"
You can try this . If its fixed that numbers to left of the colon will be always 2 & to the right will be 3. Then following code should work assuming your str has the value.
set "str=45:abc"
echo %str%
set var1=%str:~0,2%
set var2=%str:~3,3%
echo %var1% %var2%
Keep me posted. :)
It seems pointless to avoid using a FOR loop, but it does make the problem interesting.
As jeb has pointed out, getting the trailing part is easy using !str:*:=!.
The tricky bit is the leading part. Here is an alternative to jeb's solution.
You can insert a linefeed into a variable in place of the : using the following syntax
setlocal enableDelayedExpansion
set "str=45:abc"
echo !str::=^
!
--OUTPUT--
45
abc
The empty line above the last ! is critical.
I'm not sure why, but when the output of the above is piped to a command, only the first line is preserved. So the output can be piped to a FINDSTR that matches any line, and that result directed to a file that can then be read into a variable using SET /P.
The 2nd line must be eliminated prior to using SET /P because SET /P does not recognize <LF> as a line terminator - it only recognizes <CR><LF>.
Here is a complete solution:
#echo off
setlocal enableDelayedExpansion
set "str=45:abc"
echo(!str::=^
!|findstr "^" >test.tmp
<test.tmp set /p "var1="
del test.tmp
set "var2=!str:*:=!"
echo var1=!var1! var2=!var2!
Update
I believe I've mostly figured out why the 2nd line is stripped from the output :)
It has to do with how pipes are handled by Windows cmd.exe with each side being processed by a new CMD.EXE thread. See Why does delayed expansion fail when inside a piped block of code? for a related question with a great answer from jeb.
Just looking at the left side of the piped command, I believe it is parsed (in memory) into a statement that looks like
C:\Windows\system32\cmd.exe /S /D /c" echo {delayedExpansionExpression}"
I use {delayedExpansionExpression} to represent the multi-line search and replace expansion that has not yet occurred.
Next, I think the variable expression is actually expanded and the line is broken in two by the search and replace:
C:\Windows\system32\cmd.exe /S /D /c" echo 43
abc"
Only then is the command executed, and by normal cmd.exe rules, the command ends at the linefeed. The quoted command string is missing the end quote, but the parser doesn't care about that.
The part I am still puzzled by is what happens to the abc"? I would have thought that an attempt would be made to execute it, resulting in an error message like 'abc"' is not recognized as an internal or external command, operable program or batch file. But instead it appears to simply get lost in the ether.
note - jeb's 3rd comment explains why :)
Safe version without FOR
My original solution will not work with a string like this & that:cats & dogs. Here is a variation without FOR that should work with nearly any string, except for string length limits and trailing control chars will be stripped from leading part.
#echo off
setlocal enableDelayedExpansion
set "str=this & that:cats & dogs"
set ^"str2=!str::=^
!^"
cmd /v:on /c echo ^^!str2^^!|findstr /v "$" >test.tmp
<test.tmp set /p "var1="
del test.tmp
set "var2=!str:*:=!"
echo var1=!var1! var2=!var2!
I delay the expansion until the new CMD thread, and I use a quirk of FINDSTR regex that $ only matches lines that end with <cr>. The first line doesn't have it and the second does. The /v option inverts the result.
Yes, I know this is a very old topic, but I just discovered it and I can't resist the temptation of post my solution:
#echo off
setlocal
set "str=45:abc"
set "var1=%str::=" & set "var2=%"
echo var1="%var1%" var2="%var2%"
You may read full details of this method here.
In the Light of people posting all sorts of methots for splitting variables here i might as well post my own method, allowing for not only one but several splits out of a variable, indicated by the same symbol, which is not possible with the REM-Method (which i used for some time, thanks #jeb).
With the method below, the string defined in the second line is split into three parts:
setlocal EnableDelayedExpansion
set fulline=one/two/three or/more
set fulline=%fulline%//
REM above line prevents unexpected results when input string has less than two /
set line2=%fulline:*/=%
set line3=%line2:*/=%
set line1=!fulline:/%line2%=!
set line2=!line2:/%line3%=!
setlocal DisableDelayedExpansion
echo."%line1%"
echo."%line2%"
echo."%line3%"
OUTPUT:
"one"
"two"
"three or/more//"
i recommend using the last so-created partition of the string as a "bin" for the remaining "safety" split-characters.
Here's a solution without nasty tricks for leading piece
REM accepts userID#host
setlocal enableDelayedExpansion
set "str=%1"
set "host=%str:*#=%"
for /F "tokens=1 delims=#" %%F IN ("%str%") do set "user=%%F"
echo user#host = %user%#%host%
endlocal

set fileName and echo to cmd.exe in for loop of batch?

I'd like to put each of the many properties' file names into variable fileName and echo them out to the command prompt window. But only the last properties file name to be cycled thru is printed out as many times as there are properties files. Is there an easy fix to this problem. I know that ...DO echo %%-nxG can do the same thing but I'd like to save the file name in %%~nxG for future use.
FOR %%G IN (C:\ExecutionSDKTest_10.2.2\*.properties) DO (
set fileName=%%~nxG
echo %fileName%
)
You need to use delayed expansion:
setlocal enabledelayedexpansion
FOR %%G IN (C:\ExecutionSDKTest_10.2.2\*.properties) DO (
set fileName=%%~nxG
echo !fileName!
)
Environment variables in cmd are expanded when a command is parsed – in this case this includes the whole block in parentheses. So %fileName% gets replaced by an empty string because it didn't have a value before the loop ran. Delayed expansion uses ! instead of % and changes variable evaluation so that they are evaluated just before a command is run.
help set has more details about why and when it is necessary. In general, whenever you modify and use a variable within a loop you have to use delayed expansion, but it comes with a few other benefits too.

How can I URL-encode spaces in an NT batch file?

I have the misfortune of working with a program which requires all filenames passed into it to be valid URLs. (No, I don't know why.) Rather than having to drop to the command line and hand-craft file: URLs each time, I'm throwing together a batch file onto which I can simply drop files dragged from the Windows GUI.
A full, proper URL encoder is beyond my needs or interest (most of the characters the app chokes on aren't valid in Windows filenames, anyway), but the two cases I do need to solve are backslashes and spaces. Backslashes are easily handled by variable replacement syntax (SET URL=%URL:\=/%), but spaces are tricky — % is special for both URLs and batch files.
Neither type of batch escaping I'm familiar with (^%, %%) allows the variable replacement to behave as desired and I haven't had any success Googling a solution. Can any batch gurus help me out?
Here's what I have so far:
#ECHO OFF
SETLOCAL
SET URLPATH=file:/%~dp1
SET URLPATH=%URLPATH:\=/%
REM none of these work
REM SET URLPATH=%URLPATH: =%20%
REM SET URLPATH=%URLPATH: =%%20%
REM SET URLPATH=%URLPATH: =^%20%
REM works; I just need to figure out how to generate it
SET URLPATH=file:/C:/Documents%%20and%%20Settings/bblank/example.dat
stupidapp.exe %URLPATH%
ENDLOCAL
Side note - I believe you want %~f1 instead of %~dp1
You need to switch over to delayed expansion.
#echo off
setlocal enableDelayedExpansion
set "URLPATH=file:/%~f1"
set "URLPATH=!URLPATH:\=/!"
set "URLPATH=!URLPATH: =%%20!"
stupidapp.exe !URLPATH!
endlocal
A bit more work is required if any of your file names happen to contain the ! character because it will be corrupted when %1 is expanded if delayed expansion is enabled.
#echo off
setlocal disableDelayedExpansion
set "URLPATH=file:/%~f1"
setlocal enableDelayedExpansion
set "URLPATH=!URLPATH:\=/!"
set "URLPATH=!URLPATH: =%%20!"
stupidapp.exe !URLPATH!
endlocal
endlocal
dbenham's solution is almost certainly preferable (being rather easier to read), but for the sake of completeness, here is an alternative solution:
SET URLPATH=file:/%~dp1
SET URLPATH=%URLPATH:\=/%
REM for each space in the path, split the path into the portions before and after
REM that space, then join them with an escaped space
:ESCAPE_SPACE
SET TRAILING=%URLPATH:* =%
CALL SET URLPATH=%%URLPATH: %TRAILING%=%%
SET URLPATH=%URLPATH%%%20%TRAILING%
IF NOT "%URLPATH%"=="%URLPATH: =%" GOTO ESCAPE_SPACE
stupidapp.exe %URLPATH%

Resources