Board logo

标题: [其他] [50条]不能说的秘密-CMD命令奇诡语法特性汇集 [打印本页]

作者: qzwqzw    时间: 2010-4-15 15:01     标题: [50条]不能说的秘密-CMD命令奇诡语法特性汇集

  谨以此文献给我生命中最重要的三个女人,在本文更新期间,是她们给了我很大的物质支持和精神鼓励,支持我最终将此文完成。同时感谢论坛管理层,尤其是批处理之家的namejm,他不仅给我提供了很多线索和建议,同时也在论坛管理上提供了很大的便利和支持。当然,也感谢所有参与讨论并给出建议的朋友们。他们分别是(按发帖时间排序):
BATHOME:neorobin、hanyeguxing、yangfengoo、x9tiancmd、随风、x9tiancmd、Batcher、lxzzr
CN-DOS:qinchun36



声明:
    BATHOME:http://bbs.bathome.net/thread-7629-1-1.html
    CNDOS:http://www.cn-dos.net/forum/viewthread.php?tid=50912

示例:
set varname=var1
set var1=value
call echo %varname%=%%%varname%%%

效果:输出var1=value,实现变量的二次扩展
注释:这种二次扩展的办法在纯DOS中比较常用,CMD下在不能使用变量延迟时也可以考虑

示例:cd.>test.txt
效果:产生一个零字节的文件test.txt
注释:零字节文件的创建,其实有更多的办法(具体见参考链接),但这种目前是最简单可靠的。
链接:http://bbs.bathome.net/thread-939-4-1.html#pid49298
http://www.cn-dos.net/forum/viewthread.php?tid=22336

示例:
md Test Sample
cd Test Sample
rd Test Sample

效果:md和rd命令正常执行,cd命令报错“系统找不到指定的路径。”
注释:md和rd命令隐含支持多个目录的创建和删除,所以把Test Sample理解成了Test和Sample两个目录,
        但cd命令只能是一个目录,而且隐含支持带空格不带引号的路径,所以将Test Sample理解成了一个目录,
        三个命令在认识上的不统一,导致了这个问题的出现。
        另外,不像command.com,cmd下的md和rd已经都支持多级目录的创建和删除了,这个在官方文档已提到。

示例:
cd.>test.html
dir /b *.htm
copy *.htm *.bak

效果:首先列出test.html文件,随后将其扩展名改为.bak
注释:文件名中有通配符且扩展名有3个字节时,无论其扩展名是否有通配符,都会对扩展名进行通配匹配,
        扩展名之后可以添加; , .等特殊字符,不影响通配匹配。
链接:http://www.cn-dos.net/forum/viewthread.php?tid=22336

示例:echo.%var%
效果:如果变量var 为空显示空行,如果不为空则显示其值
注释:echo. 官方文档解释为输出一个空行,用于文本的排版;
但更多的用于防止输出的变量为空时,echo语句出现语法错误;
. 也可以改为, + / : ; [ ] 等特殊字符
链接:http://bbs.bathome.net/thread-4482-1-1.html#pid28940

示例:
echo 文本1 ^
文本2 ^
文本3

效果:将三段文本合并到一行显示
注释:^ 将随后的行结束符取消转义,所以有其它语言中类似续行符的作用
其它命令中也可以这样使用,甚至可以用它截断命令名、文件名或者参数
只是别在引号对中这样使用

示例:set | findstr "\\\\"
效果:显示set命令输出中有一个\ 的行
注释:findstr中不论有没有开启正则,\ 都是元字符的转义字符,而且""对还会将\ 进行再次转义
链接:http://www.cn-dos.net/forum/viewthread.php?tid=21167

示例:
set/p= <nul>示例文字
findstr /a:f1 . 示例文字?
del 示例文字?

效果:显示白色背景黑色前景的“示例文字”
注释:可以多次利用此方法在一行内显示多种不同前景色和背景色的文字
在没有发现这个用途之前,findstr的开关/a可以说是“蛇足”,它在文档中虽有说明,
但语焉不详,更别说如此奇诡的用途;/a: 后的字符可以根据color /?的说明进行调整
链接:http://bbs.bathome.net/thread-1331-1-1.html#pid11864
http://groups.google.com/group/alt.msdos.batch.nt/browse_thread/thread/f819acd4582d5de3/
http://www.cn-dos.net/forum/viewthread.php?tid=35622
http://www.cn-dos.net/forum/viewthread.php?tid=38940

示例:
set/p=不含回车换行符的文本行<nul>test.txt
findstr "$" test.txt
type test.txt|findstr "$"

效果:第二句输出任何文本,第三句输出“不含回车换行符的文本行”
注释:findstr的正则与POSIX正则以及其他标准的正则都不太一样,是个简化版的正则。
        $ 在这里做为正则表达式元字符,匹配行尾的回车和换行符号,而不是真正的行尾。
        而因为测试文本没有回车符和换行符,所以第二句的findstr "$" 匹配失败。
        第三句成功匹配是因为CMD 的“后处理”为文本添加了回车换行并通过管道传给了findstr

示例:
echo 我 你 他>test.txt
findstr "我 你 他" test.txt

效果:没有输出任何文本
注释:finstr在匹配多个中文串时存在问题,使用开关/l或者/r可以得到正确的输出。

示例:
echo.>test.txt && attrib +h test.txt
echo.>test2.txt && attrib +s test2.txt
for %%i in (*.txt) do echo %%i

效果:显示结果不包含带有隐藏属性的test.txt,但包含带有系统属性的test2.txt
注释:不带/a开关的dir 是两者都不显示,for 显然对文件属性有自己的认识

示例:
md TestSample & cd TestSample & cd.>"test 1".txt
for %%f in (*.txt) do echo %%~sf

效果:在WindowsXP SP2环境下显示D:\TESTSA~1\TEST1~1.TXTt,多了一个字符t
注释:短名扩展是基于完整长名路径后的。当路径中既有长目录名,又有含空格的短文件名时,
        CMD的短名扩展不对原长名路径的长度进行修剪,导致短名路径尾部残留原长名路径的字符。
        Windows 2003以上系统无此问题。
链接:http://www.cn-dos.net/forum/viewthread.php?tid=27063

示例:
echo 1 > 1.txt & echo 2 > 2.txt
for %%f in (*.txt) do ren %%f f-%%f

效果:生成两个文件f-2.txt和f-f-1.txt ,1.txt 被改名后再次被改名
注释:在for 语句内使用ren 要谨慎,f-可以改为任意以字母开头的字符串
链接:http://bbs.bathome.net/thread-31727-1-1.html
《批处理BAT脚本中for命令的使用》第8页

示例:for /f "delims=" %%i in (test.txt) do echo %%i
效果:显示test.txt的所有行,除了空行
注释:for/f 的文本遍历会遇到空行时,替代变量%%i被赋值为空,不会执行do的语句块。
        显示空行文本的折中方案是,使用finstr或者find为文本的所有行包括空行添加一个前缀,
        待通过for/f的“防空”保护后,再用set或者tokens把这个前缀去除。如下:

示例:for /f "eol=" %%i in (test.txt) do echo %%i
效果:显示test.txt的文本内容,忽略以起始的行
注释:这个语法之所以称得上“奇诡”,主要是因为官方文档的中文化过程中,
        将“an end of line comment character”翻译成了“行尾字符”(ntcmds.chm)
        在命令行帮助中则是“行注释字符的结尾”,按字面意义理解都会理解成行尾的字符,
        因此误人不浅,实际上应该是“代表行结束的注释字符”,而且这个字符必须在行首。
        不指定eol时,for/f缺省使用分号;作为eol,所以会过滤掉文本中以分号起始的行,
        而"eol="会将双引号指定为eol,使用"eol= delims="则会将空格指定为eol。
        使用示例中的可以近似显示所有的行,因为是一个控制字符,不太可能出现在文本中。

