Board logo

标题: [原创] 批处理技术内幕:IF命令 [打印本页]

作者: Demon    时间: 2012-8-13 15:49     标题: 批处理技术内幕:IF命令

标题: 批处理技术内幕:IF命令
作者: Demon
链接: http://demon.tw/reverse/cmd-internal-if.html
版权: 本博客的所有文章,都遵守“署名-非商业性使用-相同方式共享 2.5 中国大陆”协议条款。

比起ECHO的简单,IF命令似乎要复杂得多,至少帮助文档的文字要多得多。


    IF [NOT] ERRORLEVEL number command
    IF [NOT] string1==string2 command
    IF [NOT] EXIST filename command

如果CMD拓展开启(ENABLEEXTENSIONS)的话,IF还支持下面的用法:

    IF [/I] string1 compare-op string2 command
    IF CMDEXTVERSION number command
    IF DEFINED variable command

compare-op可以是下列之一:

    EQU - equal
    NEQ - not equal
    LSS - less than
    LEQ - less than or equal
    GTR - greater than
    GEQ - greater than or equal

你可能要说,这些地球人都知道的东西你复制粘贴上来做什么?跟批处理技术内幕有半毛钱关系?

其实我也不想,可是我实在不知道怎么写开头。我们从最简单也是最常用的if string1==string2讲起:

    @echo off
    set /p str=What's the domain of Demon's Blog:
    if %str%==demon.tw start http://demon.tw
    pause

当然,为了防止用户没有输入或者输入中含有特殊字符而导致的脚本解析错误,最佳实践是在两边都加上双引号,同时也可以加上空格以提高可读性:

    @echo off
    set /p str=What's the domain of Demon's Blog:
    if "%str%" == "demon.tw" start http://demon.tw
    pause

CMD是如何比较两个字符串是否相等的呢?其实它只做了一件事:调用lstrcmp函数:



如果lstrcmp函数认为两个字符串相等,则if条件成立。

lstrcmp应该是Locale String Compare的缩写,根据MSDN文档,它会以当前线程的Locale调用CompareString函数,将返回的结果减去2以符合C语言字符串比较函数的返回值约定(1表示大于,0表示等于,-1表示小于)。CompareString函数进行的是语言排序(Sort Linguistically)而不是二进制排序,如果两个字符串的二进制不同但是代表的语言文字相同,那么lstrcmp函数仍然会认为它们相等,这和C语言的字符串比较函数是不同的:

    #include <stdio.h>
    #include <string.h>
    #include <Windows.h>

    int main()
    {
        wchar_t s1[] = {0x00E9, 0x0000};
        wchar_t s2[] = {0x0065, 0x0301, 0x0000};

        printf("%d\n", wcscmp(s1, s2));
        printf("%d\n", lstrcmpW(s1, s2));
        return 0;
    }

U+00E9和U+0065 U+0301的组合都可以表示é这个字符,但它们的二进制表示却不同,C语言函数wcscmp认为它们并不相等,而lstrcmp却认为它们是相等的。

除了语言排序(Sort Linguistically)之外,CompareString函数默认使用的是单词排序(word sort)而不是字符串排序(string sort)。在单词排序中,除了连字号(hyphen)-和撇号(apostrophe)’之外的所有标点符号(punctuation marks)和其他非字母数字字符(nonalphanumeric characters)都排在字母数字字符(alphanumeric character)的前面。区别对待连字号-和撇号’以保证coop和co-op、cant和can’t之类的单词排列在一起。

    @echo off
    if 0xff gtr 0200 echo 255 ^> 128
    pause

equ neq lss leq gtr geq的实现和==差不多,只不过比==更智能一点,在进行字符串比较之前,CMD会首先会调用wcstol函数,尝试将字符串转成整数:



如果两边的字符串都可以完全被转成整数,那么会用左边的整数减去右边的整数,根据差与0的大小来决定哪个整数比较大。



然而减法可能会导致溢出:

    @echo off
    if 2147483647 lss -2147483648 echo 2147483647 ^< -2147483648
    pause

2147483647 – (-2147483648) = 4294967295,即十六进制的0xFFFFFFFF,由于是有符号整数,它代表-1,而-1<0,所以CMD认为2147483647 < -2147483648。

如果两边的字符串其中有一个无法转成整数,那么和==一样,也是调用lstrcmp来比较字符串的大小。

如果if加上了/i开关,处理方式也是大同小异的,只不过lstrcmp变成了lstricmp而已。

if的其他几种用法都比较简单,没什么内幕好讲的,就不多说了。

参考链接:http://msdn.microsoft.com/en-us/ ... 4%28v=vs.85%29.aspx

拓展阅读:

    http://bbs.bathome.net/viewthread.php?tid=7513
    http://bbs.bathome.net/viewthread.php?tid=6853
    http://bbs.bathome.net/viewthread.php?tid=14681
    http://bathome.net/viewthread.php?tid=12329
作者: plp626    时间: 2012-8-20 10:47

if 比较时带引号和不带引号,其内部是如何处理的?
作者: Demon    时间: 2012-8-20 11:16

if 比较时带引号和不带引号,其内部是如何处理的?
plp626 发表于 2012-8-20 10:47

一样的,都是lstrcmpW
  1. @if "a" gtr "A" (echo a ^> A) else echo a ^< A
  2. 注意看右下角的参数[code]
  3. 001AF6D8   001BB960  String1 = "\"a\""
  4. 001AF6DC   001BB988  String2 = "\"A\""
复制代码
也就是lstrcmpW(L"\"a\"", L"\"A\"")
作者: plp626    时间: 2012-8-22 12:55

本帖最后由 plp626 于 2012-8-22 13:07 编辑

回复 3# Demon
  1. @echo off&setlocal disableDelayedExpansion
  2. chcp 437
  3. if  gtr ! ECHO \x07 ^> !
  4. if "" gtr ! ECHO "\x07" ^> "!"
  5. pause
  6. chcp 936
  7. if  gtr ! ECHO \x07 ^> !
  8. if "" gtr ! ECHO "\x07" ^> "!"
  9. pause
复制代码

论坛过滤的特殊字符越来越多了
xp测试:
0x01~0x08, 0x7f 等字符 和 ! 的顺序都和引号有关。。
作者: Demon    时间: 2012-8-24 20:41

回复  Demon 
论坛过滤的特殊字符越来越多了
xp测试:
0x01~0x08, 0x7f 等字符 和 ! 的顺序都和引号有 ...
plp626 发表于 2012-8-22 12:55


去问lstrcmp函数吧,我也不知道为什么。
  1. #include <stdio.h>
  2. #include <windows.h>
  3. int main()
  4. {
  5.     wchar_t s1[] = L"\"\x07\"";
  6.     wchar_t s2[] = L"\"!\"";
  7.     wchar_t s3[] = L"\x07";
  8.     wchar_t s4[] = L"!";
  9.     printf("%d\n", lstrcmpW(s1, s2));
  10.     printf("%d\n", lstrcmpW(s3, s4));
  11.     return 0;
  12. }
复制代码

作者: han7752660    时间: 2013-1-22 17:08

如何在if 条件中添加or 或者and ???
是否存在if .. elsif ..elsif ..else ...??谢谢




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