[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖

[原创] 批处理重定向中的秘密(句柄备份)

看了 [ 小竹英雄 ] 的 “重定向中的秘密” 一文,搞得我云里雾里,真就是那饭桌上的美食转哪转——晕菜了。
所以写此文与各位看看,有什么不对的地方请大家指正。


重定向是指使用重定向符号对输入或输出位置进行重新指定。
以下是理论说明,懂的可以跳过,也可以先看例子,遇到不明白的再倒回来看。
重定向符号有 < 、> 、>> 、<& 、>& 和 | 共六个。

重定向符号的作用如下表所示:

符号   作用
——————————————————————————————
<   从文件或设备(如默认的 con 键盘)中读取命令输入。
>   将命令输出写入到文件或设备(如 prn 打印机)中。
>>   将命令输出添加到文件末尾而不删除文件中的信息。
<&  从后一个句柄读取输入并写入到前一个句柄输出中。
>&  将前一个句柄的输出写成后一个句柄的输入。
|   读取前一个命令中的输出作为后一个命令的输入。也称作管道符。
——————————————————————————————
注意:句柄的输入输出只是其指向,真正的输入输出是靠设备。
重定向符号的默认句柄,< 的默认句柄是 0,> 的默认句柄是 1。

可用句柄(0 - 9)如下表:

句柄    句柄号   说明
——————————————————————————————
STDIN    0     标准输入,默认从键盘输入
STDOUT   1    标准输出,默认输出到命令提示符窗口
STDERR   2       标准错误输出,默认输出到命令提示符窗口
UNDEFINED 3-9     这些句柄由应用程序和各个具体工具单独定义。
——————————————————————————————
STD 即 Standard —— 标准的缩写。

设备
标准输入设备为键盘,用 con 表示。
标准输出设备为控制台(即命令提示符),默认也用 con 表示。
常用的还有:
空设备,用 nul 表示。
存储设备,文件就归到这类。
不常用的如:打印机 prn。

指向
句柄实际上也是一组 0 1 数据,是存储了一个指向。
句柄 0 默认指向 con,这里 con 为标准输入设备,即键盘。
句柄 1 默认指向 con,这里 con 为标准输出设备,即控制台。
句柄 2 默认也指向 con,同 1。
此外,我们对句柄的指向重新设定(即所谓的重定向)时,可以改变句柄的指向。
如 1>nul 是让“标准输出”数据输出到空设备,效果就是屏蔽输出。
特别之处: 3 - 9 默认是没有指向的,我们可以用(空)来表示,注意是不同于空设备 nul 的哦!


(图示)

下面拿个简单的例子来说明
pause>nul
后面的 >nul 的作用就是屏蔽了 pause 命令的默认输出 “请按任意键继续. . .”。
以上是简单的描述,而实际的过程可没那么简单:
首先确定重定向符号为 >;
接着检查句柄号,发现没有,控制台为其加上默认句柄 1,此时变成 pause 1>nul;
由于句柄 1 的默认指向 con,此时要被临时设定为指向 nul,为了之后取回原来的指向,所以要先对 1 的指向进行备份,备份到句柄 3,备份过后临时设定 1 指向 nul;
(为什么要备份,为什么要备份到 3 而不是 4?见下文)
到这时这条语句才被执行,效果就是批处理暂停,没有输出只有一个光标闪阿闪。
执行完后 1 要取回原来的指向,1 的指向在上面被备份到 3,不管 3 的指向有没有改变 1 要取回它的指向来,取回后 1 指向 con;3 的指向被取走,由于 3 没有备份,所以恢复到原来的(空),即无指向。
至此整个过程描述完毕。
可以发现这时的情况与最初是相同的,所有句柄的指向都是其默认值。

  接着引出 句柄(指向)备份理论
  1. 当指向为非空的句柄被重新设置时要进行备份,而且是备份到第一个没有指向的句柄。
  2. 语句运行完后要取回其备份,而不管备份中的句柄指向有没有改变。
  3. 如果当前状态下 3 - 9 这七个句柄都有了指向,这时新设置的句柄其原指向将不再备份。
复制代码
  1. 另一种表述:也就是说当指向为(空)的句柄被设定时是不需要备份的。如:复制句柄时源句柄不备份,因为没有被再次设置。
  2. 补充说明:一个句柄只能存储一个指向。
复制代码
举例说明:


示例三与示例二有什么异同呢?
基本上相同,只有执行的这句,示例三是有输出的,示例二却没有。

有没有发现示例四和示例三是一样的,那用复制句柄的方法能不能和示例二一样呢?
可以,自己想吧。^_^

还有一个特别的例子
echo 你好!>nul >hello.txt >prn >con
它使用的是相同的句柄 1,这时要以最后一个为准,其他的忽略。把它存为批处理运行,能够清楚的看到前面的三种指向直接被剪掉了。
同理:
echo 你好! 1<&3 >nul
也以后面的 ”>nul“ 为准,即不显示 ”你好!“。

具体例子讲解
  1. @echo off
  2. echo 哇,这就是传说中的树叶吗?>con 3>nul
  3. echo 我有一片神奇的树叶(吹口哨),
  4. echo 你看不见我,
  5. echo 我现身,>con
  6. echo 我又隐身啦。
  7. echo 谁偷了我的叶子,快还我。>con 4>con
  8. echo 没穿裤裤被你们看到了!
  9. pause>nul
复制代码
哇,这就是传说中的树叶吗?
这句用了 “>con >nul” 同示例三,首先 句柄 1 指向要被设为 con,先备份到 3 再设定;第二步 这时 3 指向为 con,现在又要设为 nul,先备份 con 到 4;最后 1 指向 con,3 指向 nul,4 指向 con;所以这句执行时是要显示的。
执行完后返回,1 取回 3 的指向 nul,3 取回 4 的指向 con,4 恢复(空),所以返回结束时 1 指向 nul,3 指向 con。假设这时状态为 A。
因此接下来两句 “我有一片神奇的树叶(吹口哨),”,“你看不见我,” 是不显示的。
我现身,” 这句设定为 “>con”。执行时 1 为 con,原来的 nul 备份到 4(因为前面 3 已经为 con 不是没有指向了);执行完后 1 取回备份 nul,4 恢复(空)。这时的状态还是 A。
所以下一句 “我又隐身啦。” 还是不显示。
倒数第二句 “谁偷了我的叶子,快还我。
设定为 “>con 4>con”,首先 句柄 1 指向要被设为 con,3 已经不为(空)了,所以备份 nul 到 4;第二步 这时 4 指向为 nul,现在又要设为 con,先备份 nul 到 5;最后 1 指向 con(3 指向 nul)4 指向 con,5 指向 nul;所以这句显示。
返回时 1 取回 4 的指向 con,4 取回 5 的指向 nul,5 恢复(空)。而这时状态已经不同于 A 了,设为状态 B。最后一句 “没穿裤裤被你们看到了!” 使用的是状态 B 的句柄指向,所以也显示。

到些讲解结束。(以上红色为显示的淡蓝色为不显示的

如果要用处理来表示就是这样


补充
我们可以比较一下下面两句的差别
echo 你好!>con 2>con
echo 你好!
有人会问了 “你不是说句柄 1 和 2 默认指向为 con 吗?”
没错,我来解释一下。
第一句对句柄 1 和 2 进行了重新设定(虽然是和默认一样的),因此要有备份和取回的过程;
而第二句没有设定,直接取默认值作为当前设定(不需要备份什么的哦);
这样第二句就比第一句快一点,如果在一个循环多次的程序中使用,速度差别就很明显了。

应用
一次性屏蔽错误输出(加在开始屏蔽的语句后面)
2>nul 3>nul

一次性将输出写入文件 a.txt(不包括错误反馈,用法同上)
>&3 3>a.txt

  此帖中就有应用(是一次性屏蔽错误输出的,大大加快了程序的运行速度)
  http://bbs.bathome.net/viewthread.php?tid=2372&page=1#pid14024


以上一些个不实用的东西大家没必要深究,了解了解就行了,应用为上嘛。
实在需要研究的人例外。

问题:经过重定向后句柄的指向已经不同于默认指向了,还能恢复成原来的默认指向吗?
回答:因为指向不能设定或复制成空,所以语句运行完后的指向如果改变了是不能回到默认指向的。
(如有错误,欢迎指出。)
参考《命令行参考》,为方便说明,部分内容摘自其中。
英雄兄的 “重定向中的秘密”,由于其中没有关于备份条件的说明,且有一些错误,容易搞得人一头雾水。英雄兄说他帖子写得快吐血,我是看他帖子看得快吐血。
以上为个人观点,信则是不信则非。
还有不会做图,否则做个GIF就更形象了。
英雄的帖子链接
http://bbs.verybat.org/thread-9271-1-1.html
http://bbs.verybat.org/thread-9744-1-1.html
附件: 您需要登录才可以下载或查看附件。没有帐号?注册
7

评分人数

总结如下:
1>a.txt   将句柄1的输出设备变为a.txt,之前输入到控制台的内容会写入到a.txt
0<a.txt   将句柄0的输出设备变为a.txt,之前需要从控制台输入内容,现在直接将a.txt中的第一行作为输入的内容
1>&3     将句柄3的输出设备作为句柄1的输出设备(&3,取到句柄3的输出设备。 如3>a.txt  1>&3  可以理解成1>a.txt)
0<&3    将句柄3的输入设备作为句柄1的输入设备(&3,取到句柄3的输入设备。 如3<a.txt  0<&3  可以理解成0<a.txt)

TOP

至于 <& ,
正常输入是这样的
  1. set /p name=输入
复制代码
控制台敲完指令后,我们输入haoren,然后再输入命令
  1. echo %name%
复制代码
控制台会显示haoren
使用 <& 是这样的
  1. set /p p=输入 3<a.txt 0<&3
复制代码
此时 控制台并不需要我们输入,我们接着敲命令
  1. echo %p%
复制代码
控制台显示的是a.txt中的第一行的内容。
由此对0<&3我们可以理解为:0句柄默认的输入设备是控制台,0<&3之后,将句柄3中的输入设备 给 句柄0. 而句柄3的输入设备我们提前设置为a.txt,所以 句柄0的输入设备就是a.txt了。

TOP

正常情况:
  1. abc
复制代码
输出如下:
  1. 'abc' 不是内部或外部命令,也不是可运行的程序
  2. 或批处理文件。
复制代码
当使用句柄复制:
  1. abc 3>nul 2>&3
复制代码
此时控制台没有输入错误
所以我推测,2>&3,先从 句柄3中取到3的指向(输出设备)。然后让句柄2 也指向 句柄3的指向,即句柄2 此时的输出设备也变为nul.

TOP

关于两句应用的个人理解:
应用
1,一次性屏蔽错误输出(加在开始屏蔽的语句后面)
2>nul 3>nul

                         1con 2con 3空
2>nul                1con 2nul 3con
3>nul                1con 2nul 3nul 4con
取回2找3,3找4     1con 2nul 3con

2,一次性将输出写入文件 a.txt(不包括错误反馈,用法同上)
>&3 3>a.txt

                        1con 2con 3空
>&3即1>&3       1con 2con 3con
3>a.txt             1con 2con 3a.txt 4con
取回1找3,3找4    1a.txt 2con 3con 4空

TOP

看了多次,好像有点明白的样子,但不清爽。不知如何使用,应用的例子太少,再来一遍吧,第5遍了。俗话说书读五遍,其义自见。

TOP

学习学习,看看是什么

TOP

0 是 stdin
1 是 stdout
2 是 stderr
写的很清楚,谢谢~

TOP

绕晕了,大概理解了句柄备份和复原
我就是我,不一样的烟火!

TOP

RE: 重定向中的秘密(句柄备份) [修改]

TOP

有趣,新发现

TOP

amwfjhh推测的挺有道理
一个简单的测试
cls > con
在DOS下执行清屏
在CMD下输出字符
这说明cmd仿真出来的con
并不是原始的控制台设备
对屏幕缓冲区的处理没有做到完全仿真
也就是在cmd下con设备更加的“字符化”了

类似的现象还有
dir | more > con
天的白色影子

TOP

回复 30# CrLf


    代码运行至此后,其实sdtout已经差不多恢复功能了,否则连前的的prompt 信息也不会显示,除了sdtin接收输入的字符能被显示出来,标准输出都不会显示,显然这里还可以继续显示其它信息的。
1

评分人数

    • CrLf: 你是对的技术 + 1

TOP

回复 29# amwfjhh


    cls 是输出了一个换页符来实现清屏的,所以如果 StdOut 被指向了 nul...

TOP

如上图,为什么执行了楼主的代码后清不了屏了??
如何还原?
919141923 发表于 2013-10-4 22:02



    只能说此con非彼con了,真正的原始con已被句柄3所使用。可将echo...>con 4>con那句改成echo...>con 4>&3

TOP

返回列表