示例:for /l %%i in (2147483635,1,2147483647) do echo %%i
效果:显示两个正数后,将从小到大显示负数,零,正数,负数,依次循环
注释:for /l使用加法,2147483647加一后变为负值,仍然满足循环条件,使之成为无限循环
链接:http://bbs.bathome.net/thread-7659-1-1.html

示例:for /r %f in (.) do @echo %f
效果:分行显示当前目录下所有子目录名,并在行尾添加\.
注释:括号中的句点还可以改为其它不含文件通配符(*,?)的字符或字符串

示例:for /r %f in (test.txt) do @echo.%f
效果:分行显示当前目录下所有子目录名,并在行尾添加\该文件名.当前目录\该文件名
注释:test.txt可以换成不含通配符的其他文件名或者字符串,echo后的.可以换成+、/等特殊字符
链接:http://bbs.bathome.net/viewthread.php?tid=3465&page=1#pid29165

示例:if "〇"=="" echo 〇为空
效果:显示结果为:〇为空
注释:在Windows XP下的936代码页下测试
链接:http://bbs.bathome.net/viewthread.php?tid=7513

示例:if "Z" leq "z" (echo "Z <= z") else echo "Z > z"
效果:显示结果表明Z大于z
注释:if的字符串比较不是按照ASCII或者Unicode码表,而是自有一套规则,详见
链接:http://bbs.bathome.net/thread-6851-1-1.html

示例:
if "e" leq "-" (echo "e <= -") else echo "e > -"
if "ef" leq "-f" (echo "ef <= -f") else echo "ef > -f"

效果:显示结果表明e大于-,而ef小于-f
注释:减号-也可以换成单引号',它们在if的数值比较语句中充当一种另类的转义字符
链接:http://bbs.bathome.net/thread-6853-1-1.html

示例:if 3 gtr -2147483645 (echo 3>-2147483645) else (echo 3<=-2147483645)
效果:显示结果表明3<=-2147483645
注释:if数值比较的核心是减法,依靠对两个数值的减法差值的正负判定大小
而差值变量是32位(DWORD)整型变量,存在-2^31~2^31-1的限定范围
而3- (-2147483645)的差值即超出此限,结果有正转负,判定也由大变小
链接:http://bbs.bathome.net/thread-7659-1-1.html

示例:if defined "test var" (echo pass) else (echo fail)
效果:不管之前有无定义变量test var,显示结果都为fail
注释:if defined判定的变量名包含空格时,不能直接使用 if defined test var echo pass
        此时,test被识别为变量名,var被识别为if的执行体语句块,如果存在变量test,则出现语法错;
        也不能如示例般使用引号对将变量名引起,否则if defined将引号看作变量名的一部分,执行else块
        此时可以使用两个方法使变量名中的空格躲过CMD预处理的词法切分逻辑,详见链接:
        1. for %%f in ("test var") do if defined %%~f (echo pass) else (echo fail)
        2. set varname=test var&if defined !varname! (echo pass) else (echo fail)
        方法2中的!varname!更改为%varname%无效,因为%的变量扩展逻辑在空格的词法切分逻辑之前
        而且变量!varname!不能使用!varname:~1,2!的引用形式,因为逗号影响了CMD预处理的词法切分逻辑
链接:http://bbs.bathome.net/viewthread.php?tid=2050

示例:
md testdir&if exist testdir\nul (echo Pass) else (echo Fail)
md "test dir"&if exist "test dir"\nul (echo Pass) else (echo Fail)

效果:第一句显示Pass,第二句显示Fail
注释:if exist通常用于判断一个路径是否存在,它不能直接分辨出目标路径究竟是目录还是文件。
        而如示例中所用的 if exist test\nul ...,则是从DOS 起广泛使用的判断目录存在的标准方案。
        它借助系统中所存在的aux con nul prn com1 lpt1等标准字符设备名判断目标路径是否指向目录,
        因为这些设备没有可以在任意目录下引用,所以“目录路径\nul”这样的引用被系统认为是有效的,
        但在CMD下,这种方法不适用于判定带引号的目录,这点与if defined "变量名" 的缺陷类似。
        此时,可以使用以下方案:if exist "test dir"\* echo Pass,通配符使

示例:
setlocal enabledelayedexpansion
for /f "delims=" %%a in ('ipconfig^|findstr /i "Address"') do set var=%%a
set CarriageReturn=!var:~-1!

效果:变量CarriageReturn被赋值为一个回车符(0x0d,\r,Cr)
注释:源于 ipconfig 的输出每行行尾都有两个回车符,在被for /f截掉一个后还能剩一个;
        引用时需要用变量延迟的形式,否则就会在预处理中被当做行结束符而被过滤掉
链接:http://bbs.bathome.net/viewthread.php?tid=6692

示例:
reg add hkcu /v test /d "测试" /f
reg query hkcu /v test | find "test"

效果:输出的Test键值丢失了一个字,只余“测”
注释:XP SP3以及以上系统的reg query没有此Unicode带来的问题,解决方法见链接
链接:http://bbs.bathome.net/thread-2046-1-1.html
http://www.cn-dos.net/forum/viewthread.php?tid=21365#pid128521
http://www.cn-dos.net/forum/viewthread.php?tid=22202
http://www.cn-dos.net/forum/viewthread.php?tid=27279#pid182590

示例:
reg add hkcu\test\"quote\"folder /f
reg query hkcu\test\"quote\"folder
reg delete hkcu\test\"quote\"folder

效果:reg添加、查询和删除的注册表键支都是Test,而不是我们设想的Test"quote"folder
注释:XP SP2的reg在注册表键值出现引号" 时可以使用\ 转义,
比如 reg add hkcu /v test /d "\"value\"" /f
但是这个转义字符不能用在注册表键支中,因为它被视作键支层级的分隔符
解决办法是升级reg.exe 为更高版本系统下的reg.exe 外
链接:http://www.bathome.net/viewthread.php?tid=7004

示例:ren test1.txt *t2*
效果:test1.txt将改名为test1.txt2,而非test2.txt
注释:在ren 的任何文件名参数中均可以使用通配符(* 和 ?)。
        如果在 FileName2 中使用通配符,则通配符代表的字符与 FileName1 中的相应字符匹配。
        类似于正则表达式中的最远匹配,但只匹配通配符后的一个字符,其余字符都属于替换字符。
        另外,如果Filename1 含有通配符,那么在NTFS和FAT32下执行会略有差异,详见以下链接。
链接:http://www.cn-dos.net/forum/viewthread.php?tid=29538

示例:md test1 & ren test1 test2
效果:生成test2目录
注释:cmd的ren命令已经悄悄支持目录的改名了。当然,移动目录仍然需要使用move命令。

