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

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

如何获得expect中spawn进程的返回值  

2012-10-21 23:04:15|  分类: 脚本语言 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、返回值校验
返回值校验始终是一个安全可靠软件或者说软件或者说作业的基本流程,无论执行什么工作,都应该告诉使用者这个子进程执行的结果如何,就是通常所说的,你倒是吱一声啊。对于expect来说,它的情况是比较特殊的,它执行ssh远程进程的时候,我们在expect中读取的只能是ssh的返回值,而不是通过ssh执行的命令的返回值。
如果要获得ssh执行的远端进程的返回值,这个是用通用的方法就可以了。例如我们执行了一个scp命令,执行之后可以在命令提示符的正则表达式中做特殊处理,方法就是添加一些变量表示是第几次出现命令提示符命令,之后更具次数来执行shell的内置变量 $? 获得刚刚执行的子进程的返回值,再通过senduser或者其它的脚本变量将这个返回值保存起来,在进程退出的时候将这个值返回给执行者。
现在考虑一下对于一个本地进程的返回值如何获得。执行本地命令并不是一个稀缺的现象,例如我们在本地执行rsync命令,此时我们希望得到的其实就是这个rsync的返回值。
二、网络上的解决方法
网络上常见的说法就是通过
catch wait result
这里的catch并不是expect的命令,而是expec使用的tcl框架中的一个命令,所以在expect的代码中找不到该命令的实现。说到这里,我们可以看到tcl的设计思想对于命令的影响。通常的脚本例如bash,设置一个变量是通过 var=val的方式来定义一个变量的值,而tcl中则是通过 set var val来实现,这和tcl中提出的所有内容都是命令一致的。bash中对于变量赋值的语法其实是通过专门的解析方法来实现的,它会在每个命令中搜索是否存在有等号的存在。假设如果是通过set,这就是一个普通的命令,它有两个参数,这样词法分析也简单一些。简单的原因不是在于不能实现复杂的功能,而是因为简单的东西具有更强的扩展性和更好的可维护性,这些对于软件的维护来说都是非常有价值的属性。
wait是expect的内置命令,所以可以看一下这个命令对应的函数实现:
  argv++;
    argc--;
    for (;argc>0;argc--,argv++) {
    if (streq(*argv,"-i")) {通过-i可以指定等待哪个子进程的返回,因为expect可以同时spawn多个子进程的,所以这个-i是有意义的
        argc--; argv++;
        if (argc==0) {
        exp_error(interp,"usage: -i spawn_id");
        return(TCL_ERROR);
        }
        chanName = *argv;
    } else if (streq(*argv,"-nowait")) {
        nowait = TRUE;
    }
    }

    if (!chanName) {
    if (!(esPtr = expStateCurrent(interp,0,0,1))) return TCL_ERROR;
    } else {
    if (!(esPtr = expStateFromChannelName(interp,chanName,0,0,1,"wait")))
        return TCL_ERROR;
    }
……
 if (result == -1) {
    sprintf(interp->result,"%d %s -1 %d POSIX %s %s",
        esPtr->pid,spawn_id,errno,Tcl_ErrnoId(),Tcl_ErrnoMsg(errno));
    result = TCL_OK;
    } else if (result == NO_CHILD) {
    exp_error(interp,"no children");
    return TCL_ERROR;
    } else {
    sprintf(interp->result,"%d %s 0 %d",
        esPtr->pid,spawn_id,WEXITSTATUS(esPtr->wait))
;
也就是说,wait命令的返回值是一个"%d %s 0 %d"格式的字符串,所以我们可以通过
set result wait
来获得子进程的返回值,如果通过wait没有指定等待的接口,默认是当前spawn的进程编号。而结果的第四个字段就是通过waitpid获得子进程的返回值。
三、如果没有执行wait
这里可以看到,如果没有执行wait,expect是始终不会执行waitpid,这样多次执行spawn之后派生的所有子进程在expect退出之前都是处于Zombie状态。
四、一个例子
1、执行简单的expect来获得本地派生子进程的返回值
tsecer@harry: expect -v
expect version 5.44.1.15
tsecer@harry: cat spawn.ret.exp 
spawn echo we have echoed
expect eof
spawn ls not.exist.dir
set result [ wait result]
puts $result
sleep 12345

tsecer@harry: expect -f ./spawn.ret.exp
spawn echo we have echoed
we have echoed
spawn ls not.exist.dir
375 exp5 0 2
^Z
[1]+  Stopped                 expect -f ./spawn.ret.exp
tsecer@harry: ls not.exist.dir
ls: cannot access not.exist.dir: No such file or directory
tsecer@harry: echo $?
2
tsecer@harry: 
可以看到,这个wait获得的result中包含了4个字段,并且最后一个字段2的值就是ls一个不存在的目录的返回值2。
2、不执行wait操作
可以看到spawn已完成ls处于僵尸状态
tsecer@harry: cat spawn.nowait.exp 
spawn echo we have echoed
expect eof
spawn ls not.exist.dir
sleep 12345

tsecer@harry: expect ./spawn.nowait.exp 
spawn echo we have echoed
we have echoed
spawn ls not.exist.dir
^Z
[2]+  Stopped                 expect ./spawn.nowait.exp
tsecer@harry: ps aux | grep ls
root       405  0.0  0.0      0     0 ?        Zs   07:30   0:00 [ls] <defunct>
root       409  0.0  0.0   4352   712 pts/9    S+   07:30   0:00 grep ls
五、如何获得通过expect 派生的ssh执行的远端命令返回值
1、ssh命令的返回值
ssh 的man手册中对于ssh返回值的说明
     ssh exits with the exit status of the remote command or with 255 if an  error occurred.
也就是说,ssh返回值就是它执行的远端命令的返回值,而如果ssh本身出现问题,则返回值为255
2、简单例子
其中的YourPassWord要替换成目标用户的密码
tsecer@harry: cat remote.exp 
log_user 0
spawn ssh tsecer@127.0.0.1 "ls not.exist.dir"
expect "password:"
send "YourPassWord\r"

set result [ wait result]
puts $result
sleep 12345

tsecer@harry: expect remote.exp 
802 exp4 0 2
  评论这张
 
阅读(4287)| 评论(0)
推荐 转载

历史上的今天

评论

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

页脚

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