How to replace "=","*", ":" in a variable

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
dbenham
Expert
Posts: 2262
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: How to replace "=","*", ":" in a variable

#16 Post by dbenham » 08 Jun 2011 16:31

jeb wrote:The setlocal DisableExtensions is the key
OMG :shock: I've never seen a use for that option before. I saw Disable and just assumed DelayedExpansion. :lol:

I see it working now (from home), thanks.

I just discovered that =:: is NOT defined on my Vista 32bit machine at work :!: It is defined on Vista 64bit at home.

jeb wrote:
dBenham wrote:Just an observation - It seems very odd that normal dynamic variables (ERRORLEVEL, TIME, RANDOM etc) never show up in SET command, but do test as defined (IF DEFINED ERRORLEVEL is true). But the = dynamic variables do show up in SET "" command, but test as undefined (IF DEFINED =C: is false)

I didn't know if it is "defined", as I didn't know how to test it, in your case you only test if "C:" is defined, with echo on you will see
if defined C: (echo exist ) ELSE echo not defined


It took a while, but I'm finally catching on. I had noticed in the past how ECHO ON output does not match the source code, but only now am I realizing how useful it can be for understanding parsing, escaping, etc. issues. :)

I've discovered how to successfully test if an = variable is defined. There is a parsing problem in phase 2 concerning the =, and some special IF parsing rule prevents us from escaping the = properly. Yes :?: The problem is solved by using a FOR variable or delayed expansion of a normal variable. :D

Code: Select all

@echo off
setlocal enableDelayedExpansion
prompt $g
echo on

:: direct test does not work, and escaping doesn't help
if defined =c: (echo =c: is defined) else echo =c: is not defined
if defined ^=c: (echo =c: is defined) else echo =c: is not defined
if defined ^^=c: (echo =c: is defined) else echo =c: is not defined
if defined ^^^=c: (echo =c: is defined) else echo =c: is not defined

:: for variable and delayed expansion bypass parsing problem in phase 2
@set var==c:
for /f %%v in ("!var!") do if defined %%v (echo %%v is defined) else echo %%v is not defined
if defined !var! (echo !var! is defined) else echo !var! is not defined

Output:

Code: Select all

>if defined c: (echo =c: is defined )  else echo =c: is not defined
=c: is not defined

>if defined c: (echo =c: is defined )  else echo =c: is not defined
=c: is not defined

>if defined ^ c: (echo =c: is defined) else echo =c: is not defined

>if defined ^ (echo =c: is defined )  else echo =c: is not defined
=c: is not defined

>for /F %v in ("!var!") do if defined %v (echo %v is defined )  else echo %v is not defined

>if defined =c: (echo =c: is defined )  else echo =c: is not defined
=c: is defined

>if defined !var! (echo !var! is defined )  else echo !var! is not defined
=c: is defined


Dave Benham

Ed Dyreen
Expert
Posts: 1568
Joined: 16 May 2011 08:21
Location: Flanders(Belgium)
Contact:

Re: How to replace "=","*", ":" in a variable

#17 Post by Ed Dyreen » 08 Jun 2011 18:13

'
Nice example ben, now I understand. But why is it such a big deal.
Why setting a variable to begin with an = in the first place ?

dbenham
Expert
Posts: 2262
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: How to replace "=","*", ":" in a variable

#18 Post by dbenham » 08 Jun 2011 19:10

I'm not setting them - no one can (at least not directly). They are (undocumented?) dynamic variables. The =C: variable tells you the current directory of the C: drive, even if you happen to be on drive D:. If you change the directory of drive :C, then the =C: value will change as well. However, it is only defined if your cmd session has accessed the C: drive at least once. It works the same for all of the other drive letters as well.

Dave Benham

jeb
Expert
Posts: 907
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: How to replace "=","*", ":" in a variable

#19 Post by jeb » 09 Jun 2011 05:31

dbenham wrote:I've discovered how to successfully test if an = variable is defined. There is a parsing problem in phase 2 concerning the =, and some special IF parsing rule prevents us from escaping the = properly. Yes :?: The problem is solved by using a FOR variable or delayed expansion of a normal variable. :D