示例:set/=value
效果:生成变量/,值为value
注释:set将分隔符识别为变量名的一部分,/可以改为( + , . : ; [ \ ] 等特殊字符

示例:
@echo off
set var=
echo.%errorlevel%
set var=value
echo.%errorlevel%

效果:此段代码保存为test.bat执行后分行显示“1 1”,保存为test.cmd执行后显示“1 0”
注释:第一个数字1表示:set试图删除一个不存在的变量var时发生内部错误,产生errorlevel为1
        第二个数字表示:set成功创建变量var后,.cmd文件会重置errorlevel为0,而.bat文件则不会
        也就是说,在.cmd文件中,set成功执行后会重置errorlevel为0,而在.bat文件不管errorlevel
链接:http://www.cn-dos.net/forum/viewthread.php?tid=30968&page=3#pid217157

示例:
set test 1=value
set test 2

效果:test 1的名值对
注释:set 识别单纯的变量名时会丢掉最后一个空格的字符串

示例:set "test=value > 3 "
效果:生成一个变量test,值为"value > 3 "(含空格不含引号)
注释:在变量值涉及特殊字符和尾部空格的时候,用引号对将变量名和变量值括起是个好办法
链接:http://www.cn-dos.net/forum/viewthread.php?tid=17785#pid105897

示例:set "
效果:显示所有的环境变量,其中有包括“=::=”、“=C:=”等变量名含有等号的隐含环境变量
注释:可以把双引号换成一个或多个逗号、双引号、分号或者其混合形式,双引号最好成对使用
        “=::”、“=C:”等变量其代表当前CMD会话曾访问过的各个驱动盘下的当前目录。
        因变量名中包含等号=,正常情况下,set无法对其显示和修改。
        也可能会出现变量“=ExitCode=”,代表CMD所调用外部程序的错误返回码(%Errorlevel%)
链接:http://bbs.bathome.net/thread-7696-1-1.html

        
示例:
set LineFeed=^


效果:变量LineFeed被赋值为一个换行符(0x0a,\n,Lf)
注释:注意set下的两个空行不可少,也就是^后需要三个换行符;
        第一个和第三个换行符被当做行结束标志,第二个被^转义成普通字符;
        引用时需要用变量延迟的形式,否则就会在预处理中被当做行结束符而被过滤掉
链接:http://bbs.bathome.net/viewthread.php?tid=6692

示例:set/a n=1,n=!n!+7
效果:显示n=7,而不是8
注释:!!对的变量延迟语法是针对语句块内的多条语句的,
        而set/a 的逗号表达式是一个语句,而不是语句块,自然不能实现延迟
        解决的办法就是让set/a 自己处理: set/a n=1,n=n+7

示例:
set /a var=_abc
set /a var2=1a

效果:第一句会将变量var赋值为0,第二句会报“无效数字”的语法错,且不会对变量var2赋值
注释:第一句set/a将_abc识别为变量名,因找不到对应变量,所以赋值结果为0;
        第二句set/a将1a识别为操作数,而它又不是一个合法数值,所以提示语法错误,并置errorlevel为9167。
        set/a的判断变量名和操作数的标准类似高级语言:字符串以数字起始是操作数,否则为变量名。
        此外,判断一个字符串是否是合法的十进制数值的方法如下:
        set /a _var=%var% 2>nul
        if "%_var%"=="%var%" echo %var%是合法的十进制数值

示例:set /p test=Text without CrLF(\r\n)<nul
效果:显示一串文本,不含回车换行符
注释:多次使用set /p可以将多串文本输出在一行中,也用在重定向输出过程中

示例:set /p test=<test.txt
效果:将变量test赋值为test.txt的第一行内容
注释:利用了set/p 的重定向输入特性,文本中的回车换行符被cmd理解为语句结束符
        因此可以利用这点切分出文本首行,进而在循环中结合findstr切分出文本的每一行。
        提示:set/p最多可以获取1024个字节作为变量,而环境变量长度限制为8192字节。
链接:https://mp.weixin.qq.com/s/95FNcguB_j22Kfm-l_I-4Q

示例:for /l %%i in (1,1,33) do setlocal
效果:显示“已经达到最大的 setlocal 递归层。”
注释:“使用 setlocal 和 endlocal 命令,可以在 Cmd.exe 的实例中(或在脚本中)进一步将更改局部化。
        Setlocal 创建局部作用范围,而 endlocal 终止局部作用范围。在 setlocal 和 endlocal 作用范围
        内所做的更改将会被放弃,从而保持原始环境不变。这两个命令的嵌套最高可达到 32 级。”
        此段描述引用自官方文档(ntcmds.chm::/ntcmds_shelloverview.htm)
        setlocal意味着重新复制一份环境变量到新的内存地址,而系统只预留了32份地址空间,
        所以环境变量复制份数不能超过32份,也就意味着setlocal的递归次数不能超过32级。
        因此,适时使用endlocal回收不再使用的地址空间不仅是严谨的,也是必要的。

示例:subst ~: C:\Windows & ~:
效果:创建虚拟驱动器~,并跳转到该驱动器下
注释:subst 用于创建、显示和删除虚拟驱动器,该驱动器将指向其它真实或虚拟驱动器文件夹的路径,
        虚拟驱动器的盘符可以是字母、数字以及一些特殊字符,但非字母的盘符不会出现在subst的显示列表中。

示例:type *.txt *.log
效果:显示当前目录下所有文本文件和日志文件的内容
注释:在COMMAND下type是不支持文件通配符的,在CMD下悄悄的支持了

示例:
cd >nul 3>nul
echo Test1
cd >con 4>con
echo Test2

效果:第一句后不再显示以后命令的输出Test1,第二句后恢复显示以后命令的输出Test2
注释:源于重定向后句柄的备份机制,两句中的第一个NUL和CON可以换成其他文件名或者设备名,详见
链接:http://www.cn-dos.net/forum/viewthread.php?tid=16942
http://bbs.bathome.net/viewthread.php?tid=2579

示例:
(
    echo 第一行
    echo 第二行
    echo 第三行
)> test.txt

效果:将圆括号中的所有echo输出重定向到指定的文件test.txt
注释:语句块的重定向使文件句柄的创建或销毁次数减少,因此可以提升执行效率,书写上也更加简单
链接:http://www.cn-dos.net/forum/viewthread.php?tid=46717

示例:
(echo test)|findstr /c:" "

效果:显示test,findstr认为前面的命令输出含有空格
注释:CMD的预处理在分析语句时,会在)和|这些有特殊意义的转义字符前后插入空格,同时剪除多余的空格。
        而当)和|一同出现时,CMD的预处理没有把这些空格从命令行中全部过滤掉,而会遗漏一个空格到echo命令中。
        而echo命令则把这个空格连通之前文本一同输出到管道后命令findstr中,所以导致findstr匹配出含空格行。
链接:http://www.bathome.net/viewthread.php?tid=7629&page=3#pid50356
        http://www.bathome.net/viewthread.php?tid=1480&page=4#pid28166

示例:
>>test.txt echo 行 1
echo>>test.txt 行 2
echo 行>>test.txt 3

效果:将三行输出分别输出到test.txt中
注释:重定向符号可以出现语句中的很多地方,前提是不影响输入输出的正常语法;
        在上面提到语句块的重定向特性出现之前,在纯DOS的环境中行1 的用法更多的被使用,
        因为它使输出语句看起来更加整齐,另外在CMD下也可以避免在行尾出现单独的数字时,
        这个数字可能与重定向符号被一起理解为某个句柄的重定向,而出现意想不到的输出。

示例:
echo.>time.com
time.com

效果:显示COMMAND中TIME命令的输出,并等待用户输入
注释:time可以换成其它COMMAND支持的内部命令名。
对于.COM和没有PE标记的.EXE,Windows都将其认为16位DOS程序,会自动调用其
16位MS-DOS子系统(NTVDM.EXE)解释执行该程序,如果它与COMMAND的内部命令同名,
则优先调用该命令。

