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.
Related
i have a request for theorical knowledge purpose only in Windows Batch scripting.
Today my question is about preparing the value of a local string loc_str INSIDE A BLOCK OF PARENTHESIS and WITHOUT ENABLING DELAYED EXPANSION, in order to return it to a local scope that enables variable expasion (or eventually to prepare it for an incoming local for loop with in("%loc_str%") if nasty characters in loc_str have been properly escaped before).
We assume that loc_str contains at least one ^ and one ! and do not contain double quotes. Lets consider the following code:
#echo off
setlocal disabledelayedexpansion
set "flag_dde_prev=%flag_dde%" & set "flag_dde=!"
(
set "loc_str=Hello^^^ planet!!!! ^^Earth^"
if not defined flag_dde_prev (
call set "out_str=%%loc_str:^=^^%%"
set out_str
call set "out_str=%%out_str:^=^^%%"
set out_str
)
)
endlocal & set "str=%out_str%"
set str
As you know the local flags flag_{dde, dde_prev} are used to test the type of the calling and current scopes. Typically they're both defined at the beginning of a block setlocal..endlocal. The flag_dde is equal to ! if the current scope disables variable expansion, or is undefined
if the current scope enables it. The local value of flag_dde_prev is the inherited value of flag_dde in the calling scope.
What we must do here is to escape all ^ and ! to prepare the return of out_str with %..% to the calling scope when this latter enable variable expansion (ie when flag_dde_prev is defined). Two substitutions ^=^^ then !=^! with a simple set would be enough, but being inside a block (..) forces us to use the call set statement. Unfortunatly the caret ^ is not replaced in the same way they are in a scope that enables variable expansion.
Precisely, the first substitution ^=^^ doubles even sequences of carets, but does not double odd ones (it doubles them minus one).
Then, the second substitution !=^! replace each ! with ^^!.
To sump up,
loc_str=Hello^^^ planet!!!! ^^Earth^ ;init
out_str=Hello^^^^^ planet!!!! ^^^^Earth^ ;1st substitution ^=^^
out_str=Hello^^^^^ planet^^!^^!^^!^^! ^^^^Earth^ :2nd substitution !=^!
^=^^ seems to be reversible with ^^=^ if done BEFORE !=^! only.
If ^^=^ is done after !=^! then sequences of ^ not preceeding ! are modified only.
If ^^=^ is done after !=^! i didn't find a way to replace the sequences of ^^ before each ".
It behaves like a sequence ^^ before ! is the atomic unreplacable one, longer caret sequences can be replaced but i couldn't obtain ^! whatever i tried.
The same problem is quite easy to solve when variable expansion is enabled, even with nasty strings containing quotes by replacing "" by " at first (jeb already talked about this in another thread). For example if locs_tr doesn't contain quotes, the substitutions would be the following:
set "out_str=!loc_str:^=^^^^!" ;1st substitution, multiplicates the number of ^ by 4 as expected
call set "out_str=%%out_str:^!=^^^!%%" ! ;2nd substitution, replaces each ! by ^^!
set "out_str=!out_str:^^=^!" ;3rd substitution required, replaces each ^^ by ^ to divide by 2 the total number of ^
So my question is simple:
Is there a way using substitution IN THE EXACT SAME CONTEXT (ie no call) to obtain the desired prepared output value for out_str, so it can be returned safely to a scope that enables delayed expansion ?
The prepared output value should double the number of ^ and add ^ before each ! ie:
out_str=Hello^^^^^^ planet^!^!^!^! ^^^^Earth^^
Note that's my question is for theorical knowledge purpose ONLY. Indeed calling a label to do the two substitutions with a simple set is the reasonable way to proceed.
Your assumptions are wrong!
What we must do here is to escape all ^ and ! to prepare the return ...
Two substitutions ^=^^ then !=^! with a simple set would be enough ...
The problem is here the fact that caret folding is different if there is an exclamation mark in the line or not!
setlocal EnableDelayedExpansion
echo "one caret^"
echo "no caret^ but a bang^!"
echo "caret ^^ and a bang^!"
echo one caret ^^
echo no caret^^ but a bang^^!
echo caret ^^^^ and a bang^^!
call and carets and exclamation marks are really funny.
Because call always double all carets before any other rule, but you don't see it with quotes, because in the next parser phase the doubled carets are folded to single carets again.
Parse flow for a single line like
Starting with:
call echo my caret^^^^ "caret^^" and a bang !
1. Caret escaping outside quotes
call echo my caret^^ "caret^^" and a bang !
2. delayed expansion caret escaping (outside and **inside** quotes), the bang itself is lost here
call echo my caret^ "caret^" and a bang
3. The `CALL` caret doubling
call echo my caret^^ "caret^^" and a bang
4. Caret escaping outside quotes
call echo my caret^ "caret^^" and a bang
The problem itself was solved by dbenham and me a few years ago.
See SO: Macro to preserve variables when leaving setlocal scope
or
SO: Make an environment variable survive ENDLOCAL.
In your case it's much simpler, because you don't support quotes nor line feeds in the variable.
And you know, that you leave the current scope and will return into a scope with delayed expansion enabled, this simplifies the problem, too.
#echo off
setlocal EnableDelayedExpansion
setlocal disabledelayedexpansion
(
set "loc_str=Hello^^^ planet!!!! ^^Earth^"
set "loc_str"
setlocal EnableDelayedExpansion
set "out_str=!loc_str:^=^^^^!" # 1st substitution, multiply all carets by four
set "out_str"
call set "out_str=%%out_str:^!=^^^!%%" ! # 2nd substitution, replace all exclamation marks to two carets with exclam
set "out_str"
set "out_str=!out_str:^^=^!" # 3rd substitution reduce all carets by factor of two
set "out_str"
for /F "delims=" %%V in ("!out_str!") do (
endlocal
endlocal
set "str=%%V" ! # The trailing exclamation mark is intentionally, but will not part of the result
)
)
echo str = '!str!'
Ok jeb i just executed your macro. The code below can be copied and run. If i understood properly, the general principle is the following (correct me if i didn't).
#echo off
setlocal enableextensions disabledelayedexpansion
set "_(echo=echo: & echo"
set "_(set=echo: & set"
:: Initialize the DDE flags of the current/previous scopes.
set "flag_dde_prev=" & set "flag_dde=!"
:: Base string ; one of your nasty strings defined in another post ;p
set "str_base=caret ^ bang ! and some (other ) <>&| %% \"
setlocal enabledelayedexpansion
set "flag_dde_prev=!flag_dde!" & set "flag_dde=!"
setlocal enabledelayedexpansion
set "flag_dde_prev=!flag_dde!" & set "flag_dde=!"
:: Nasty local string to be returned ; still one of yours !
set "loc_str_nasty=!str_base! and also quoted stuff "!str_base!""
set loc_str & echo:
if defined flag_dde_prev (
set "out_str=!loc_str_nasty!"
) else (
rem STEP1: Each " is replaced by ""q so everything is consider outside quotes
set "out_str=!loc_str_nasty:"=""q!"
set out_str
rem STEP2: Each ^ is replaced with ^^
set "out_str=!out_str:^=^^!"
set out_str
rem STEP3: Each ! is replaced by ""e!
rem WARN A simple `set` must be avoided because of special characters.
call set "out_str=%%out_str:^!=""e^!%%"
set out_str
rem STEP4/5: Initial " and ^! were marked differently with letters q and e so we can restore them by replacing ""q and ""e! by " and ! respectively
set "out_str=!out_str:""e=^!"
set out_str
set "out_str=!out_str:""q="!"
set out_str
)
rem Return the value of out_str to the calling DDE/EDE scope by using a safe `for`.
for /F "delims=" %%# in ("!out_str!") do endlocal & set "str_pro=%%#"
:: Echo safely the protected string str_pro in the current DDE/EDE scope
%_(set% str_pro
if defined flag_dde (
for /F "tokens=1* delims==" %%v in ('set str_pro') do echo %%str_pro%%=%%w
) else (
%_(echo% ^^^!str_pro^^^!=!str_pro!
)
endlocal
Thanks for your remarks, indeed i talked too fast there is no problem at all with ""q and ""e in the input value of loc_str. You're very right about avoiding special characters with rem, i totally forgot. I did typing mistake also by assigning flag_dde_prev, it must be done with !..! inside an EDE scope of course. I struggle with the code editor here, i'm not used to such stuffs for now (yes i know i sound like a cavern man lol).
I edited my previous post, the protection block (..) is skipped when returning to a DDE scope (i forgot also to assign out_str when flag_dde_prev when defined to !). The returned protected string to DDE contains nasty characters so can't be echoed with %..%, so i used a for to echo it.
Is there a way to deal with " and &|<> to protect a such nasty loc_str to echo it in the calling DDE scope with a brutal echo %..% or call set %..%, or at the contrary is an undirect echoing can't be avoided ?
By undirect i mean without a slow echo only ie with a for like above, or with a very fast set str_pro only by deleting the leading string "str_pro=" by adding a dynamic leading string of backspace in the out_str value.
OUTPUT
Run twice, first when the calling scope is DDE, second when it's EDE (written as EDE in the code above).
I have a string
<?define customGUID= "DA7C36F0-A749-4CC5-9575-398C06039325"?>
I am trying to trim out DA7C36F0-A749-4CC5-9575-398C06039325 from this line.
To begin with I tried to set this string in a variable but I am not able to do that may be because of < and ? in string, I tried:
set "var=<?define customGUID= "DA7C36F0-A749-4CC5-9575-398C06039325"?>"
But later I was able to fetch the string somehow at runtime and now I have the variable
line=<?define customGUID= "DA7C36F0-A749-4CC5-9575-398C06039325"?>
I am not able to figure out how can I trim only value i.e DA7C36F0-A749-4CC5-9575-398C06039325 out of this variable using batch script.
I gave it a try to trim suffix at least with:
set "line=%line:"?>%"
But getting error, can anyone help with better approach?
Note: the spaces are included in string
You can split the string using " as a delimiter, but since quotes are used to specify for loop options, the syntax looks a little different than usual:
#echo off
setlocal enabledelayedexpansion
set "line=<?define customGUID= "DA7C36F0-A749-4CC5-9575-398C06039325"?>"
echo !line!
for /f tokens^=2^ delims^=^" %%A in ("!line!") do set "line=%%A"
echo !line!
You may use this very simple trick:
#echo off
setlocal EnableDelayedExpansion
set "var=<?define customGUID= "DA7C36F0-A749-4CC5-9575-398C06039325"?>"
set "i=0" & set "v0=%var:"=" & set /A i+=1 & set "v!i!=%"
echo Desired string: [%v1%]
If you want to know how this works, remove the #echo off line and carefully review what appears in the screen...
Your command line:
set "line=%line:"?>%"
does not make sense, because there is an =-sign missing (refer to sub-string substitution):
set "line=%line:"?>=%"
To trim away the unwanted prefix you could remove everything up to the first quotation mark:
set "line=%line:*"=%"
However, this only works when you do that after having removed the suffix, because you are dealing with unbalanced quotation marks, which are problematic together with immediate variable expansion. If you want to change the order, you have to implement escaping in order not to exhibit the redirection operator > unquoted:
set ^"line=%line:*"=%"
set "line=%line:"?>=%"
To avoid the need of escaping depending on the input string, use delayed variable expansion, like this:
set "line=<?define customGUID= "DA7C36F0-A749-4CC5-9575-398C06039325"?>"
set line
rem // First enable delayed expansion:
setlocal EnableDelayedExpansion
rem // Then apply it by replacing `%` with `!`:
set "line=!line:*"=!"
set "line=!line:"?>=!"
set line
rem // This restores the previous state:
endlocal
rem // At this point changes in the variable are no longer available due to localisation:
set line
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
I'm trying to check a variable that contains a path to see if there is a trailing slash and if there is then remove it. I have it working if the variable value is not surrounded by quotes but I also need to check for the trailing slash even if quotes exist.
The issue that I'm running into is trying to get an If statement to work to check for the double quote so I can basically check if it has a double quote and then check for the trailing slash. The If statement fails with "( was unexpected at this time". I'm sure it's an escaping issue but I've tried every way I can think of and haven't been able to get it to work. I've been searching on the web for hours with no luck.
Here's what I have for it so far (I left out the code checking for it without the quotes, basically the same format). Also, if there's a better way to achieve this I'm all ears.
set appRoot="C:\test\"
REM grab the last two characters
set lastChar=%appRoot:~-2%
if %lastChar% == \" (
echo It works!
)
This is a robust method
#echo off
set appRoot="C:\test\"
for %%a in (%approot%) do for %%b in ("%%~a\.") do echo "%%~fb"
pause
If you can change the layout of quotes in the set statement then this is simpler:
#echo off
set "appRoot=C:\test\"
for %%a in ("%approot%\.") do echo "%%~fa"
pause
Once the line if %lastChar% == \" ( is expanded (variables replaced with values), what you get is if \" == \" ( which is not a valid condition, so the parenthesis is not expected.
With the value assigned to appRoot, your best option is directly check not the last but the previous character with
set "lastChar=%appRoot:~-2,1%"
if %lastChar% == \
Or, if you can change the value of appRoot
set "appRoot=C:\test\"
REM grab the last character
set lastChar=%appRoot:~-1%
if %lastChar% == \ (
echo It works!
)
EDITED to adjust to comments
Since the OP don't have control on how the paths are asigned to the variables, the possible cases are: variables in the form set var=, set var="path", set var=path or set "var=path" (we can assume the two last are equivalent if no special characters present). With or without trailing backslash. With or without spaces in path. And the need is to get a correct path (good looking, exists or not, this to be checked later), without trailing backslashes and (not in OP question, but should be) with or without quotes in a consistent manner.
So, here we go
#echo off
setlocal enableextensions
set "appRoot=c:\some where\"
call :cleanToFullPath appRoot
echo %appRoot%
set "appRoot=c:\some where"
call :cleanToFullPath appRoot
echo %appRoot%
set appRoot="c:\some where\"
call :cleanToFullPath appRoot
echo %appRoot%
set appRoot="c:\some where\a\"
call :cleanToFullPath appRoot
echo %appRoot%
set appRoot=c:\some where\in a rare place
call :cleanToFullPath appRoot
echo %appRoot%
set appRoot=""
call :cleanToFullPath appRoot
echo %appRoot%
goto :EOF
:cleanToFullPath variableName
rem Prepare environment
setlocal enableextensions enabledelayedexpansion
rem get variable name
set "_varName=%~1"
rem get value of variable
set "_tmp=!%~1!"
rem remove quotes from variable value
set "_tmp=%_tmp:"=%"
rem handle empty variables. Default current folder
if not defined _tmp set "_tmp=."
rem prepare to process trailing bar if any
if "%_tmp:~-1%"=="\" set "_tmp=%_tmp%."
rem resolve to full path
for %%# in ("%_tmp%") do set "_tmp=%%~f#"
rem cleanup and update variable
endlocal & set "%~1=%_tmp%"
goto :eof
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