禁用在括号变量扩展的块中准备一个字符串,以将其返回到可以扩展的呼叫范围(不允许呼叫)

发布于 2025-02-09 06:09:35 字数 2644 浏览 2 评论 0原文

我只有在Windows批处理脚本中才要求理论知识目的。

今天,我的问题是要准备本地字符串loc_str在一个括号的块内而无需延迟扩展,以将其返回到启用变量外汇的本地范围(或最终准备它)对于in(“%loc_str%”),如果loc_str中的讨厌字符已正确逃脱)。

我们假设loc_str至少包含一个^和一个,并且不包含双引号。让我们考虑以下代码:

@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

如您所知,本地标志flag_ {dde,dde_prev}用于测试调用和当前范围的类型。通常,它们都在块setLocal..endLocal的开头定义。 flag_dde等于如果当前范围禁用变量扩展,或者不确定 如果当前范围可以启用它。 flag_dde_prev的本地值是调用范围中flag_dde的继承值。

我们必须在这里做的是逃脱所有^,准备OUT_STR的返回,并使用%..%..%当后者启用变量扩展时(即定义flag_dde_prev时)时,要进行调用范围。两个替代^= ^^然后用简单的设置就足够了,但是在一个块(..)力我们使用呼叫集语句。不幸的是,Caret ^并未以启用变量扩展的范围中的方式替换。

确切地说,第一个替换^= ^^偶数均匀的镜头序列,但不会加倍奇数(它使它们翻了一番)。 然后,第二个替换!=^!^^!替换每个

要陷入困境,

loc_str=Hello^^^ planet!!!! ^^Earth^               ;init
out_str=Hello^^^^^ planet!!!! ^^^^Earth^           ;1st substitution ^=^^
out_str=Hello^^^^^ planet^^!^^!^^!^^! ^^^^Earth^   :2nd substitution !=^!

如果仅在^^ =^仅在!=^!之前完成,似乎可以使用^^ =^可逆。 如果^^ =^是在!=^!之后完成的,则仅修改^ notefeard 仅修改。 如果^^ =^是在!=^!之后完成的代码>“ 。 在之前,它的行为就像序列^^是原子不可再生的,可以更换更长的caret序列,但我无法获得^!无论我尝试什么。

当启用可变扩展时,也很容易解决同样的问题,即使通过替换 >” “ ”首先(Jeb已经在另一个方面谈论过这一点),即 )。

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 ^

线程 是否有一种方法在完全相同的上下文中使用替换(即否呼叫)来获取所需的准备的out_str>启用延迟扩展?

准备好的输出值应将^的数量增加一倍,并在每个^添加^之前! ie:

out_str=Hello^^^^^^ planet^!^!^!^! ^^^^Earth^^

请注意,这是我的问题仅用于理论知识目的。确实,请称标签以简单的SET进行两个替换是进行的合理方法。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

柏林苍穹下 2025-02-16 06:09:35

您的假设是错误的!

我们在这里必须做的是逃脱所有 ^和!准备回报...
两个替换^= ^^然后!=^!用简单的集合就足够了...

问题在这里,如果行中有感叹号,伦名折叠是不同的!

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^^!

呼叫,镜头和感叹号真的很有趣。

因为呼叫始终在任何其他规则之前将所有镜头加倍,但是您不会用引号看到它,因为在下一个解析器阶段,双重的手套再次折叠为单个镜头。

单一线的解析流

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

几年前,Dbenham和我解决了 。
请参阅 so:离开setLocal范围时要保留变量的宏

或者
so:使环境变量生存在endlocal 中。

在您的情况下,这要简单得多,因为您不支持引号,也不支持变量中的线条。
而且您知道,您会离开当前范围,并将恢复启用延迟扩展的范围,这也简化了问题。

@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!'

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!'
孤者何惧 2025-02-16 06:09:35

好吧,杰布,我刚刚执行了你的宏。以下代码可以复制并运行。如果我正确理解,那么一般原则就是以下(如果没有,请纠正我)。

@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

感谢您的发言,实际上我说的太快了,LOC_STR的输入值中的“ Q and” e都没有问题。避免使用rem的特殊角色,您完全忘记了。我也通过分配flag_dde_prev来键入错误,必须使用!当然是在EDE范围内。我在这里与代码编辑器挣扎,现在我还不习惯这样的东西(是的,我听起来像个洞穴人大声笑)。

我编辑了我以前的帖子,返回DDE范围时会跳过保护块(..)(我也忘记了在定义为!时flag_dde_prev时分配out_str。返回的受保护字符串到DDE包含令人讨厌的字符,因此不能用%..%回声,因此我为使用了来呼应它。

有没有一种方法可以处理&amp; |&lt;&gt;来保护这样令人讨厌的loc_str,以在带有Brutal 的调用dde范围中呼应它。 echo%..%呼叫设置%..%,或者在相反的情况下,无法避免

通过slow echo <避免 回声/code>仅具有,或者是非常快的set str_pro仅通过删除领先字符串“ str_pro =” by 值中添加一个动态的backspace

在OUT_STR
运行两次,首先在调用范围为DDE时,第二次是EDE(上面的代码中的EDE)。

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).

OUTPUT

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文