为什么推迟扩张code的管道块内时失败? [英] Why does delayed expansion fail when inside a piped block of code?

查看:193
本文介绍了为什么推迟扩张code的管道块内时失败?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面是一个演示如何扩展延迟失败,如果它是被管道块内一个简单的批处理文件。 (失败是对剧本的结尾)任何人能解释这是为什么?

我有一个变通办法,但它需要一个临时文件的创建。我最初遇到了这个问题的<一个工作时href=\"http://stackoverflow.com/questions/5607489/find-files-and-sort-by-size-in-a-windows-batch-file/8192602#8192602\">Find文件和排序在Windows批处理文件大小

 关闭@echo
SETLOCAL enableDelayedExpansion设置TEST1 = X
一套测试2 = Y
设置TEST3 = Z回声(回声师范大学膨胀试验
回声未排序的作品

  回声%TEST3%
  回声%测试1%
  回声%TEST2%

回声(
回声排序作品

  回声%TEST3%
  回声%测试1%
  回声%TEST2%
)|分类回声(
回声---------
回声(回声延迟膨胀试验
回声未排序的作品

  回音!TEST3!
  回音!TEST1!
  回音!TEST2!

回声(
回声排序失败

  回音!TEST3!
  回音!TEST1!
  回音!TEST2!
)|分类
回声(
回声排序解决办法

  回音!TEST3!
  回音!TEST1!
  回音!TEST2!
)&GT; TEMP.TXT
排序TEMP.TXT
德尔TEMP.TXT

下面是结果

 师范大学膨胀试验
未排序的作品
ž
X
ÿ排序作品
X
ÿ
ž---------延迟膨胀试验
未排序的作品
ž
X
ÿ排序失败
!TEST1!
!TEST2!
!TEST3!排序解决办法
X
ÿ
ž


解决方案

由于Aacini显示,似乎很多东西在管道内失败。

 回声你好|集/ p = VAR
在这里回响|拨打:函数

但在现实中,这只是一个问题,了解管道是如何工作的。

管道的每一面在自己的ascynchronous线程开始了自己的cmd.exe。结果
这是原因为什么这么多的事情似乎被打破。

但有了这个知识,你就可以避免这个创造新的效果。

 呼应ONE | (集/ p VARx前提=放大器;设置VARx前提)
设置VAR1 = VAR2
两集VAR2 =内容
回声ONE | (回声%%% VAR1 %%%)
回声3 |回声MYCMDLINE %% CMDCMDLINE %%
回声4 | (CMD / V:上/ C回声4:!VAR2)

编辑:在深入分析

正如dbenham所示,管道的两侧是等效的扩张阶段。结果
主要的规则似乎是:

正常批次分析器阶段完成结果
..百分号展开的结果
..特殊字符相/块开始检测的结果
..延迟扩展(仅当延迟扩充被启用,这是不是一个命令块)

开始以的CMD.EXE C:\\ WINDOWS \\ SYSTEM32 \\ CMD.EXE / S / D / C&LT;批处理命令&gt;中结果
这些扩展遵循CMD线分析器不是间歇线分析器的规则。

..百分号展开的结果
..延迟扩展(仅当延迟扩展启用)

&LT;批处理命令&GT; 将如果它是一个括号块内修改。

 
回音1 %% CMDCMDLINE %%
回音2
)|更多

古称 C:\\ WINDOWS \\ SYSTEM32 \\ CMD.EXE / S / D / C(回声1%CMDCMDLINE%放大器;回声二),所有的换行符更改为&放大器; 运营商

为什么要延迟扩展阶段是用括号影响?结果
我想,它不能在批量解析器相展开,作为一个块可以包含许多指令,并执行一个线时延迟扩展生效。

 
组VAR =一个
回音!VAR!
组VAR =两个
)|更多

显然!无功!不能在批处理环境评估,作为线路只在cmd行环境中执行。

但为什么它可以在这种情况下,在间歇上下文进行评估

 呼应!无功! |更多

在我opionion这是一个错误或inconsitent的行为,但它不是第一个

编辑:添加LF招

由于dbenham显示,似乎有通过CMD-行为改变所有线路送入一定的局限性&放大器;

 
  回声7:第一部分
  REM这杀死了整个街区,因为关闭)的评论!
  第二部分回声
)|更多

这将导致到结果
C:\\ WINDOWS \\ SYSTEM32 \\ CMD.EXE / S / D / C(回声7:第1部分和放大器; REM这...&放大器;回声分2)

REM 将此话完整的线尾,所以即使右括号丢失呢。

但你可以嵌入自己的换行解决这个问题!

 集LF = ^
上述REM中的两个空行是必需的

  回声8:第一部分
  REM这工作,因为它拆分命令%% LF %%回声第二部分
)|更多

此结果 C:\\ WINDOWS \\ SYSTEM32 \\ CMD.EXE / S / D / C(回声8:第一部分%CMDCMDLINE%放大器; REM这工作,因为它拆分命令%LF%回声第二部分)

和作为%LF而由解析器解析parenthises%膨胀,所得code看起来像

 (回声8:第1部分和放大器; REM这工作,因为它分裂的命令
  回声分2)

%LF%行为作品一贯括号内,也是在一个批处理文件。结果
但不是在正常的线,有一个&LT;换行方式&gt; 将停止这一行解析

编辑:异步不是全部真相

