[关闭]
@babydragon 2016-01-12T14:14:31.000000Z 字数 3019 阅读 3063

empty:expect的替代

原创 工具


之前为了能够处理命令交互,编译了静态链接的expect,结果遇到了大问题导致在没有tcl的机器上还是无法运行。找了半天,发现empty是一个不错的替代。

优势

首先,empty是一个纯C实现的,不依赖任何其他库的工具。这对于需要在不同机器上分发执行非常关键,之前expect就是因为无法确保机器上都有tcl或者其初始化脚本,导致无法使用。

其次,empty使用非常简单,可以方便的嵌入到shell脚本中,而无须像expect那样使用单独的脚本。这能够大大降低学习成本和后续维护成本。

使用

empty和一般应用安装方式类似,直接make即可。整个源码包中核心只有一个C文件,结构非常简单。下载地址在:http://sourceforge.net/project/showfiles.php?group_id=136798 这里。安装方式可以参考项目首页命令。

编译完成之后,就可以直接使用empty这个可执行程序了。它的help输出不是很友好,比较简单,大致参数有这几个:

因此,大部分场景下,先通过-f启动命令,然后通过-w命令监视输出,并且写入结果。例如代码中的示例,如果要模拟ssh登录,可以这样:

  1. ssh="ssh" # (/full/path/to/)ssh
  2. target="localhost" # target host
  3. login="luser" # username (Change it!)
  4. password="TopSecret" # password (Change it!)
  5. fifo_in="/tmp/empty.in" # input fifo
  6. fifo_out="/tmp/empty.out" # output
  7. # -----------------------------------------------------------------------------
  8. cmd="$ssh $login@$target"
  9. tmp="/tmp/empty.tmp" # tempfile to store results
  10. echo "Starting empty"
  11. empty -f -L $tmp -i $fifo_in -o $fifo_out $cmd
  12. if [ $? = 0 ]; then
  13. if [ -w $fifo_in -a -r $fifo_out ]; then
  14. echo "Sending Password"
  15. empty -w -v -i $fifo_out -o $fifo_in -t 5 assword: "$password\n"
  16. echo "Sending tests"
  17. empty -s -o $fifo_in "echo \"-- EMPTY TEST BEGIN --\"\n"
  18. empty -s -o $fifo_in "uname -a\n"
  19. empty -s -o $fifo_in "uptime\n"
  20. empty -s -o $fifo_in "who am i\n"
  21. empty -s -o $fifo_in "echo \"-- EMPTY TEST END --\"\n"
  22. echo "Sending exit"
  23. empty -s -o $fifo_in 'exit\n'
  24. echo "Check results:"
  25. sleep 1
  26. cat $tmp
  27. rm -f $tmp
  28. else
  29. echo "Error: Can't find I/O fifos!"
  30. return 1
  31. fi
  32. else
  33. echo "Error: Can't start empty in daemon mode"
  34. return 1
  35. fi
  36. echo "Done"

这么一大堆脚本,其实就三行和empty使用有关,一个是empty -f -L $tmp -i $fifo_in -o $fifo_out $cmd,该命令执行ssh命令,并且指定输入、输出和日志文件。empty -w -v -i $fifo_out -o $fifo_in -t 5 assword: "$password\n"该命令通过监听ssh命令的输出,如果遇到“assword:”字样,则向ssh进程发送密码。注意,这里的-i和-o参数和之前相反,因为需要监听ssh命令的输出,向ssh命令的输入发送字符。最后就是empty -s -o $fifo_in "echo \"-- EMPTY TEST BEGIN --\"\n"这时ssh已经认证成功了,因此就直接向远程写入通过ssh执行的命令即可。

除了这种预先知道命令每一步的输出之外,还有一些场景是命令可能在有些场景下会进行交互,有些场景下不会。我们遇到的场景就是webx提供的autoconfig命令。该命令默认会在缺少配置项的时候进行交互,提示用户是否使用默认值,并且写入到antx.properties文件。

这时,就不能简单的通过每一步等待特定的字符,然后输出了。这里我们利用了empty的另一个特性,即自动维护管道文件,命令开始执行时,empty会创建对应的标准输入和标准输出两个管道文件,命令结束时删除,这样就不用用户来维护命令的输入输出,也不会产生垃圾文件。因此,我们可以通过循环的方式,监听是否有我们需要的关键字,在每次监听前,增加判断管道文件是否存在,以防止命令已经正常退出。大致的脚本如下:

  1. empty -f -i input -o output -L $HOME/autoconfig.log autoconfig -u $HOME/antx.properties $f
  2. sleep 1
  3. while true;
  4. do
  5. if [ -w input -a -r output ]; then
  6. empty -w -i output -o input -t 5 ".*\Yes.*" "yes\n" ".*\[Quit.*" "quit\n"
  7. else
  8. break;
  9. fi
  10. done

while循环里面,首先需要判断的是input和output两个文件是否存在。对于input文件,通过-w检查文件是否存在的同时,检查文件是否可写;对于output文件,通过-r检查文件是否存在的同时,检查文件是否可读。如果两个文件都存在,那么运行empty命令,等待输出并且设置超时时间。

这样的实现有两个好处,首先是可以兼容命令正常运行的流程,即无须进行交互。其次是不需要知道命令当前的状态,我们在一个empty命令里面已经兼容了命令所有可能的输出对应的用户输入值。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注