注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

bat中echo数字到文件及bash对应实现  

2012-02-12 12:14:23|  分类: 脚本语言 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、echo数字到文件中
这个看起来很傻很天真的问题,但是windows的cmd总是可以给你意料之外的惊喜。如果没有使用重定向,那么它可以正常的打印,但是这个需要echo的内容最后刚好是一个数字,那么问题就不那么明显了,加上这个蹩脚的工具没有公开源代码(一方面是版权,另一方面可能代码太垃圾不好意思让别人看),所以查起来不太方便。雪上加霜的是这只是一个使用了大量变量、循环、展开替换脚本系统的一部分,所以分析起来更是诡异。
问题是这样的,现在需要把一个变量的值以命令的形式保存在bat中供之后读取(至于为什么不用环境变量本身来传递是因为这是一个子进程向父进程传递的信息,并且脚本调用之间有若干个setlocal指令),例如
echo set somevar=%num%>remember.bat
这个指令看起来非常合法,但是如果num展开的值为1,那么结果是怎么样的呢?
set num=1
echo set somevar=%num%>remember.bat
这个脚本执行生成的脚本文件remember.bat的内容为
set /a somevar=
是不是很奇怪最后的那个1丢失了,这样之后调用remember.bat来获取变量的时候变量会被清空。
没错,cmd是吧那个%num%展开后等于1,而这个1后面跟上重定向符号就是把标准输出重定向的意思,所以这个1的民意就这么被强奸了。
二、解决尝试
1、数字之后加空格
这个是最为直接的想法,这个空格可以避免cmd把这个数字作为文件描述符,如果试一下,的确是可以的结果是酱紫的:
set num=1
echo set somevar=%num% >remember.bat
call remember.bat
echo hello%somevar%world
输出的结果是
hello1 world
可以看到,1后面的空格会被保留,这对于某些字符的格式化处理是不满足要求的。
由于这里可以确定是只对数字进行处理,所以可以给set一点提示,就是通过set的 /a 选项说明这是一个数值表达式,从而cmd可以忽略空格
set num=1
echo set /a somevar=%num% >remember.bat
call remember.bat
echo hello%somevar%world
执行结果
hello1world
可以看到中间空格消失。
2、使用转义字符
set num=1
echo set somevar=^^%num%>remember.bat
call remember.bat
echo hello%somevar%world
执行结果同样正确,也就是取消数字的文件描述符重定向功能。
3、使用管道
set num=1
echo set somevar=%num%|more>remember.bat
call remember.bat
echo hello%somevar%world
这里中间加了一个管道,从而隔离开描述符和重定向符号。
4、使用子命令
set num=1
(echo set somevar=%num%)>remember.bat
call remember.bat
echo hello%somevar%world
这里同样是利用了子命令功能的划分定界功能。
三、看看bash的表现
[tsecer@Harry redirect]$ cat redirect.sh
#!/bin/sh
echo somevar=2>remember.sh
[tsecer@Harry redirect]$ . redirect.sh
[tsecer@Harry redirect]$ cat remember.sh
somevar=2
结果是正确的,如我们所预料的。
我们来从词法分析的角度看一下这个问题,bash-4.1\parse.y中对于重定向的解析为
redirection:    '>' WORD
            {
              source.dest = 1;
              redir.filename = $2;
              $$ = make_redirection (source, r_output_direction, redir, 0);
            }
……
    |    NUMBER '>' WORD
            {
              source.dest = $1;
              redir.filename = $3;
              $$ = make_redirection (source, r_output_direction, redir, 0);
            }
这里也就是一个数字后跟重定向是可以满足一个文件重定向的问题,这里没有满足的原因在于‘=’并不是bash的一个内置关键字,它不能作为单词划分的依据,或者更为直接的说,在词法分析阶段,bash是不把‘=’作为关键字来区分基本单词的,所以这个
echo somevar=2>remember.sh
会被分为
echo 
somevar=2
>
remember.sh 

somevar=2
作为词法单位它只是一个普通的WORD(而不是NUMBER),所以不会构成bash重定向语法。
四、为什么会想到这么低级的问题
可能cmd是很多人不屑于用,甚至从来没有人把它作为一个真正的shell。在make中,即使是在windows下,make也会优先从环境变量中搜索sh.exe文件
make-3.81\job.c
construct_command_argv_internal (char *line, char **restp, char *shell,
                                 char *ifs, char **batch_filename_ptr)
{
#ifdef __MSDOS__
  /* MSDOS supports both the stock DOS shell and ports of Unixy shells.
     We call `system' for anything that requires ``slow'' processing,
     because DOS shells are too dumb.  When $SHELL points to a real
     (unix-style) shell
, `system' just calls it to do everything.  When
     $SHELL points to a DOS shell, `system' does most of the work
     internally, calling the shell only for its internal commands.
     However, it looks on the $PATH first, so you can e.g. have an
     external command named `mkdir'.
无论如何,cmd作为windows的本土脚本,它还是有它的优势的,那就是windows下看起来非常亲切,可以双击执行(例如make或者shell脚本都需要在命令行里执行,这里会有一个打开命令行以及修改当前工作目录的问题),这照顾到了大部分windows用户大部分时间在操作鼠标的场景。从生活的角度看,强龙不压地头蛇,也应该使用cmd。
那么为什么会遇到这个看似简单的问题呢?如果说这个是一个大型的自动化构建工程,它只是里面一个小的技术插曲,那它可能看起来就没那么幼稚了。
其中还遇到过一个问题,就是如果对于数字开头定义的变量取值,那一般不会如你所预料的样子,例如
set 7-zipInstall=SomePlace
echo %7-zipInstall%
不会打印出SomePlace,因为%7是一个对命令行第七个参数取值的语句。
  评论这张
 
阅读(996)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017