6

I'm trying to replace all instances of ~ with {~} in a batch script so that VBScript doesn't assume I mean to hit enter when I pass it to sendKeys, however, the variable keeps getting replaced by the literal string meant to replace it.

For example,

rem Intended function;
rem input  = Hello~world.
rem output = Hello{~}world.

However each time, it either does nothing, or sets refVar to the literal string refVar:~={~}.

Here's what I have tried;

set refVar=!refVar:~={~}!

rem Also
set refVar=%refVar:~={~}%

rem and...
set refVar=%refVar:^~=^{^~^}%

rem and of course;
set refVar=!refVar:^~=^{^~^}!

rem and even:

set "pre=~"
set "post={~}"
set refVar=%refVar:!pre!=!post!%

rem and the same set commands with;
set refVar=!refVar:%pre%=%post%!

Am I missing a way to escape this? I'm pretty sure it has to do with ~ being a positional character in a similar command.

I understand there's likely a way to fix this in VBScript, but this bothers me to have a restriction without apparent workarounds to me.

user692942
  • 16,398
  • 7
  • 76
  • 175
Bloodied
  • 1,004
  • 7
  • 20

3 Answers3

4

So I did some digging and it seems this is really, really hard. However, I did find this, which solves the problem, giving us this as code:

@echo off

set "input=Hello~world."
echo input: %input%

call :wavereplacer "%input%" {tilde} output
set output=%output:{tilde}={~}%
echo output: %output%
pause

::your own code should be above this.
goto :eof

:wavereplacer String Replacer [RtnVar]
setlocal
rem  the result of the operation will be stored here
set "result=#%~1#"
set "replacer=%~2"
call :strlen0.3 result wl
call :strlen0.3 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.3 part1 p1l
  call :strlen0.3 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.3  StrVar  [RtnVar]
  setlocal EnableDelayedExpansion
  set "s=#!%~1!"
  set "len=0"
  for %%A in (2187 729 243 81 27 9 3 1) do (
   set /A mod=2*%%A
   for %%Z in (!mod!) do (
      if "!s:~%%Z,1!" neq "" (
         set /a "len+=%%Z"
         set "s=!s:~%%Z!"

      ) else (
         if "!s:~%%A,1!" neq "" (
            set /a "len+=%%A"
            set "s=!s:~%%A!"
         )
      )
   )
  )
  endlocal & if "%~2" neq "" (set %~2=%len%) else echo %len%
exit /b

This cannot replace ~ to {~} immediately, you need to use a temporary substitution, in this case {tilde}

Dennis van Gils
  • 3,487
  • 2
  • 14
  • 35
2

I offer a perhaps easier solution for replacing each tilde character in value string of an environment variable by a tilde in braces than Dennis van Gils which works also fine for this task.

@echo off
set "TestVar=hello~world."
echo TestVar has value: %TestVar%
call :SpecialReplace "TestVar"
echo TestVar has value: %TestVar%
goto :EOF


rem The subroutine below expects the name of an environment variable as
rem parameter. The subroutine does nothing if called without parameter.
rem Also nothing is done if specified environment variable is not defined.

rem Each tilde character in value of this environment variable is replaced
rem by {~} by this subroutine.

rem Note: This subroutine can be also easily modified to replace other
rem special characters like the equal sign by a different string which
rem can be also no string in case of special character should be removed.
rem Just modify Search and Replace variables for a different replace. But
rem be aware of more code must be changed if search string is longer than
rem one character. Length of replace string does not matter on code below.

:SpecialReplace
if "%~1" == "" exit /B
setlocal EnableDelayedExpansion
set "VarName=%~1"
set "VarValue=!%VarName%!"
if "!VarValue!" == "" endlocal & exit /B
set "NewValue="
set "Search=~"
set "Replace={~}"