示例:
set var=value
:: %var:*=%

效果:显示错误信息“此时不应有 =%”
注释:::虽然被当作注释符号在使用,但是请别忘记它本质上仍然是批处理标签语句,
        cmd在解释执行时也是需要预处理的,虽然只是少量的,但出错也是会提示的。
链接:http://www.cn-dos.net/forum/viewthread.php?tid=40242

示例:
cmd /u /c echo Unicode Text> test.txt & type test.txt
cmd /u /c echo Unicode Text|findstr ".*"
cmd /u /c echo Unicode Text|find /v ""
cmd /u /c echo Unicode Text|more
cmd /u /c echo Unicode Text|sort

效果:前两行命令输出“  n i c o d e   T e x t”的文本,三四行命令将逐字分行输出
        测试文本,并在尾部附五个空行,第五条命令将显示“Unicode”原文。       
注释:cmd /u用于使命令输出或管道中的文本成为Unicode编码,对于纯英文文本,其Unicode
        编码只使用一个高字节,另外一个字节以0x00填补。对于此编码各文本命令支持有很多差异。
        type命令虽然支持显示Unicode文本,但需要文本前缀包含有Unicode的“FFFE”的BOM标记,
        对于cmd /u /c输出的无BOM的Unicode文本将按照ANSI标准输出,字符间的00字节不做转换。
        findstr的特性与type类似,但需要注意的是,它将无法处理重定向输入的Unicode文本。
        而这段文本通过管道送到more和find命令之后,它们会将0x00理解为行结束而输出一个换行符。
        文本后的五个空行,分别是从文本中的回车符、换行符以及它们之间的两个0x00字符转换而来,
        最后一个则是CMD 的“后处理”为文本自动添加的。
        而sort则会将Unicode文本转换为ANSI文本,所以会显示正确的文本,即使其中不包含BOM标记。

示例:mem
效果:首次在CMD窗口中执行会清空窗口,并显示十一个空行,提示符中的长名路径切换为短名;
        第二次执行不会清空窗口,但仍会显示十一个空行;以后执行均只显示一个空行。
        如果在CMD的全屏窗口中执行时,则会正常显示输出结果。
注释:mem.exe是16位DOS 程序,在CMD中调用之前会启用Windows的16位MS-DOS子系统(NTVDM/WOW),
        模拟mem.exe程序类似MSDOS的运行环境,因此导致CMD窗口的输入或输出机制均发生较大的变化,
        首先这个模拟子系统的键盘布局缺省只支持“美式英语的美式键盘”,而大多定制的中文系统已将
        该键盘布局从“文字服务和输入语言”中删除,所以会报“Invalid keyboard code specified”,
        并自动切换回该缺省方案。其次,模拟子系统缺省只支持美式英语的代码页(437),而中文系统下
        的CMD是简体中文的代码页(936),所以会将CMD的活动代码页切换为437,并切换屏幕显示字体,
        因此导致清屏动作,因为以上两个动作是快速发生,所以用户一般不会观察到键盘布局错误的显示。
        如果使用mem>c:\mem.txt重定向命令的输出,则会在输出文本的第一行发现该错误信息。
        在CONFIG.NT中使用COUNTRY命令可以更改模拟DOS环境的国家代码和字符集代码页。
        在AUTOEXEC.NT中使用KB16命令可以更改模拟DOS环境的键盘方案,但不支持中文键盘方案的代码。
链接:http://www.cn-dos.net/forum/viewthread.php?tid=9452

作者: neorobin    时间: 2010-4-16 14:47

补充一个字符串比较中的问题, 关键是比较时对 短横线 - 和 单引号 ' 的处理问题
  1. if "e" leq "-" (echo 小于) else echo 大于
复制代码
显示的是 大于, 表明 字母 e 是大于短横线 - 的
那么, 字符串 "ef" 也应该是大于 "-f" 的, 可是其实不然
  1. if "ef" leq "-f" (echo 小于) else echo 大于
复制代码
显示的却是 小于
上面所述中, 将短横线换作单引号也得到同样结果, 究竟含有 短横线 或 单引号 的字符串在比较时是如何处理的?
我对此问题的分析和猜测请见: http://bbs.bathome.net/thread-6853-1-1.html
作者: neorobin    时间: 2010-4-16 15:57

在不考虑 4 楼所述 短横线 单引号 在 if 字符串比较中的问题时, if 字符串比较时, ASCII 字符的大小也不是按 ASCII 字符代码值来排序的, 我也从未找到过相关的官方文档说明, 我对其次序作了一个初步的分析: http://bbs.bathome.net/thread-6851-1-1.html
作者: hanyeguxing    时间: 2010-4-16 18:13

“〇”算不上特殊字符,但在936代码页(默认),xp sp3系统下,if 会认定该字符为空。
http://bbs.bathome.net/viewthrea ... ight=%2Bhanyeguxing
  1. @echo off
  2. if "〇"=="" (echo 〇为空)else echo 〇不为空
  3. if "12〇34"=="1234" (echo 相同)else echo 不同
  4. if 12〇34==1234 (echo 相同)else echo 不同
  5. pause
复制代码

作者: yangfengoo    时间: 2010-4-16 22:44

我也贡献个
  1. M:\世界之窗>echo.g>ttbb.ttbb
  2. M:\世界之窗>echo.g>ttbb.ttb
  3. M:\世界之窗>echo.g>ttbb.ttbbb
  4. M:\世界之窗>dir /b *.ttb
  5. ttbb.ttb
  6. ttbb.ttbb
  7. ttbb.ttbbb
复制代码
由此可见 cmd 识别扩展名只看前三位。扩展名小于等于3位是完全匹配,大于3位只匹配前三位
作者: hanyeguxing    时间: 2010-4-17 15:11

1,关于变量延迟的特例:
  1. @echo off
  2. set n=1&if defined n echo.定义了变量n,即变量n存在
  3. set/a n+=1,n=n+7
  4. echo.%n%
  5. pause
复制代码
对于 if defined 和 set/a ,不使用%或!括变量,无论是否开了变量延迟,变量本身都会被延迟扩展。如果换成其它写法,就需要开启变量延迟,如:
  1. @echo off&setlocal enabledelayedexpansion
  2. set n=1&if not "!n!"=="" echo.定义了变量n,即变量n存在
  3. set/a n=%n%+1&set/a n=!n!+7
  4. echo.%n%
  5. pause
复制代码
2,关于表达式分隔符,:

以上面的批处理为例,是不可以写成:
  1. @echo off&setlocal enabledelayedexpansion
  2. set n=1&if not "!n!"=="" echo.定义了变量n,即变量n存在
  3. set/a n=%n%+1,n=!n!+7
  4. echo.%n%
  5. pause
复制代码
即用,连起来的表达式,无论是否开了变量延迟,只要这个变量被用!括起来,!n!都不会动态扩展分隔符,前n的变化。
即 set/a n=%n%+1&set/a n=!n!+7 和 set/a n=%n%+1,n=!n!+7 并不等效;
set/a n=n+1,n=n+7set/a n=%n%+1,n=!n!+7 也不等效。
所以:在一个用分隔符 , 连起来的表达式中,!括变量并不能动态扩展该变量在,前的变化,去掉这个!才可以动态扩展该变量在,前的变化。

[ 本帖最后由 hanyeguxing 于 2010-4-17 15:39 编辑 ]
作者: qzwqzw    时间: 2010-4-17 17:54     标题: 回复 6楼 的帖子