我说的是两个线程是异步的,通常这是真的。结果
但在现实中左螺纹可以锁定本身当管道数据没有被正确的线程消耗掉。结果,
似乎有一个限度〜在管缓冲区1000个字符,那么线程被阻塞,直到数据被消耗。

 关闭@echo

    (
    对/ L %%一中(1,1,60)DO(
            回声长文本可以锁定这个线程
            回声线程1 ##### %% A&GT; CON
        )
    )
    回声线程1月底#####&GT; CON
)| (
    对/ L %%的n(1,1,6)DO @(
        平-N 2 - 本地主机GT; NUL
        回声线程2 ..... %% n和
        集/ p X =
    )

Here is a simple batch file that demonstrates how delayed expansion fails if it is within a block that is being piped. (The failure is toward the end of the script) Can anyone explain why this is?

I have a work-around, but it requires creation of a temporary file. I initially ran into this problem while working on Find files and sort by size in a Windows batch file

@echo off
setlocal enableDelayedExpansion

set test1=x
set test2=y
set test3=z

echo(

echo NORMAL EXPANSION TEST
echo Unsorted works
(
  echo %test3%
  echo %test1%
  echo %test2%
)
echo(
echo Sorted works
(
  echo %test3%
  echo %test1%
  echo %test2%
) | sort

echo(
echo ---------
echo(

echo DELAYED EXPANSION TEST
echo Unsorted works
(
  echo !test3!
  echo !test1!
  echo !test2!
)
echo(
echo Sorted fails
(
  echo !test3!
  echo !test1!
  echo !test2!
) | sort
echo(
echo Sort workaround
(
  echo !test3!
  echo !test1!
  echo !test2!
)>temp.txt
sort temp.txt
del temp.txt

Here are the results

NORMAL EXPANSION TEST
Unsorted works
z
x
y

Sorted works
x
y
z

---------

DELAYED EXPANSION TEST
Unsorted works
z
x
y

Sorted fails
!test1!
!test2!
!test3!

Sort workaround
x
y
z

解决方案

As Aacini shows, it seems that many things fail within a pipe.

echo hello | set /p var=
echo here | call :function

But in reality it's only a problem to understand how the pipe works.

Each side of a pipe starts its own cmd.exe in its own ascynchronous thread.
That is the cause why so many things seem to be broken.

But with this knowledge you can avoid this and create new effects

echo one | ( set /p varX= & set varX )
set var1=var2
set var2=content of two
echo one | ( echo %%%var1%%% )
echo three | echo MYCMDLINE %%cmdcmdline%%
echo four  | (cmd /v:on /c  echo 4: !var2!)

EDIT: In depth analysis

As dbenham shows, both sides of the pipes are equivalent for the expansion phases.
The main rules seems to be:

The normal batch parser phases are done
.. percent expansion
.. special character phase/block begin detection
.. delayed expansion (but only if delayed expansion is enabled AND it isn't a command block)

Start the cmd.exe with C:\Windows\system32\cmd.exe /S /D /c"<BATCH COMMAND>"
These expansions follows the rules of the cmd-line parser not the the batch-line parser.

.. percent expansion
.. delayed expansion (but only if delayed expansion is enabled)

The <BATCH COMMAND> will be modified if it's inside a parenthesis block.

(
echo one %%cmdcmdline%%
echo two
) | more

Called as C:\Windows\system32\cmd.exe /S /D /c" ( echo one %cmdcmdline% & echo two )", all newlines are changed to & operator.

Why the delayed expansion phase is affected by parenthesis?
I suppose, it can't expand in the batch-parser-phase, as a block can consist of many commands and the delayed expansion take effect when a line is executed.

(
set var=one
echo !var!
set var=two
) | more

Obviously the !var! can't be evaluated in the batch context, as the lines are executed only in the cmd-line context.

But why it can be evaluated in this case in the batch context?

echo !var! | more

In my opionion this is a "bug" or inconsitent behaviour, but it's not the first one

EDIT: Adding the LF trick

As dbenham shows, there seems to be some limitation through the cmd-behaviour that changes all line feeds into &.

(
  echo 7: part1
  rem This kills the entire block because the closing ) is remarked!
  echo part2
) | more

This results into
C:\Windows\system32\cmd.exe /S /D /c" ( echo 7: part1 & rem This ...& echo part2 ) "
The rem will remark the complete line tail, so even the closing bracket is missing then.

But you can solve this with embedding your own line feeds!

set LF=^


REM The two empty lines above are required
(
  echo 8: part1
  rem This works as it splits the commands %%LF%% echo part2  
) | more

This results to C:\Windows\system32\cmd.exe /S /D /c" ( echo 8: part1 %cmdcmdline% & rem This works as it splits the commands %LF% echo part2 )"

And as the %lf% is expanded while parsing the parenthises by the parser, the resulting code looks like

( echo 8: part1 & rem This works as it splits the commands 
  echo part2  )

This %LF% behaviour works always inside of parenthesis, also in a batch file.
But not on "normal" lines, there a single <linefeed> will stop the parsing for this line.

EDIT: Asynchronously is not the full truth

I said that the both threads are asynchronous, normally this is true.
But in reality the left thread can lock itself when the piped data isn't consumed by the right thread.
There seems to be a limit of ~1000 characters in the "pipe" buffer, then the thread is blocked until the data is consumed.

@echo off
(
    (
    for /L %%a in ( 1,1,60 ) DO (
            echo A long text can lock this thread
            echo Thread1 ##### %%a > con
        )
    )
    echo Thread1 ##### end > con
) | (
    for /L %%n in ( 1,1,6) DO @(
        ping -n 2 localhost > nul
        echo Thread2 ..... %%n
        set /p x=
    )
)

这篇关于为什么推迟扩张code的管道块内时失败?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