The way to access the name with delayed or FOR technic is so simple :(
I can't remember why I didn't test this in my second try.

So now you are the winner :shock: :D

jeb

dbenham
Expert
Posts: 2262
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: How to replace "=","*", ":" in a variable

#20 Post by dbenham » 09 Jun 2011 10:30

jeb wrote:The way to access the name with delayed or FOR technic is so simple :(
I can't remember why I didn't test this in my second try.

So now you are the winner :shock: :D

:D No worries jeb - I'm sure this is a rare occurance :!:
Besides - you should be happy. This just means the master is doing a good job teaching his student.

Dave

dbenham
Expert
Posts: 2262
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: How to replace "=","*", ":" in a variable

#21 Post by dbenham » 03 Nov 2011 10:22

jeb wrote:The other characters ["*",":"] have problems if they are the first character, because of their special meaning.

I agree that replacing * is a problem if * is the first character in the search.

But replacing : is not a problem

Code: Select all

@echo off
set var=good:bye
echo %var::=%

results in

Code: Select all

goodbye

Replacing ~ is a problem if ~ is the 1st character in the search because the parser assumes substring instead of search and replace.

Did I finally catch a slip on jeb's part :?: :lol:
God knows I've made plenty on this site. :roll:

Dave Benham

OJBakker
Expert
Posts: 26
Joined: 12 Aug 2011 13:57

Re: How to replace "=","*", ":" in a variable

#22 Post by OJBakker » 03 Nov 2011 16:14

The = can be used in a variable name, just not as the first character

Code: Select all

set var^=name=value
set var

and even more confusing names:

Code: Select all

set !^=!
set %^=%
set ^^=^^
set !
set %
set ^^

These examples work with set but not with set/p

dbenham
Expert
Posts: 2262
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: How to replace "=","*", ":" in a variable

#23 Post by dbenham » 03 Nov 2011 17:52

OJBakker wrote:The = can be used in a variable name, just not as the first character

Code: Select all

set var^=name=value
set var

The result is not what you think :wink:

Code: Select all

echo %var%

result:
- variable name = "var"
- variable value = "name=value"

I'm pretty sure it is impossible to set a variable using = in the name. The only = in variable names are the undocumented dynamic variables that can only be read, never set (at least not directly using the SET command).

Dave Benham

OJBakker
Expert
Posts: 26
Joined: 12 Aug 2011 13:57

Re: How to replace "=","*", ":" in a variable

#24 Post by OJBakker » 04 Nov 2011 04:21

Oops,
It seems I confused myself :D

Ed Dyreen
Expert
Posts: 1568
Joined: 16 May 2011 08:21
Location: Flanders(Belgium)
Contact:

Re: How to replace "=","*", ":" in a variable

#25 Post by Ed Dyreen » 29 Nov 2011 20:17

'
Is it me or is there a little flaw in oranges code, the last delimited value never gets assigned, I've added the delimiter one extra time, I also initially set the referenced variable undefined, I think it's a little dangerous otherwise :?

Code: Select all

:EQ_Replace  %VarString%  %VarReplacement%
::----------------------------------------
setlocal enableDelayedExpansion
:: (
   set "###$###="
   set    "###$=!%~1!=###"
   set      "$c=1"
   set      "$r="
   for /l %%! in (

      0, 1, 99

   ) do    if defined $c for /f "delims==" %%a in (

      'set ###$'

   ) do (
      set "$a=%%a" &set "$b=!%%a!" &set "%%a=" &2>nul set "###$!$b!" ||set "$c="
      if %%! gtr 0 set "$r=!$r!!$a:~4!!%~2!"
   )
   set "$r=!$r!######" &for %%? in ( "!%~2!" ) do set "$r=!$r:%%~?######=!"
:: )
for /f "delims=" %%r in ( "!$r!" ) do endlocal &set "%~1=%%r" &exit /b 0

dbenham
Expert
Posts: 2262
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: How to replace "=","*", ":" in a variable

#26 Post by dbenham » 08 Dec 2011 21:11

I don't have a good solution for replacing =, but I do have a quick test to see if a value contains = .

Code: Select all

@echo off
setlocal enableDelayedExpansion
set "var=%~1"

::test if var contains =
set "test=a!var!"
if /i "!test:%test%=%test%!" neq "!test!" (echo value "!var!" DOES contain =) else echo value "!var!" does NOT contain =


This test is very fast, and is probably worth doing prior to executing a comparatively expensive = replacement, either by brute force linear search, or the amel27 SET approach. No need to perform the replacement if = does not exist :!:

This is almost 10 times faster than testing for = using FIND.


Dave Benham

Liviu
Expert
Posts: 470
Joined: 13 Jan 2012 21:24

Re: How to replace "=","*", ":" in a variable

#27 Post by Liviu » 13 Jan 2012 21:36

dbenham wrote:I don't have a good solution for replacing =, but I do have a quick test to see if a value contains = .

This test may return false positives if the value contains '!' exclamation marks.

For example, create a directory "C:\x=y" and make it current, so that "%cd%" is "C:\x=y", then run the posted batch at a cmd prompt with delayedExpansion turned off and input string "ab!cd!ef" - it will display "value "ab!cd!ef" DOES contain =" (tested under xp.sp3 x86 if that matters).

Liviu

dbenham
Expert
Posts: 2262
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: How to replace "=","*", ":" in a variable

#28 Post by dbenham » 13 Jan 2012 22:08

Liviu wrote:
dbenham wrote:I don't have a good solution for replacing =, but I do have a quick test to see if a value contains = .

This test may return false positives if the value contains '!' exclamation marks.

For example, create a directory "C:\x=y" and make it current, so that "%cd%" is "C:\x=y", then run the posted batch at a cmd prompt with delayedExpansion turned off and input string "ab!cd!ef" - it will display "value "ab!cd!ef" DOES contain =" (tested under xp.sp3 x86 if that matters).

Actually the test is working perfectly :!: :wink:

The value "C:\x=y" is introduced into the string upon assignment of %~1 to var. But that assignment is not part of the test. It is just a convenient way to assign a user defined value to a variable. The actual test consists of the final two lines of my short script.

In other words, the test checks if a variable contains =. It is not designed to test if a command argument contains =. The test can't begin until we have a value in the variable. :)


Dave Benham

Liviu
Expert
Posts: 470
Joined: 13 Jan 2012 21:24

Re: How to replace "=","*", ":" in a variable

#29 Post by Liviu » 13 Jan 2012 23:50

dbenham wrote:In other words, the test checks if a variable contains =. It is not designed to test if a command argument contains =. The test can't begin until we have a value in the variable. :)


Thanks for clarifying, though the way it was posted it sure looked like it was meant to test the command argument being passed into it ;-)

What is a bit confusing is that the argument is evaluated in the context of "enableDelayedExpansion" rather than the caller's context. If the snippet is saved as, say, hasEq.cmd then the following output shows that what's actually tested is different from what the caller thinks or assumes it's testing.

Code: Select all

C:\x=y>echo ab!cd!ef
ab!cd!ef

C:\x=y>hasEq ab!cd!ef
value "abC:\x=yef" DOES contain =

C:\x=y>echo "ab!cd!ef"
"ab!cd!ef"

C:\x=y>hasEq "ab!cd!ef"
value "abC:\x=yef" DOES contain =

C:\x=y>echo ab^!cd^!ef
ab!cd!ef

C:\x=y>hasEq ab^!cd^!ef
value "abC:\x=yef" DOES contain =

C:\x=y>echo "ab^!cd^!ef"
"ab^!cd^!ef"

C:\x=y>hasEq "ab^!cd^!ef"
value "ab!cd!ef" DOES contain =


Liviu

npocmaka_
Posts: 481
Joined: 24 Jun 2013 17:10
Location: Bulgaria
Contact:

Re: How to replace "=","*", ":" in a variable

#30 Post by npocmaka_ » 15 Nov 2013 05:37

orange_batch wrote:A quick adaptation of my Get Last Token code.

Args: "string" "char(s) to find" "char(s) to place"

Code: Select all

@echo off&setlocal enabledelayedexpansion

call :l_replace "yellow banana=black cat=rainbow = noodle=beer mug" "=" "#"
echo:!str!
call :l_replace "yellow banana:black cat:rainbow : noodle:beer mug" ":" "#"
echo:!str!
call :l_replace "yellow banana*black cat*rainbow * noodle*beer mug" "*" "#"
echo:!str!
call :l_replace "yellow banana~black cat~rainbow ~ noodle~beer mug" "~" "#"
echo:!str!

call :l_replace "yellow banana=:*~black cat~*:=rainbow =~*~:*=* noodle~beer mug" "=:*~" "#---#"
echo:!str!

call :l_replace "yellow banana=" "=:*~" "#---#"
echo:!str!
call :l_replace "=yellow banana" "=:*~" "#---#"
echo:!str!

pause
exit

:l_replace
set "str=x%~1x"
:l_replaceloop
for /f "delims=%~2 tokens=1*" %%x in ("!str!") do (
if "%%y"=="" set "str=!str:~1,-1!"&exit/b
set "str=%%x%~3%%y"
)
goto l_replaceloop

Iterates by tokens, so it's much faster than char-by-char iteration.

Delayed expansion isn't even necessary. (Remove setlocal and change !str! to %str% is all.) But of course, you lose the ability to echo command characters outside of quotation marks.

(PS: The x's in set "str=x%~1x" is to allow single-token strings. Maybe optimizations are possible, but I need to sleep.)



I wondered over the problem and came to the same idea.Here's an 'improved' version that does correctly replaces the symbols if they repeat(works with = and * too).


Code: Select all

@echo off

set "waves=~~wave~~wave~wave~wave~~~"
set replace_with=X1
echo %waves%

call :wavereplacer "%waves%" %replace_with% res
echo %res%
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

Post Reply