批处理技术内幕 ECHO命令介绍

所属分类: 脚本专栏 / DOS/BAT 阅读数: 1729
收藏 0 赞 0 分享
众所周知,如果echo后面跟一个环境变量,但是该变量却为空时,相当于不加任何参数的echo,即输出当前echo是on还是off。很多文章或者教程给出的解决方案都是在echo后面加一个点号echo.,这样就会输出空行。
复制代码 代码如下:

@echo off
echo %demon.tw%
:: ECHO is off.
echo.%demon.tw%

pause据我所知,用echo输出空行至少有十种方法:
复制代码 代码如下:

@echo off

echo=
echo,
echo;

echo+
echo/
echo[
echo]

echo:
echo.
echo\
pause

这十种方法可以分为三组,每组的效率依次递减。可悲的是,那些被奉为经典的教程给出的却是效率最低那组中的echo.

echo.不仅效率低下,而且还容易引发错误:
复制代码 代码如下:

@echo off
cd .>echo
echo.
pause

我知道你很难接受,但事实的确如此。

第一组中echo后面的=,;都是批处理中的分隔符,所以CMD可以正确地解析出echo命令,并把=,;作为echo命令的参数。是的,你没有看错,分隔符并不是用来分隔命令与参数,它们通常是参数的一部分。既然是参数,那么为什么不会被输出?那是因为echo命令直接跳过了参数的第一个字符,从第二个字符开始输出,而第二个字符是NUL,所以输出了空行。

你可能又要问,那为什么用空格做分隔符却不能输出空行呢?那是因为在输出之前,CMD要检查echo命令的参数是不是on或者off,或者参数为空:首先跳过所有空白字符,如果跳过之后字符串就结束了,那么就认为没有加参数,输出echo是on还是off;如果字符串没有结束,就调用wcsnicmp函数来判断剩下的字符串是否为on或者off,进而修改echo的状态。

因此加上很多空格也是一样的效果:
复制代码 代码如下:

@echo off
echo
echo on
echo
pause

而对于第二和第三组,事情就没那么简单了,由于echo后面跟的并不是分隔符,所以解析之后会被当成一个整体,而echo+ echo/等等显然又不是内部命令,CMD会把它们当做外部命令进行搜索。嗯,你知道,搜索是很花时间的,这就是为什么它们的效率低于第一组。

可惜的是,CMD花了很大力气搜索,却仍然找不到这样的外部命令,这时候它会尝试着修复(Fix)命令,看看命令中是否有某些字符(如图):

FindAndFix
可以看到,CMD对:.\的处理跟+[]/不太一样,如果是+[]/,CMD会直接把它们从命令中删除并且添加到原有参数的前面;而如果是:.\并且CMD拓展是开启的话,那么会多调用一次GetFileAttributes函数获取文件属性,多调用一次函数自然会多花一些时间,所以第三组的效率又稍稍比第二组的低些。 GetFileAttributes

再来解释一下为什么echo.有时候会引起错误。文件名中是不能出现:.\的,理论上GetFileAttributes函数都应该返回-1(INVALID_FILE_ATTRIBUTES),然而事实却不是如此,我也不知道这算不算GetFileAttributes函数的BUG:

复制代码 代码如下:

#include <stdio.h>
#include <windows.h>

int main()
{
FILE *fp = fopen("echo", "wb");
fclose(fp);
printf("0x%x\n", GetFileAttributes("echo:"));
printf("0x%x\n", GetFileAttributes("echo."));
printf("0x%x\n", GetFileAttributes("echo/"));
return 0;
}

如果你测试一下上面的C程序,就会发现echo.那行返回的不是-1。

如果GetFileAttributes函数返回的不是-1(一般表示文件不存在),也不是0×10(表示文件是文件夹),那么命令还是会保持原来的样子,当成外部命令运行。
复制代码 代码如下:

@echo off
cd .>echo
echo.
pause

‘echo.' is not recognized as an internal or external command, operable program or batch file.
复制代码 代码如下:

@echo off
cd .>echo
setlocal disableextensions
echo.
pause

关闭了CMD拓展,没有问题。
复制代码 代码如下:

@echo off
md echo
echo.
pause

echo是文件夹而不是文件,没有问题。

最后总结一下吧,在大部分情况下,你都应该使用第一组的echo, echo; echo=来进行输出,它们的效率跟echo (空格)是一样的,并且可以用来输出on或者off,在变量为空时还能输出空行。

但是echo, echo; echo=却不能输出以/?开头的行,如果你需要,可以使用第二组的echo+ echo/ echo[ echo],它们的效率低一些,但能保证原样输出。

我不建议你使用第三组的echo: echo. echo\,如果你仍然要像垃圾教程里面那样用,我也没有办法。
作者: Demon
链接: http://demon.tw/reverse/cmd-internal-echo.html

更多精彩内容其他人还在看

for 语句从入门到精通

在批处理中,for是最为强大的命令语句,它的出现,使得解析文本内容、遍历文件路径、数值递增/递减等操作成为可能
收藏 0 赞 0 分享

dos 目录跳转 cd

当我们需要处理不同路径下的文件的时候,很可能需要切换目录,这个时候,可以考虑使用目录跳转命令cd。
收藏 0 赞 0 分享

tree 以树形格式罗列文件

tree ,在英语中的基本含义是“树”,在cmd中,tree命令的功能是以树形格式罗列文件。
收藏 0 赞 0 分享

dos 内容重定向

当我们在cmd窗口中查询某条命令的帮助信息的时候,帮助信息是显示在命令行窗口中的,命令行窗口关闭后,这些帮助信息就看不到了,如果下次还想看,又得在命令行窗口中输入查询命令,比较繁琐。
收藏 0 赞 0 分享

执行批处理bat程序中的条件处理

一直用bat实现部分功能,对于我们仍需要控制是否满意我们的要求,下面是if帮助文档,方便查询
收藏 0 赞 0 分享

不错的批处理脚本 第一部分

非常不错的批处理脚本代码,功能比较多,用到了,很多的批处理机器
收藏 0 赞 0 分享

不错的批处理脚本实例代码 第二部分

不错的批处脚本实例代码,用到了批处理中的很多技巧与知识点,不懂得可以逐一查找相关资料
收藏 0 赞 0 分享

开机更新桌面主题的批处理代码

更新桌面主题的批处理代码
收藏 0 赞 0 分享

非常好的for 教程, 当时我就是看这个学习for 的第1/2页

批处理for命令详解 FOR这条命令基本上都被用来处理文本,但还有其他一些好用的功能! 看看他的基本格式(这里我引用的是批处理中的格式,直接在命令行只需要一个%号)
收藏 0 赞 0 分享

批处理的"循环"效果脚本

曾经在回答一个问题时 无意中想到的方法 今天又看到类似的问题 个人认为是非常实用的 于是 想把这种方法推荐给大家
收藏 0 赞 0 分享
查看更多