变量延迟的特例我需要考虑一下
因为从我的理解
这些语法特性都可以理解
算不上“奇诡”
if defined 和 set /a因为都是在内部处理变量
而不是在预处理过程中处理
也就是说不需要使用变量扩展标记
所以天然具有变量延迟的特性

至于逗号表达式
这是set /a才能处理的特性
cmd的预处理过程
是把两个表达式理解为一句
而不是一个语句块
所以自然不可能延迟扩展变量
作者: neorobin    时间: 2010-4-17 17:57     标题: 数值比较溢出错误的问题

Winxp CMD 处理的数值范围 是 [-2^31, 2^31-1], 即使两个数值都在此范围内, 但它们进行 if 比较( 除仅仅只是相等或不相等 的比较) 时, 也可能发生溢出错误, 发生情形为两个数的差值的绝对值 大于或等于 2^31 时, 示例
  1. if 3 gtr -2147483645 (echo 3>-2147483645) else (echo 3<=-2147483645)
复制代码
显示结果为:
3<=-2147483645

更多请见: http://bbs.bathome.net/thread-7659-1-1.html
作者: hanyeguxing    时间: 2010-4-17 19:34

原帖由 qzwqzw 于 2010-4-17 17:54 发表
变量延迟的特例我需要考虑一下
因为从我的理解
这些语法特性都可以理解
算不上“奇诡”
if defined 和 set /a因为都是在内部处理变量
而不是在预处理过程中处理
也就是说不需要使用变量扩展标记
所以天然具有变量延迟的特性

至于逗号表达式
这是set /a才能处理的特性
cmd的预处理过程
是把两个表达式理解为一句
而不是一个语句块
所以自然不可能延迟扩展变量
...
这个我同意,当初自学的时候,我绕了好大一个圈子才明白过来
作者: qzwqzw    时间: 2010-4-17 23:05

因为帖子字数所限
现在主题帖暂时无法更新
楼上的两位兄弟别着急
我正在想办法
作者: namejm    时间: 2010-4-18 20:47

  我也来补充几条:
1、
  1. for %i in (*.txt) do echo %i
复制代码
  以上语句不能检测到带隐藏属性的txt文件;
2、
  1. ren 10.pdf *2.c
复制代码
  10.pdf 被改成了什么?10.pdf2.c!具体分析请看这篇文章:http://www.cn-dos.net/forum/viewthread.php?tid=29538
3、
  cmd窗口中运行 for /?,找到一段话:
eol=c           - 指一个行注释字符的结尾(就一个)

  什么意思?是说如果要找到以c字符结尾的某行文字,只需要添加 eol=c 的限制就可以了么?试试运行如下代码:
  1. @echo off
  2. for /f "eol=c" %%i in (test.txt) do echo %%i
  3. pause
复制代码
测试文本test.txt的内容为:
;abc
cab
;cab
acb

  结果如何?居然是不显示第二行内容!
  第二行内容有什么规律?它是以字符 c 打头的。My God,天杀的瘟到死,居然把"忽略以字符c打头的行"给翻译成"忽略以字符c结尾的行"!
4、
  还是上面那个test.txt,代码还是上面那一个,不过去掉了引号内的限制,改成:
  1. @echo off
  2. for /f %%i in (test.txt) do echo %%i
  3. pause
复制代码
  结果,所有以分号打头的行都没显示出来。原来for语句暗含忽略以分号打头的行的功能,因为以分号打头的行在很多语言中都是作为注释语句。
5、
  1. @echo off
  2. for /f %%i in (test.txt) do (
  3.     set str=%%i
  4.     setlocal enabledelayedexpansion
  5.     echo !str!
  6. )
  7. pause
复制代码
  当test.txt内的行数超过32行时,第32行之后,每显示一行的内容,将报错一次:“已经达到最大的 setlocal 递归层。”,当在 echo 的下一句添加 endlocal 语句时,将不会报错。
5、
  1. set /p str=<test.txt
  2. echo %str%
复制代码
  获取 test.txt 第一行的内容,并赋予变量 str

6、
  1. findstr /v "$" test.txt
复制代码
  当 test.txt 最后一行不是空行的时候,将获取最后一行的内容,因为最后一行的字符在结尾处只含回车符号而不含换行符号(貌似是这样解释的吧?),而 $ 在findstr中的准确含义是"匹配回车+换行符号"。

7、
  1. set str=abc
  2. set /a num=%str%&&(echo %str% 是数字) || echo (%str%不是数字)
复制代码
  在SP2环境下,它是可以判断某一字符串是不是纯数字的(要求数字的范围在0~2^32-1这个范围内),但是,到了SP3下之后,这条语句已经不再适用,因为如果某一字符串不是数值,将 set /a num=%str% 之后,num的值将会被赋为0值。

8、
  1. set /a num1=123,num2=345
  2. echo %num1%
  3. echo %num2%
复制代码
  同时给两个变量赋予数值类型的值。如果其中某个变量的值不是数值,在SP2下将出错,在SP3下该变量的值为0。

9、
  1. @echo off
  2. for /f "delims=" %%i in ('type test.txt') do echo "%%i"
  3. pause
复制代码
  对一个追求代码极限简洁的狂热分子来说,type命令可能显得画蛇添足,但是,当你处理的test.txt是Unicode编码的时候,你将体会到什么叫不可或缺。当然,type也可以换成more,不过more在显示长文本的时候会分屏显示。

10、
  1. findstr "我 你" test.txt
复制代码
test.txt中有一行内容:
我 你 他

  上面的代码竟然不能查到这行内容!加上 /i 或 /r 参数,奇迹就会出现。难道这是 findstr 在查找以空格分隔的多个纯中文字符串的时候存在的bug?
作者: qzwqzw    时间: 2010-4-19 14:49

攒的真多啊
手忙脚乱了半天
才整理的差不多

第5条应该是笔误吧
set /p str=test.txt<nul
好像该是
set /p str=<test.txt

第6条表述有些小问题
那种情况下
最后一行既没有回车符0x0d,\r,也没有换行符0x0a,\n
而"获取最后一行的内容"应该是”不获取最后一行的内容“
改了

第8条没看出跟第7条有什么不同

第9条的内容稍微改了一下加进去了
type支持unicode不算奇诡
cmd/u和type不兼容才算是奇诡
作者: qzwqzw    时间: 2010-4-19 14:50

印象中还记得有一个关于for /f usebackq 的奇诡语法
不知道谁还能找到?

另外现在帖子是按核心命令行的字母顺序排序
现在的条数越来越多
索引方式就有点乱了
更新起来也比较麻烦
大家有什么其它的好办法没有
最好是多种索引方式并行

[ 本帖最后由 qzwqzw 于 2010-4-19 15:31 编辑 ]
作者: namejm    时间: 2010-4-19 15:48

原帖由 qzwqzw 于 2010-4-19 14:49 发表
攒的真多啊
手忙脚乱了半天
才整理的差不多

以前碰到过很多诡异的情况,由于不善归纳整理,目前只是自己心知肚明,像楼主这样整理出来与人分享,是件功德无量的事情,值得大家学习。
第5条应该是笔误吧
set /p str=test.txt<nul
好像该是
set /p str=<test.txt

嘿嘿,一激动就写错了,已更改。
第6条表述有些小问题
那种情况下
最后一行既没有回车符0x0d,\r,也没有换行符0x0a,\n
而"获取最后一行的内容"应该是”不获取最后一行的内容“
改了

