标题: [原创] 批处理命令连接符号优先级证明 [打印本页]
作者: qzwqzw 时间: 2012-3-22 21:32 标题: 批处理命令连接符号优先级证明
- @echo off
- echo.
- echo. 已经确认命令连接符号优先级从低到高如下:
- echo. ^& 命令分隔符
- echo. ^|^| 逻辑或操作符
- echo. ^&^& 逻辑与操作符
- echo. ^| 管道操作符
- echo. ^< ^> ^>^> I/O重定向操作符
- echo. ^(^) 命令分组
- echo.
- echo.证明过程如下:
- pause
- echo.
- echo. 首先应当公认^(和^)作为命令分组符号有最高的优先级,可以改变任意命令组的优先级
- echo.
- echo. 另外需要注意的是,命令连接符号的优先级与语句的执行顺序不是直接对应的
- echo.
- echo. 命令行中的语句仍然是先执行左侧,再执行右侧
- echo.
- echo. 命令连接符只影响左侧与右侧语句的分组过程,而不会使右侧语句先执行
- echo.
- pause
- cls
- echo on
- echo flag1 | echo flag2 > con
- echo flag1 | (echo flag2 > con)
- (echo flag1 | echo flag2) > con
- @echo off
- echo.
- echo.前两句输出效果相同,说明第二句的分组符号可以省略,证明 ^> 优先级大于 ^|
- pause
-
- cls
- echo on
- echo flag1 && echo flag2 | echo flag3
- echo flag1 && (echo flag2 | echo flag3)
- (echo flag1 && echo flag2) | echo flag3
- @echo off
- echo.
- echo.前两句输出效果相同,说明第二句的分组符号可以省略,证明 ^| 优先级大于 ^&^&
- pause
-
- cls
- echo on
- echo flag1 || echo flag2 && echo flag3
- echo flag1 || (echo flag2 && echo flag3)
- (echo flag1 || echo flag2) && echo flag3
- @echo off
- echo.
- echo.前两句输出效果相同,说明第二句的分组符号可以省略,证明 ^&^& 优先级大于 ^|^|
- pause
-
- cls
- echo on
- echo flag1 || echo flag2 & echo flag3
- (echo flag1 || echo flag2) & echo flag3
- echo flag1 || (echo flag2 & echo flag3)
- @echo off
- echo.
- echo.前两句输出效果相同,说明第二句的分组符号可以省略,证明 ^|^| 优先级大于 ^&
- pause
-
- cls
- echo.综合测试示例:
- echo.
- echo on
- echo flag1 & more flag2 || echo flag3 && echo flag4 | echo flag5 || echo flag6
- echo flag1 & ((more flag2 || (echo flag3 && (echo flag4 | echo flag5))) || echo flag6)
- (echo flag1 & (more flag2 || (echo flag3 && echo flag4) | echo flag5)) || echo flag6
- @echo off
- echo.
- echo.前两句输出效果相同,说明符合优先级和分组优先级相同,第三句是个反例
- pause
复制代码
作者: wankoilz 时间: 2012-3-22 23:07
有意思,先支持一下!
作者: CrLf 时间: 2012-3-22 23:30
本帖最后由 CrLf 于 2012-3-22 23:39 编辑
- (echo flag1 | echo flag2) > con
复制代码
什么情况,这句在我电脑上似乎也出现了内存泄露?win7 系统,求验证及解答
另外,我觉得楼主对重定向符的优先级验证不严谨吧,应该这样证明:- set =|find /c "" 2>nul
- (set =|find /c "") 2>nul
- set = 2>nul|find /c ""
复制代码
作者: plp626 时间: 2012-3-23 08:50
本帖最后由 plp626 于 2012-3-23 08:53 编辑
回复 3# CrLf
你的“内存泄露”显示的是?
我这里:复制代码
比较惊讶的是后面两个特殊字符到底是什么?
作者: CrLf 时间: 2012-3-23 09:04
回复 4# plp626
显示是这样的,多显示了两个没见过的字符。话说让我吃惊的是,执行到这里时居然惊动了 360:
[attach]5061[/attach]
而我电脑上的 0x0~0xff 应显示为:
[attach]5062[/attach]
作者: qzwqzw 时间: 2012-3-23 13:28
实际上那两个字符就是你的ID的“原形”
用这种方法还能显示出0x08 0x09等字符
作者: qzwqzw 时间: 2012-3-23 13:41
版主再仔细推敲一下你的证明代码
我觉得逻辑上似也有问题
至于我的证明代码
一方面因为我是先通过其它方法确认了这个优先级
再去寻求从代码角度证明的
另一方面为了追求代码的优雅
设计上主要考虑了前后代码的连贯性
所以有些逻辑上的疏忽在所难免
当然重定向符号的细节还比较多
包括句柄复制符号也没有展开来
作者: plp626 时间: 2012-3-23 13:47
本帖最后由 plp626 于 2012-3-23 14:16 编辑
这个副产品有意思。。。cmd的黑框显示的字符还真多。。。下次准备更新 ascii.cmd- cmd/v
- call ascmap.cmd $
- (echo;|echo off)>con&echo !$:~0,70!
复制代码
作者: CrLf 时间: 2012-3-23 16:20
本帖最后由 CrLf 于 2012-3-23 16:23 编辑
回复 6# qzwqzw
噢,果然如此,写成这样和 dir 的结果一比较就很明显了:复制代码
但是不知为何会因为重定向而导致回车换行符显现真身呢?08、09 确实试出来了,但是不知其所以然- (echo;|set /p= )>con
- rem 长串空格为一个 tab 制表符
复制代码
作者: qzwqzw 时间: 2014-11-27 10:41
本帖最后由 qzwqzw 于 2014-11-27 10:52 编辑
现在基本可以确定
| 和>这些符号会影响cmd的控制台IO模式
而这些模式会影响等控制台标准IO函数的行为
主要包括ReadFile/ReadConsole,WriteFile/WriteConsole
3,8,9楼的代码
主要是因为ENABLE_PROCESSED_OUTPUT模式被置假
导致WriteFile/WriteConsole不再特殊处理屏幕缓冲区中的控制区字符
可用的模式共- ENABLE_ECHO_INPUT 0x0004
- ENABLE_EXTENDED_FLAGS 0x0080
- ENABLE_INSERT_MODE 0x0020
- ENABLE_LINE_INPUT 0x0002
- ENABLE_MOUSE_INPUT 0x0010
- ENABLE_PROCESSED_INPUT 0x0001
- ENABLE_QUICK_EDIT_MODE 0x0040
- ENABLE_WINDOW_INPUT 0x0008
-
- ENABLE_PROCESSED_OUTPUT 0x0001
- ENABLE_WRAP_AT_EOL_OUTPUT 0x0002
复制代码
参考链接
http://msdn.microsoft.com/en-us/library/ms686033(VS.85).aspx
作者: amwfjhh 时间: 2014-11-27 12:56
让我想起了批处理内幕:句柄 那篇帖子了,后面有一个句柄转换后把con重新指给1句柄的,那里面就出现了cls控制字符的显示,看来根源也在这里。
作者: qzwqzw 时间: 2014-11-28 17:39
本帖最后由 qzwqzw 于 2014-11-28 17:44 编辑
回复 11# amwfjhh
让我想起了批处理内幕:句柄 那篇帖子了,后面有一个句柄转换后把con重新指给1句柄的,那里面就出现了cls控制字符的显示,看来根源也在这里。
你说的应该是《重定向中的秘密(句柄备份)》的27楼的问题
http://www.bathome.net/redirect. ... 92&fromuid=3023
两个问题其实并非同源
那里的问题主要是因为con并非完全仿真的控制台
导致cls以及dir/p等命令识别出了这个特殊设备
所以在显示逻辑上做了例外处理
在真正的控制台上cls不会输出0x0c的字符
这里的问题主要是管道、重定向以及括号字符的联用
导致控制台的IO模式发生变化
最终忽略了缓冲区中控制字符的特殊处理
你可以注意到两个问题的命令行用法并不完全相同
echo.>con并不会输出0x0d 0x0a的图形字符
echo+0x0c(可以通过Ctrl-L输入)也不会执行清屏
作者: amwfjhh 时间: 2014-11-28 17:44
回复 12# qzwqzw
(echo,|set a=&cls)>con
作者: qzwqzw 时间: 2014-11-28 17:52
本帖最后由 qzwqzw 于 2014-11-28 17:54 编辑
回复 13# amwfjhh
你想通过这段代码说明什么?
代码中>con会将标准输出重定向到con
而cls执行时会探测到标准输出不再是原来的控制台
因此直接输出字符了事
作者: CrLf 时间: 2014-11-28 18:37
话说句柄默认输出就是标准设备,那么可以这样:- (cls 1>&2)>nul
- pause
-
- (cls 1>&3)>nul
- pause
复制代码
测试中从句柄 4 开始都不能放在 >& 之后,会报这个错误:复制代码
作者: CrLf 时间: 2014-11-28 19:32
猜测句柄 4~9 的初始值不是有效句柄
做了一个测试试图证明当初始句柄不是有效句柄时,产生的备份句柄会保留,却得到了奇怪的结果:
A、测试中 3 和 4 为备份句柄:
3=1,4=2
而结束备份后句柄 4 仍无效- echo test >nul 2>nul
- echo 无效句柄 1>&4 2>con
复制代码
B、
测试中 3、4、5 为备份句柄:
3=1,4=2,5=3
结束备份后句柄 4 变成 3 所备份的状态- echo test >nul 2>nul 3>nul
- echo 变成句柄3(nul) 1>&4 2>con
复制代码
- echo test >nul 2>nul 3>con
- echo 变成句柄3(con) 1>&4 2>con
复制代码
C、
测试中 3、4、5 为备份句柄:
3=1,4=2,5=3
结束备份后句柄 4 变成 nul
既不是 3 也不是 2,违背测试 C 体现的规律- echo test >nul 2>&1 3>&1
- echo 变成 nul 1>&4 2>con
复制代码
D、
这两个测试中 3、4、5 为备份句柄:
3=1,4=2,5=3
结束备份后句柄 4 变成 1 的原始状态- echo test >nul 3>nul 4>nul
- echo 正确 1>&4 2>con
复制代码
E、
这两个测试中 4、5 为备份句柄:
4=3,5=4
结束备份后句柄 4 仍是无效句柄- echo test 3>nul 4>nul
- echo 错误 1>&4 2>con
复制代码
作者: CrLf 时间: 2014-11-28 19:33
有人能看出 16 楼测试结果的隐含规律吗?
作者: CrLf 时间: 2014-11-28 20:05
本帖最后由 CrLf 于 2014-11-28 20:26 编辑
发现前面的说法有误,比较以下三个测试:复制代码
复制代码
复制代码
看来句柄 3~9 的初始状态都是无效句柄
是先发生备份(想过去这也是理所当然的),句柄 3 保存了句柄 1 的值,再执行了 1>&3,把句柄 3 的值借给句柄 1,命令执行完毕后,句柄 3 把备份还给句柄 1,所以看起来句柄 3 仿佛默认等于句柄 1 的初始状态
作者: qzwqzw 时间: 2014-11-28 21:47
把以前的老帖翻出来炒冷饭- :: 句柄模拟.cmd - 模拟运算句柄操作后的句柄状态
- :: qzwqzw - 2009-6-12
- @echo off & setlocal EnableDelayedExpansion
- if [%1]==[] (
- echo. 这是句柄操作示例, 模拟运算进行几次句柄操作后的句柄状态
- echo --------------------- call %~n0 ">nul" "2>nul" ---------------------
- call %0 ">nul" "2>nul"
- set 句柄
- echo.
- echo --------------------- call %~n0 ">nul" "2>nul" "3>nul" ---------------------
- call %0 ">nul" "2>nul" "3>nul"
- set 句柄
- echo.
- echo --------------------- call %~n0 ">nul" "2>&1" "3>&1" ---------------------
- call %0 ">nul" "2>&1" "3>&1"
- set 句柄
- echo.
- echo --------------------- call %~n0 ">nul" "3>nul" "4>nul" ---------------------
- call %0 ">nul" "3>nul" "4>nul"
- set 句柄
- echo.
- echo --------------------- call %~n0 "3>nul" "4>nul" ---------------------
- call %0 "3>nul" "4>nul"
- set 句柄
- echo.
- pause
- goto :eof
- )
- set "0=con" & set "1=con" & set "2=con"
- for %%h in (%句柄%) do set %%h
- echo.----- 起始状态 Initiation state -----
- for %%h in (0 1 2 3 4 5 6 7 8 9) do set/p=%%h=!%%h! <nul
- echo.
-
- echo.----- 预处理 Pre-process -----
- :loop
- if [%1]==[] goto :end
- echo.----- %1 -------
- set "_tmp=%~1"
- if "%_tmp:~0,1%"=="<" set "_tmp=0%_tmp%"
- if "%_tmp:~0,1%"==">" set "_tmp=1%_tmp%"
- set _句柄=%_tmp:~0,1%
- set _句柄_2=
- set _dev=
- if "%_tmp:~2,1%"=="&" (set _句柄_2=%_tmp:~3%) else (set _dev=%_tmp:~2%)
- for %%h in (0 1 2 3 4 5 6 7 8 9) do (
- if "!%%h!"=="" (
- set/p=b: %_句柄%[!%_句柄%!]=^>%%h[!%%h!] : <nul
- set "%%h=!%_句柄%!"
- set bak_%%h=%_句柄%
- goto :set
- )
- )
- :set
- for %%h in (0 1 2 3 4 5 6 7 8 9) do set/p=%%h=!%%h! <nul
- echo.
- if defined _dev (
- set/p=a: %_dev%=^>%_句柄%[!%_句柄%!] : <nul
- set %_句柄%=%_dev%
- ) else if defined _句柄_2 (
- set/p=a: %_句柄_2%[!%_句柄_2%!]=^>%_句柄%[!%_句柄%!] : <nul
- set %_句柄%=!%_句柄_2%!
- )
- for %%h in (0 1 2 3 4 5 6 7 8 9) do set/p=%%h=!%%h! <nul
- echo.
- shift
- goto :loop
-
- :end
- echo.----- 后处理 Post-process -----
- for %%h in (0 1 2 3 4 5 6 7 8 9) do (
- if not "!bak_%%h!"=="" (
- set/p=r: !bak_%%h![]^<=%%h[!%%h!] : <nul
- set !bak_%%h!=!%%h!
- set %%h=
- set bak_%%h=
- for %%h in (0 1 2 3 4 5 6 7 8 9) do set/p=%%h=!%%h! <nul
- echo.
- )
- )
- set 句柄=
- for %%h in (0 1 2 3 4 5 6 7 8 9) do set 句柄=!句柄! "%%h=!%%h!"
- endlocal & set 句柄=%句柄%
- echo.----- 结束状态 End Status -----
- for %%h in (%句柄%) do set/p=%%~h <nul
- echo.
复制代码
作者: amwfjhh 时间: 2014-11-29 00:34
回复 14# qzwqzw
举那个例子可能有点不合适,因为(echo,|cls)>con与cls>con是一样的效果,但是下面这两句就不一样了。复制代码
复制代码
但是若用句柄备份的思想来看待这个问题的话,那么可以再把这里的错位再还原回去:复制代码
它们表现出的现象何其相似,并且可以用这个方式来解释出现的原因并将之还原回去,所以我才说所谓的句柄备份是否背后的原因也是触发了你这里面提到的模式置换。
作者: CrLf 时间: 2014-11-29 00:57
回复 20# amwfjhh 复制代码
相同句柄的重定向只以最后一次为准,经过预处理,这里实际执行的重定向只有 1>&3,而句柄 3 是句柄 1 的备份,所以句柄 1 的重定向结果还是原来的句柄 1
作者: amwfjhh 时间: 2014-11-29 02:35
回复 21# CrLf
说的不错。我的理解是:复制代码
当命令的执行结果被重定向到下一个非接收流的命令,并且过程被括起来重定向到con后,其实是相当于标准输出被重定向到了数据流,而数据流又被重定向到了con(这个非原始标准输出),则所有的输出内容都被看着流数据对待,特殊控制字符不再有其原始的控制效能,比如清屏,beep等,这个时候的输出内容就有点类似于debug的d命令右边显示字符的那块了,所有字节被看成普通文本对待,能显示就显示,不能显示留空跳过。当然它们的具体显示效果不同。
欢迎光临 批处理之家 (http://bbs.bathome.net/) |
Powered by Discuz! 7.2 |