Home > database >  Prepare a string in a block of parenthesis variable expansion is disabled to return it to a calling
Prepare a string in a block of parenthesis variable expansion is disabled to return it to a calling

Time:06-22

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.

CodePudding user response:

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 enter image description here

  • Related