嗯,最后一行应该是同时没有回车符和换行符。不过我那条代码的执行结果和我的文字描述是吻合的,因为我使用了 /v 来排除回车换行符。
第8条没看出跟第7条有什么不同

第7条的重点是描述 set /a 在不同版本的系统上有不同的表现;而第8条描述是是多个数值赋值语句可以写成一行。
第9条的内容稍微改了一下加进去了
type支持unicode不算奇诡
cmd/u和type不兼容才算是奇诡

呵呵,我的本意是 for 语句不能直接读取Unicode编码的文本,需要借助type或more。
作者: namejm    时间: 2010-4-19 15:58

原帖由 qzwqzw 于 2010-4-19 14:50 发表
另外现在帖子是按核心命令行的字母顺序排序
现在的条数越来越多
索引方式就有点乱了
更新起来也比较麻烦
大家有什么其它的好办法没有
最好是多种索引方式并行

  你在顶楼归纳的那4种情况其实就是一个大的分类啊,在这些分类下面,再按照字母顺序排下来就是了。
作者: qzwqzw    时间: 2010-4-19 16:57     标题: 回复 14楼 的帖子

第一次使用帖子的"引用"
好麻烦

当 test.txt 最后一行不是空行的时候,将获取最后一行的内容


这样的描述很别扭
让我掂不清是最后一行
还是倒数第二行

你在顶楼归纳的那4种情况其实就是一个大的分类啊,在这些分类下面,再按照字母顺序排下来就是了。

我不是专指分类
而是指索引
也就是快速定位的方式方法
比如分楼显示加楼层锚点

正在试for
一头雾水中……
独郁闷不如众郁闷
发上来
也让大家伙费费脑筋
D:\>echo %errorlevel%
0

D:\>(for /f %f in (test) do do set)
系统找不到文件 test。

D:\>echo %errorlevel%
0

D:\>(for /f %f in (test) do do set) 2>nul

D:\>echo %errorlevel%
0

D:\>(for /f %f in (test) do do set) || echo test
系统找不到文件 test。
test

D:\>echo %errorlevel%
5

D:\>(for /f %f in (test) do do set) 2>nul || echo test
test

D:\>echo %errorlevel%
6

D:\>(for /f %f in (uni.txt) do do set) || echo test
test

D:\>echo %errorlevel%
1

D:\>(for /f %f in (uni.txt) do do set) 2>nul || echo test
test

D:\>echo %errorlevel%
1

D:\>(for /f %f in (uni.text) do do set) || echo test
系统找不到文件 uni.text。
test

D:\>echo %errorlevel%
2

D:\>

注释:D盘根存在名为Test的目录

[ 本帖最后由 qzwqzw 于 2010-4-19 17:09 编辑 ]
作者: hanyeguxing    时间: 2010-4-19 16:57

有一个问题算不上诡异,但多数新手却很容易找不到错误的原因:
  1. set a=
复制代码
这个是很多人经常用的一行代码,但却产生了一个问题。如果在这之前没有定义变量a,则这行代码后会产生%ERRORLEVEL%为1。
例如:
  1. @echo off
  2. echo.%ERRORLEVEL%
  3. set a=
  4. echo.%ERRORLEVEL%
  5. pause
复制代码
这将直接干扰后面对%ERRORLEVEL%的使用

[ 本帖最后由 hanyeguxing 于 2010-4-20 19:20 编辑 ]
作者: qzwqzw    时间: 2010-4-19 17:02

errorlevel 的细节还很多
足可以单独盖一栋楼了
这是打的基础
http://bbs.bathome.net/thread-7479-1-1.html
作者: qzwqzw    时间: 2010-4-19 17:24     标题: 回复 14楼 的帖子

呵呵,我的本意是 for 语句不能直接读取Unicode编码的文本,需要借助type或more。

得你提醒
又发现一系列”非奇诡“的奇诡
D:\>cmd /u /c echo test123>uni.txt

D:\>more uni.txt
test123

D:\>type uni.txt | more
t
e
s
t
1
2
3


D:\>cmd /u /c echo 测试>uni.txt

D:\>more uni.txt
Km諎

逐字分行又多一个思路

[ 本帖最后由 qzwqzw 于 2010-4-19 17:25 编辑 ]
作者: qzwqzw    时间: 2010-4-19 23:53

而第8条描述是是多个数值赋值语句可以写成一行。

set /a 的逗号表达式在命令行帮助和windows帮助中都有提到
大家的代码里也经常用到
而且很多高级语言也支持逗号表达式
所以说明写的简略一点也说得过去

不过你到提醒了我另外一点
做个更新备忘:
set "var=3  > 2   "
作者: qzwqzw    时间: 2010-4-20 16:33     标题: 回复 14楼 的帖子

第7条的重点是描述 set /a 在不同版本的系统上有不同的表现;

关于set/a在xp sp3下的表现我这里与你所述不同


已确认,无论在哪种环境下
set /a var=abc都不会出错
因为它将abc识别为变量名,所以计算结果为0
set /a var=1a都会出错
因为它将1a识别为操作数,所以计算出错

set/a识别变量名和操作数的标准
应改就是是否以字母打头,还有一些其它特殊字符也可以
作者: hanyeguxing    时间: 2010-4-20 16:47

你该用set/a "n=寒夜孤星"去实验,嘿嘿
在sp3下,set/a首先判断是否是数字(第一个字符是否是数字或数字符号),然后判断是否为有效数字
作者: qzwqzw    时间: 2010-4-20 17:03

我已经确认问题细节
你再看一下的我的21楼帖吧
示例:
set /a var=_abc
set /a var2=1a

效果:第一句会将变量var赋值为0,第二句会报“无效数字”的语法错,且不会对变量var2赋值
注释:第一句set/a将_abc识别为变量名,因找不到对应变量,所以赋值结果为0;
        第二句set/a将1a识别为操作数,而它又不是一个合法的数值,所以提示语法错误。
        set/a的判断变量名和操作数的标准类似高级语言:字符串以数字起始是操作数,否则为变量名。
        此外,判断一个字符串是否是合法的十进制数值的方法如下:
        set /a _var=%var% 2>nul
        if "%_var%"=="%var%" echo %var%是合法的十进制数值

[ 本帖最后由 qzwqzw 于 2010-4-20 17:06 编辑 ]
作者: 随风    时间: 2010-4-20 17:04     标题: 回复 16楼 的帖子

实在没看明白你郁闷的是什么地方
D盘根目录下存在 uni.txt  uni.text 这两个文件吗?
如果不存在,那么cmd 返回信息似乎没什么不对的地方呀?
你在最后注释 D盘根目录存在 test 目录是什么意思?for /f 本就不能遍历文件夹吧。
作者: qzwqzw    时间: 2010-4-20 17:09     标题: 回复 24楼 的帖子

测试环境D盘根有文件uni.txt和目录test,没有uni.text
请注意几种情况下不同的errorlevel
我们以为是相同的错误情况
结果出现了不同的errorlevel
作者: 随风    时间: 2010-4-20 17:23

对cmd这些底层的东西向来是敬而远之~
qzwqzw 的功力真是令人羡慕,赞一个先。

下面有几个应该算不上是奇诡,但也有些特殊之处

if defined ab cd  无法检测含空格的变量名是否定义。
相关讨论 http://bbs.bathome.net/viewthread.php?tid=2050&highlight=defined

一种比较另类的变量截取
http://bbs.bathome.net/viewthrea ... ge=1&extra=page%3D4

楼主在13说的 关于for /f usebackq 的奇诡语法 在这里
http://bbs.bathome.net/viewthrea ... hlight=%C1%E9%D2%EC

