zoukankan      html  css  js  c++  java
  • 解决DOS批处理中一个困扰我几十个月的编码问题

      DOS批处理中的编码很有意思。&是命令连接符,先执行&左边的命令,再执行&右边的命令。|是管道操作,把左边的输出当作右边的输入。此外还有&&和||,当要表示这些特殊的字符本身的时候,得在左边加^号,如用^&表示&本身,而不是命令连接符。^^表示^。

      我有一个批处理myfor.bat, 可以重复N次执行命令, 命令从参数传入。它很简单:

    @Echo Off
    set /a i=%1
    :start
    %~2
    set /a i=i-1
    if %i% GTR 0 goto start

    参数1是执行的次数,参数2是要执行的命令。

      这次的实际任务就是重复地执行ping,要ping n个IP,我不想开n个窗口。所以命令是这样的: myfor 999 "ping 10.0.0.1&ping 10.0.0.2&ping 10.0.0.3" 。是的没错,双引号的优先级大于&,所以双引号中的&不需要加^,我也是实测发现的。每个ping的结果是这样的:

    正在 Ping 10.0.0.1 具有 32 字节的数据:
    来自 10.0.0.1 的回复: 字节=32 时间<1ms TTL=64
    来自 10.0.0.1 的回复: 字节=32 时间<1ms TTL=64
    来自 10.0.0.1 的回复: 字节=32 时间<1ms TTL=64
    来自 10.0.0.1 的回复: 字节=32 时间<1ms TTL=64
    
    10.0.0.1 的 Ping 统计信息:
        数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
    往返行程的估计时间(以毫秒为单位):
        最短 = 0ms,最长 = 0ms,平均 = 0ms

      为了只看延时,过滤掉统计信息和空行,我加了个Find,只显示带有“来自”的行: myfor 999 "ping 10.0.0.1|find "来自"&ping 10.0.0.2|find "来自"&ping 10.0.0.3|find "来自"" 。

      然后,因为这条命令最近经常要执行,而且IP还不是固定的,于是我想能不能做成一个批处理plist.bat。用的时候就这样用: plist 10.0.0.1 10.0.0.2 10.0.0.3 。思路很简单,就是通过shift循环取到每个参数,并拼成类似这样的字符串: "ping 10.0.0.1|find "来自"&ping 10.0.0.2|find "来自"&ping 10.0.0.3|find "来自"" ,然后传给myfor去执行。

    @Echo Off
    cls
    set cmd=ping %1 ^| find "来自"
    shift
    :forIps
    if "%1"=="" goto :exitIps
    set cmd=%cmd% ^& ping %1 ^| find "来自"
    shift
    goto :forIps
    :exitIps
    echo myfor 999 "%cmd%"
    myfor 999 "%cmd%"

      我执行了: plist.bat 10.0.0.1 10.0.0.2 10.0.0.3 ,出现:

    找不到文件 - &
    找不到文件 - PING
    找不到文件 - 10.0.0.2
    找不到文件 - |
    找不到文件 - FIND
    找不到文件 - 来自
    找不到文件 - &
    找不到文件 - PING
    找不到文件 - 10.0.0.3
    找不到文件 - |
    找不到文件 - FIND
    找不到文件 - 来自
    myfor 999 "ping 10.0.0.3 | find "来自""
    来自 10.0.0.3 的回复: 字节=32 时间<1ms TTL=64
    来自 10.0.0.3 的回复: 字节=32 时间<1ms TTL=64

      怎么回事? 分析了一下, 关键在set cmd这条命令:  set cmd=ping %1 ^| find "来自" 实际执行了 set cmd=ping 10.0.0.1 ^| find "来自" 这个没问题。然后执行了 set cmd=%cmd% ^& ping %1 ^| find "来自" 实际执行了 set cmd=ping 10.0.0.1 | find "来自" ^& ping 10.0.0.2 ^| find "来自" 正是^|已经被解析成了|, 从而命令的意思是把 set cmd=ping 10.0.0.1 的输出当作find 的输入,而find右边的全部内容都被当作find的参数,于是出现“找不到文件 - &” 等错误。分析了这么多,根源在于最左边的|被解析成管道操作,而不是当作普通的字符串,要知道这里是要拼接字符串,此时得当作普通字符,等到要执行的时候,才当作管道。

    那好,我就在拼接前把|变成^|, 改成这样子:

    @Echo Off
    cls
    set cmd=ping %1 ^| find "来自"
    shift
    :forIps
    if "%1"=="" goto :exitIps
    set cmd=%cmd:|=^^^|%
    set cmd=%cmd% ^& ping %1 ^| find "来自"
    shift
    goto :forIps
    :exitIps
    echo myfor 999 "%cmd%"
    myfor 999 "%cmd%"

    执行: plist.bat 10.0.0.1 10.0.0.2 10.0.0.3 出现:

    错误的参数 ^|。
    myfor 999 "ping 10.0.0.1 | find "来自"  & ping 10.0.0.3 | find "来自""

      分析原因, 关键还是在这一行  set cmd=%cmd:|=^^^|% 。为了不让|生效,我们需要将|替换成^|,为了不让&生效,我们需要将&替换成^&,但我们替换|的时候&生效了,我们替换&的时候|生效了。先替换哪个都有问题。思路卡住了,这一卡就卡了几十个月。

      真的没有出路了吗?我们是希望每次拼接完是可以执行的语句,即&和|左边刚好没有^,所以才会卡住。但如果我们退一步,每次拼接完&和|左边都只有1个^,然后在执行前再把这个^去掉,这条路是否行得通?去掉^非常简单,就是这么一句  set cmd=%cmd% 。由于每次拼接完&和|左边要有一个^,所以原来的 set cmd=ping %1 ^| find "来自" 得变成 set cmd=ping %p1% ^^^| find "来自" ,其它情况类似。由于&和|左边都有^,所以每次我只需要控制^的数量,就可以防止^&和^|被解码。经过一番修改、调试、再修改。最终可行的版本浮现了:

    @Echo Off
    cls
    set cmd=ping %1 ^^^| find "来自"
    shift
    :forIps
    if "%1"=="" goto :exitIps
    set cmd=%cmd:^=^^^^^^^%
    set cmd=%cmd% ^^^& ping %1 ^^^| find "来自"
    shift
    goto :forIps
    :exitIps
    set cmd=%cmd%
    echo myfor 999 "%cmd%"
    myfor 999 "%cmd%"

    执行: plist.bat 10.0.0.1 10.0.0.2 10.0.0.3 出现:

    myfor 999 "ping 10.0.0.1 | find "来自" & ping 10.0.0.2 | find "来自" & ping 10.0.0.3 | find "来自""
    来自 10.0.0.1 的回复: 字节=32 时间<1ms TTL=128
    来自 10.0.0.1 的回复: 字节=32 时间<1ms TTL=128
    来自 10.0.0.1 的回复: 字节=32 时间<1ms TTL=128
    来自 10.0.0.1 的回复: 字节=32 时间<1ms TTL=128
    来自 10.0.0.2 的回复: 字节=32 时间<1ms TTL=128
    来自 10.0.0.2 的回复: 字节=32 时间<1ms TTL=128
    来自 10.0.0.2 的回复: 字节=32 时间<1ms TTL=128
    ……

    大功告成。

      到了山穷水尽疑无路的境地, 退一步却迎来了柳暗花明又一村。这个解决方案不仅用于这个交替ping,在我的其它工具也会用到,它对我而言,意义重大。读者朋友你们的网络中不一定有10.0.0.*这样的IP,因为使用了find过滤,所以ping不存在的ip是不会有显示的。所以测试时为了尽最大可能的成功,可以替换成 127.0.0.1 127.0.0.2 127.0.0.3。

      对于Dos shell,虽然不像linux shell那么强大,但是适用就是好猫。而对于只要%cmd%就会自动解码这种方式,有时会带来棘手的编码问题,就如本文的这个问题,是我遇到的最难处理的dos shell编码问题了。既然已找到我自己满意的解法,以后就可以大胆地移值到其它类似的棘手的编码问题上。

  • 相关阅读:
    java中不常见的keyword:strictfp,transient
    D3DXMatrixMultiply 函数
    expect
    char* 和char[]的差别
    下载安装tomcat6.0
    Eclipse或SVN—怎样在Eclipse中安装SVNclient插件
    sharepoint 訪问缩略图
    斜率优化专题1——bzoj 1597 [Usaco2008 Mar] 土地购买 题解
    phpmywind教程:关于日期函数调用整理
    linux服务之smtp
  • 原文地址:https://www.cnblogs.com/BillySir/p/8025791.html
Copyright © 2011-2022 走看看