:ReplaceLoop
if "!VarValue:~0,1!" == "!Search!" (
    set "NewValue=!NewValue!!Replace!"
) else (
    set "NewValue=!NewValue!!VarValue:~0,1!"
)
set "VarValue=!VarValue:~1!"
if not "!VarValue!" == "" goto ReplaceLoop
endlocal & set "%VarName%=%NewValue%" & exit /B

For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.

  • call /?
  • echo /?
  • endlocal /?
  • exit /?
  • goto /?
  • if /?
  • rem /?
  • set /?
  • setlocal /?

And see also answer on Single line with multiple commands using Windows batch file to understand how the two lines with multiple commands with a single ampersand between the commands work.


A variant of above with an extended subroutine SpecialReplace supporting now variable search and replace strings specified as second and third parameter as suggested by Dennis van Gils.

@echo off
set "TestVar=hello~world."
echo TestVar has value: %TestVar%
call :SpecialReplace "TestVar" "~" "{~}"
echo TestVar has value: %TestVar%
goto :EOF


rem The subroutine below expects the name of an environment variable as
rem first parameter. The subroutine does nothing if called without parameter.
rem Also nothing is done if specified environment variable is not defined.

rem The second parameter must be the search string. The subroutine does
rem nothing if there is no second parameter which can be also just one
rem or two double quotes enclosed in double quotes.

rem The third parameter is an optional replace string. Without a third
rem parameter all occurrences of search string are removed from value
rem of the specified environment variable.

rem With just one or two double quotes in double quotes as search string
rem no replace string can be defined because command processor does not
rem parse the quote strings as most would expect it. But """ or """" as
rem search string with no replace string works and results in removing
rem all double quotes or all occurrences of two double quotes.

:SpecialReplace
if "%~1" == "" exit /B
setlocal EnableDelayedExpansion
set "VarName=%~1"
set "VarValue=!%VarName%!"
if "!VarValue!" == "" endlocal & exit /B
set "NewValue="
set "Search=%~2"
if "!Search!" == "" endlocal & exit /B
set "Replace=%~3"
set "Length=0"

:GetLength
if "!Search:~%Length%,1!" == "" goto ReplaceLoop
set /A Length+=1
goto GetLength

:ReplaceLoop
if "!VarValue:~0,%Length%!" == "!Search!" (
    set "NewValue=!NewValue!!Replace!"
    set "VarValue=!VarValue:~%Length%!"
) else (
    set "NewValue=!NewValue!!VarValue:~0,1!"
    set "VarValue=!VarValue:~1!"
)
if not "!VarValue!" == "" goto ReplaceLoop
endlocal & set "%VarName%=%NewValue%" & exit /B
Community
  • 1
  • 1
Mofi
  • 46,139
  • 17
  • 80
  • 143
  • That is indeed a neat answer. It could be better using arguments for the search and replace string, for easier usability. – Dennis van Gils Feb 06 '16 at 22:12
  • Haha, I know batch-file scripting commands for the most part, but I'm guessing the help commands are for others looking for the same answer. Either way, thanks, glad to know the solution wasn't just a certain order of delimiters i failed to try. – Bloodied Feb 07 '16 at 01:02
  • @Arescet Yes, I added list of used commands and how to get help on them for everyone reading this answer as I nearly always do on my batch file related answers. It was obviously for me from your question that you know all those commands very well. – Mofi Feb 07 '16 at 14:50
  • 1
    @DennisvanGils I took your suggestions and extended subroutine `SpecialReplace` to accept search and replace string as arguments and of course work also with variable length of search string. – Mofi Feb 07 '16 at 20:42
1
@ECHO Off
SETLOCAL ENABLEDELAYEDEXPANSION 
SET "input=hello~world"

SET "xhead=!input:%input:*~=%=!"
SET "output=!input:%xhead%=%xhead:~0,-1%{~}!"

ECHO %input% --^> %output% 

GOTO :EOF

This appears to work provided there is exactly one ~ and that it's not the initial or terminal character.

Magoo
  • 77,302
  • 8
  • 62
  • 84