[ 本帖最后由 随风 于 2010-4-20 17:29 编辑 ]
作者: hanyeguxing    时间: 2010-4-20 17:43

原帖由 qzwqzw 于 2010-4-19 17:24 发表

得你提醒
又发现一系列”非奇诡“的奇诡

逐字分行又多一个思路

实际应用中受到很大的限制
1,对于ANSI编码的,必须使用cmd /u去转换
2,ANSI编码的文本内要求只能有字母、英文符号和数字
不过想到个简单的显示20个空行的方法,哈哈:
  1. fsutil file createnew uni.txt 20
  2. type uni.txt | more
复制代码

[ 本帖最后由 hanyeguxing 于 2010-4-20 18:02 编辑 ]
作者: qzwqzw    时间: 2010-4-20 19:24     标题: 回复 27楼 的帖子


简单问题复杂化
显示20个空行
这样不更简单吗
for /l %i in (1,1,20) do echo.


逐字分行的意义
不在于分行显示
而在于分行处理
比如下面的代码
  1. for /f %f in ('cmd /u /c echo test^|more') do @echo value=%f
复制代码
在这个意义下
我们通常处理的是单个字符串变量
那么西方字符集的限制就不是什么大问题
至于cmd /u
闲着也是闲着
干嘛不用呢?

[ 本帖最后由 qzwqzw 于 2010-4-20 19:31 编辑 ]
作者: hanyeguxing    时间: 2010-4-20 19:30

问题在于我就是通过这个才认清more逐字分行的原因啊,嘿嘿
作者: 随风    时间: 2010-4-20 20:13

又发现一个奇诡的问题
以下代码,不管是否存在a.txt都无法正确运行,把rem 换成其它的命令则可以,或去掉最外面的一对括弧也可以。
  1. @echo off
  2. (for /f %%f in (a.txt) do echo.123&rem abc)
  3. pause
复制代码

作者: yangfengoo    时间: 2010-4-20 20:19

我记得 dir *b*.txt

用*x*来匹配含X的文件名好像有BUG 有时会列出一些不含X的文件名  
  具体例子不记得了
作者: hanyeguxing    时间: 2010-4-20 20:30

原帖由 随风 于 2010-4-20 20:13 发表
又发现一个奇诡的问题
以下代码,不管是否存在a.txt都无法正确运行,把rem 换成其它的命令则可以,或去掉最外面的一对括弧也可以。@echo off
(for /f %%f in (a.txt) do echo.123&rem abc)
pause

因为)成了rem的一部分,所以for前的(就孤立了
例如:
  1. for /f %%f in (a.txt) do echo.123&rem abc&echo.这个不显示
复制代码

[ 本帖最后由 hanyeguxing 于 2010-4-20 20:33 编辑 ]
作者: 随风    时间: 2010-4-20 20:35     标题: 回复 32楼 的帖子

          是哦
作者: Batcher    时间: 2010-4-20 21:30

  1. if exist C:\a.txt
复制代码
不能直接判断C:\a.txt是文件还是文件夹,这个算吗?
作者: Batcher    时间: 2010-4-20 21:36

  1. ipconfig >a.txt
复制代码
XP系统里面行尾会多出一个CR,Vista、Win7系统里面没有这个问题,这个算不?
作者: qzwqzw    时间: 2010-4-20 21:43

34楼这个按说不算
if  exist没有义务为你判断是文件还是目录
不过可以用以下两个办法判断目录
if exist %1\. echo %1是目录
if exist %1\nul echo %1是目录
这两个可以算

35楼内容之前就已经添加了
搜索一下主题的ipconfig
作者: Batcher    时间: 2010-4-20 21:51

  1. set /p test=<test.txt
复制代码
要不要注明一下这个方法最多只能读取1024字节?
作者: neorobin    时间: 2010-4-20 21:59

双引号, 空格对 set 输出和变量识别的影响
详见 http://bbs.bathome.net/thread-7696-1-1.html
一. 半角双引号, 半角空格, 全角空格都可能导致变量名不能被 set 正常识别(其中部分原因和下面第二条有关)
二. 以下情形可以显示每个驱动器下的最后工作目录(排在空 set 命令显示的环境变量信息之前):
  1. set 后面是一个或若干个全角空格(可以紧跟)
  2. set 后面是一个双引号(不能紧跟) 或 一对半角双引号 " (一对双引号中间可以含有若干个半角或全角空格)
作者: Batcher    时间: 2010-4-20 22:01

是不是需要把findstr的最后行问题在补充的详细一些?
http://www.bathome.net/viewthrea ... amp;page=2#pid32098

要不要介绍一个这个多余的空格?
http://www.bathome.net/viewthrea ... amp;page=4#pid28166
http://www.bathome.net/viewthrea ... amp;page=2#pid35901
作者: lxzzr    时间: 2010-4-20 23:15     标题: 也来添加个

代码:
  1. @ECHO OFF
  2. FOR /F "DELIMS=" %%A IN ('MORE +7^<%~FS0') DO ECHO %%A
  3. PAUSE>NUL
  4. COPY %0 TEST.TXT>NUL
  5. MORE +7<"TEST.TXT"
  6. PAUSE>NUL
  7. EXIT
  8. ;忽略1
  9. ;忽略2
  10. 忽略 ";" 开头的行和空行
复制代码


尚不清楚是FOR命令还是MORE,还是管道符的作用....
作者: neorobin    时间: 2010-4-20 23:22

新发现半角逗号 "," 也对 set 命令有影响, 用逗号作参数也可 set, 来输出各驱动器的最后工作目录, 对 set 命令变量识别的影响也和 38楼所述相同或相似

set, 可以显示工作目录, 让人想起了 dos 下, 逗号可以加强 dir 命令

[ 本帖最后由 neorobin 于 2010-4-20 23:26 编辑 ]
作者: hanyeguxing    时间: 2010-4-20 23:25

原帖由 lxzzr 于 2010-4-20 23:15 发表
代码:

@ECHO OFF
FOR /F "DELIMS=" %%A IN ('MORE +7^<%~FS0') DO ECHO %%A
PAUSE>NUL
COPY %0 TEST.TXT>NUL
MORE +7<"TEST.TXT"
PAUSE>NUL
EXIT
;忽略1

;忽略2

忽略 ";" 开头的行和空行



尚不清楚是FOR命令还是MORE,还是管道符的作用.. ...

for的作用,例如,你在for里强制eol= 为空格,就可以看到;的内容了。
(俺错了,偷偷改过,嘿嘿)

[ 本帖最后由 hanyeguxing 于 2010-4-20 23:36 编辑 ]
作者: qzwqzw    时间: 2010-4-21 10:34     标题: 回复 39楼 的帖子

多余空格的问题已基本确认
应该是CMD预处理括号对()和管道符时插入的
请看以下示例
D:\>(echo.abc) > 1.txt

D:\>echo.abc|more>2.txt

D:\>(echo.abc)|more>3.txt

只有3.txt有空格
而1.txt,2.txt都没有
这说明CMD的预处理在做语句的词法切分时
为了方便以后的预处理识别)和|这些转义字符
而特意在这些字符前后插入一个空格
同时也会把多余的空格去除

这种增减空格的现象
在不含echo off的批处理中可以看到很多

比如以下的用法
  1. (echo test)
  2. (echo.test)
  3. echo test|more
  4. echo.test|more
  5. (echo test)> a.txt
  6. (echo.test) > a.txt
  7. echo.test|more>b.txt
  8. (echo.test)|more>c.txt
