2

This question is originally coming from Escape percent signs in given variables. I do not want to disarrange the good answer over there. But my issue changed a little bit...

Let's assume there is a given string variable enclosed by double quotes which may include one or more percent signes. It is not possible to switch to enabled delayed expansion permanently (other code is already usable). Calling a function including the string variable as a parameter is necessary. This is what I determined so far:

@echo off & setlocal ENABLEEXTENSIONS
SET AlbumArtist=%1
CALL :EscapePoisonChars %AlbumArtist% AlbumArtist_VDN
echo %AlbumArtist_VDN%

CALL :EscapePoisonChars %%AlbumArtist%% AlbumArtist_VDN
echo %AlbumArtist_VDN%
endlocal &GOTO:EOF

:EscapePoisonChars
@echo off & setlocal ENABLEEXTENSIONS
SET TmpString="%~1"
SET TmpString=%TmpString:&=^^^&%
SET TmpString=%TmpString:(=^^^(%
SET TmpString=%TmpString:)=^^^)%
endlocal&SET %2=%TmpString:~1,-1%&GOTO :EOF

I know that this is probably not a "clean solution". But I would like to understand why when the routine is invoked by CALL :EscapePoisonChars %AlbumArtist% AlbumArtist_VDN the percent sign disappears. When called with the string variable %%AlbumArtist%% enclosed by doubled percent signs it gives the wanted output:

D:\Batch>PercentTwins.bat "100% Rock & Roll"
100 Rock & Roll
100% Rock & Roll

D:\Batch>

Why there is a different result if %AlbumArtist% is expanded in- or outside the function :EscapePoisonChars? With echo on I see that the percent sign just disappears with SET TmpString="~1". Any explanations will help me to improve my further cmd techniques. Thanks!

Community
  • 1
  • 1
CmdQuestion
  • 142
  • 1
  • 2
  • 8

2 Answers2

1

Anyone correct me if I am wrong, but I think when single percent signs in one command are passed on to another command, they will disappear. If it is just used within the same command, it normally will not disappear.

(There is probably a more 'correct' way of saying this, or this idea might be wrong altogether)

(some info on percent signs at Microsoft Support website)

user2033427
  • 853
  • 7
  • 8
  • 5 Minutes limit reached :-) I now read the Microsoft KB you mentioned and it says that a single percent sign will be interpreted as a NUL character when processed by echo. By try-out I found that when the string is enclosed in double quotes also single percent signs will be echoed. And now I parse the double-quoted string via CALL and the behaviour changes completely. Strange. Martin – CmdQuestion Feb 08 '13 at 17:12
0

The call command initiates the % parsing phase a second time (reference the accepted answer to the question post How does the Windows Command Interpreter (CMD.EXE) parse scripts?); this lets the % signs disappear. You could double the % signs in advance, but you would need to enable delayed expansion for that, like this:

@echo off & setlocal ENABLEEXTENSIONS EnableDelayedExpansion
SET AlbumArtist=%1
set AlbumArtist=!AlbumArtist:%%=%%%%!
CALL :EscapePoisonChars %AlbumArtist% AlbumArtist_VDN
echo %AlbumArtist_VDN%
endlocal &GOTO:EOF

In your second sub-routine call, the two parsing phases do not ever see the % signs of the original string; the first phase expands %%AlbumArtist%% to %AlbumArtist% literally, the second phase expands it to 100% Rock & Roll.

But:
There is no need for all that, you do not need the sub-routine at all, when you stick to the only safe set syntax and ensure to always have the string quoted properly:

set "AlbumArtist=%~1"

%1 would return the string as given; %~1 removes potential surrounding quotation marks. Enclosing the entire assignment expression within quotation marks makes the syntax robust against poisonous characters by not considering the "" as part of the assigned string itself.

When using/returning the string (by echo as in the example here), you need to enclose the string within quotes again to maintain safety, in case you are using normal (immediate) % expansion:

set "AlbumArtist=%~1"
echo "%AlbumArtist%"

Alternatively, delayed expansion makes reading variables safe even without quotation marks:

set "AlbumArtist=%~1"
setlocal EnableDelayedExpansion
echo !AlbumArtist!
endlocal
Community
  • 1
  • 1
aschipfl
  • 33,626
  • 12
  • 54
  • 99