复制代码
D:\>(echo test )
test

D:\>(echo.test)
test

D:\>echo test  | more
test


D:\>echo.test | more
test


D:\>(echo test ) 1>a.txt

D:\>(echo.test) 1>a.txt

D:\>echo.test | more1>b.txt

D:\>(echo.test)  | more1>c.txt

[ 本帖最后由 qzwqzw 于 2010-4-21 10:39 编辑 ]
作者: Batcher    时间: 2010-4-27 17:54

顶楼内容里面的findstr /l不一定能够解决汉字“或”的问题,测试环境:XP SP3 Pro CS,测试结果:

C:\Test>type a.txt
我们
他们
123

C:\Test>findstr /l "我们 他们" a.txt

C:\Test>findstr /r "我们 他们" a.txt
我们
他们

C:\Test>findstr "我们.* 他们" a.txt
我们
他们
作者: qzwqzw    时间: 2010-4-28 14:39

findstr 匹配多个中文串的问题比较棘手
目前找不到问题的实质原因
所以我在顶楼也只是含糊其辞
如果有更多的测试和证据
足以让人判断其问题的源头
那么我可以考虑更新一下
作者: hanyeguxing    时间: 2010-5-2 02:08

  1. @echo off
  2. set m=1234567890
  3. echo.测试1:%m:~1,1%
  4. echo.测试2:%m:~20,1%
  5. echo.测试3:%m:~20%
  6. echo.测试4:%m:~8,-5%
  7. echo.测试5:%m:~0,1%
  8. echo.测试6:%m:~-0,1%
  9. echo.测试7:%m:~-20,1%
  10. echo.测试8:%m:~-5%
  11. echo.测试9:%m:~-20%
  12. pause
复制代码
在 XP SP3 Pro 下,显示结果:
测试1:2
测试2:
测试3:
测试4:
测试5:1
测试6:1
测试7:1
测试8:67890
测试9:1234567890
测试1、测试5、测试6、测试8,都是显示正常。
测试2、测试3,因为超出了范围,所以为空。
字符串都是从左向右截取的,所以测试4为空。
当数值为负,且超出范围时,是否也为空呢?从测试7和测试9中可以看出,显然不是。
形如%m:~-20,1%,超出范围时,依然截取并获取了最左边的字符。
形如%m:~-20%,超出范围时,总是截取并获取整个字符串。
所以:在截取字符串时,即使负的数值超出范围,依然是从最左边的字符开始截取。

[ 本帖最后由 hanyeguxing 于 2010-5-2 02:13 编辑 ]
作者: namejm    时间: 2010-5-2 03:20

原帖由 Batcher 于 2010-4-27 17:54 发表
顶楼内容里面的findstr /l不一定能够解决汉字“或”的问题,测试环境:XP SP3 Pro CS,测试结果:

C:\Test>type a.txt
我们
他们
123

C:\Test>findstr /l "我们 他们" a.txt

C:\Test>findstr /r "我们 他们" a.txt
我们
他们

C:\Test>findstr "我们.* 他们" a.txt
我们
他们

请注意,你这里使用的是 /l 而不是 /i,前者是按字符搜索的意思,后者是不区分大小写的意思。
作者: Batcher    时间: 2010-5-2 09:30     标题: 回复 47楼 的帖子

我举得例子是针对楼主的这句:
finstr在匹配多个中文串时存在问题,使用开关/l或者/r可以得到正确的输出。

楼主在这里没有提到/i啊
作者: qzwqzw    时间: 2010-5-2 17:38     标题: 回复 46楼 的帖子

早就习惯了变量的这种扩展形式
忘了这也是不符合大多数人的认知习惯的
其实关于增强的变量扩展方面
负值的使用也在挑战大多数人的认知

使用正值就是起始的偏移量+长度
使用负值却不是起始的反向偏移量+反向长度
而是起始的反向偏移量+终止的反向偏移量

我最初在理解set的这句说明了
也还纳闷了老半天
为什么呢?

这大概说明了
增强的变量扩展在对正值和负值的处理上
采用了完全不同的两种处理逻辑
所以正值超出后为空
而负值超出后为首字符地址的逻辑
也就不难理解了
作者: hanyeguxing    时间: 2010-5-6 13:36

1,dir的 /l 参数是按小写字母显示未排序的目录名和文件名。/l 不会将扩展字符转换为小写字符。
2,dir的 /s 参数是列出指定目录及所有子目录中出现的每个指定的文件名。
3,%%~nxi是扩展%%i的文件名和扩展名的。(%%~ni、%%~fi等也是这样)
当这三个复合到一起,就诡异了:
  1. @echo off
  2. for /f "delims=" %%i in ('dir /s/l/b/a "D:\rec\测试"')  do echo.%%i和%%~nxi
  3. pause
复制代码
%%i是小写的,%%~nxi却是原始的大小写
而当不使用 /s 时,%%~nx和%%i一样,都转成小写。

[ 本帖最后由 hanyeguxing 于 2010-5-6 13:57 编辑 ]
作者: qzwqzw    时间: 2010-5-6 18:16     标题: 回复 50楼 的帖子

这是可以理解的

%%i是dir的原样输出
所以受到dir/l的控制
而%%~nxi是扩展输出
是受到变量扩展逻辑控制的

dir/s/b将给出文件的完整路径
而在%%~nxi扩展时能根据路径找到文件
自然会把变量当作文件名处理
所以是文件自身的大小写状态

没有/s将只给出文件名
而如果当前路径不是dir的路径
则%%~nxi没有办法找到对应文件
自然只有按“当前路径+替代变量“的形式扩展
所以是替代变量%%i原来的大小写状态
作者: hanyeguxing    时间: 2010-5-6 20:02     标题: 回复 51楼 的帖子

  1. @echo off
  2. set "r=D:\rec\abc.txt"
  3. for %%a in (0 1) do call:han %r% %%a
  4. set "r=abc.txt"
  5. for %%a in (0 2) do call:han %r% %%a
  6. pause&exit
  7. :han
  8. if %~2==1 cd.>D:\rec\ABC.txt
  9. if %~2==2 cd.>ABC.txt
  10. echo.参数%1和%~nx1与%~f1
  11. for %%i in (%~1) do echo.变量%%i和%%~nxi与%%~fi
复制代码
写了个脚本测试,发现参数和变量的替换扩展确实如是按如此规律此显示的。
  %%i 的大小写由 (set) 直接控制,但 %%~nxi 则受 for 能否在扩展中找到文件的控制。如果扩展不到该文件,则按 %%i(set) 中的赋值大小写为准;如果能够扩展到该文件,则按实际文件的大小写为准。 %%~i 例外,原因是 %%~i 直接删除 %%i 的前后引号 ("") ,而不扩展寻找文件。

  参数的增强扩展同样如此。

[ 本帖最后由 hanyeguxing 于 2010-5-6 20:49 编辑 ]
作者: 2013haohaoma123    时间: 2013-7-26 12:22

md .\%input%是什么意思
作者: 网上邻居    时间: 2016-3-17 17:35

太多深奥的东西了,诡异的预处理,甚至微软本身的一些奇怪逻辑,批处理是个大坑。
作者: LJY4.0    时间: 2022-6-12 13:17

回复 1# qzwqzw
帮助很大
作者: Zhanggli    时间: 2023-9-1 09:16

华山论剑,高手过招够精彩




欢迎光临 批处理之家 (http://bathome.net./) Powered by Discuz! 7.2