linux expect的使用
来源:https://www.jianshu.com/p/b987f5e92c03
参考:https://blog.csdn.net/houmou/article/details/53102051
参考: https://zhuanlan.zhihu.com/p/416370937
参考:https://www.yiibai.com/tcl
1. expect介绍
使用Linux的程序员对输入密码都不会陌生,比如使用超级用户执行命令,又比如scp
、ssh
连接远程主机等等。如果我们脚本里面有scp
的操作,总不可能执行一次scp
就输入密码一次,这样就需要一个人盯着脚本运行了。 为了解决这个问题,我们需要一个自动输入密码的功能。
expect
是建立在tcl(Tcl/Tk快速入门 )基础上的一个工具,它可以让一些需要交互的任务自动化地完成。相当于模拟了用户和命令行的交互操作。
一个具体的场景:远程登陆服务器,并执行命令
登录时输入密码需要交互,bash脚本无法完成,可以使用expect来完成。
2. 安装
brew install expect
3. 原理
首先使用 spawn
开启一个会话,然后使用 expect-send
对来执行交互式操作。
spawn
后面跟上一个命令操作,表示开启一个会话。expect
等待输出特定的字符串(通常是提示符),然后使用send
发送交互字符串。比如:
spawn ssh username@host # 远程登录
expect "*assword" # 提示为:"username@host's password:", 等待用户输入密码
send "${password}\r" # 这时使用send模拟用户输入密码的字符串,完成登录验证
4. 基本语法
脚本解释器
脚本中首先引入文件,表明使用的是哪一个shell
#!/usr/bin/expect
set
设置会话超时时间为30s, 若不限制超时时间则应设置为-1
set timeout 30
set 还可以设置变量
# 使用变量语句:$param 或者 ${param}({}用来避免param和后面的字符串拼接起来导致错误)
set param "param_str"
set param 1
spawn
spawn 后面跟一个命令,开启一个会话
spawn ${cmd} # for example : spawn su root
expect - send
expect
接收命令执行后的输出,然后和期望字符串匹配,若对应这执行相应的send
来发送交互信息。
expect "$case1" {send "$respond1\r"} # 这一行等同于下面两行
expect "$case1"
send "$response1\r"
expect 可以有多个分支,就像switch语句一样。
expect
{
"$case1" {send "$response1\r"}
"$case2" {send "$response2\r"}
"$case3" {send "$response3\r"}
}
结束符
expect eof
:等待执行结束,若没有这一句,可能导致命令还没执行,脚本就结束了
interact
: 执行完成后保持交互状态, 这时可以手动输入信息
注:expect eof
与 interact
二选一即可
接收参数
参数存在argv中,使用第一个参数如下:
set param0 [lindex $argv 0]
$argc
表示参数个数,判断语句如下:
if {$argc < 1} {
#do something
send_user "usage: $argv0 <param1> <param2> ... "
exit
}
注:$argv0
是脚本名,但[lindex $argv 0]
是第一个参数 param1, [lindex $argv 1]
是第二个参数 param2, 以此类推 send_user 用来显示信息到父进程(一般为用户的shell)的标准输出。
5. 实例
实现远程登录服务器,并切换到root用户下执行关闭防火墙的命令,然后退出
#!/usr/bin/expect
if {$argc < 4} {
#do something
send_user "usage: $argv0 <remote_user> <remote_host> <remote_pwd> <remote_root_pwd>"
exit
}
set timeout -1
set remote_user [lindex $argv 0] # 远程服务器用户名
set remote_host [lindex $argv 1] # 远程服务器域名
set remote_pwd [lindex $argv 2] # 远程服务器密码
set remote_root_pwd [lindex $argv 3] # 远程服务器根用户密码
# 远程登录
spawn ssh ${remote_user}@${remote_host}
expect "*assword:" {send "${remote_pwd}\r"}
expect "Last login:"
# 切换到 root
send "su\r"
expect "*assword:" {send "${remote_root_pwd}\r"}
# 执行关闭防火墙命令
send "service iptables stop\r"
send "exit\r"
send "exit\r"
expect eof
将代码保存到 remot_root_command.exp 中,权限改为755,然后执行下面这条命令即可:
./remote_root_command.exp <remote_user> <remote_host> <remote_pwd> <remote_root_pwd>
Linux expect使用方法
来源 https://www.lxlinux.net/6637.htmlexpect是一个自动化交互套件,主要应用于执行命令和程序时,系统以交互形式要求输入指定字符串,实现交互通信,本篇文章为大家详细讲解一下Linux expect使用方法。
expect参数
启用选项
-c
:执行脚本前先执行的命令,可多次使用。-d
:debug模式,可以在运行时输出一些诊断信息,与在脚本开始处使用exp_internal 1
相似。-D
:启用交换调式器,可设一整数参数。-f
:从文件读取命令,仅用于使用#!时。如果文件名为”-“,则从stdin读取(使用”./-“从文件名为-的文件读取)。-i
:交互式输入命令,使用”exit”或”EOF”退出输入状态。--
:标示选项结束(如果你需要传递与expect选项相似的参数给脚本时),可放到#!
行:#!/usr/bin/expect --
。-v
:显示expect版本信息。
expect的4个命令
Expect中最关键的四个命令是send,expect,spawn,interact。
命令 | 说明 |
---|---|
send | 用于向进程发送字符串 |
expect | 从进程接收字符串 |
spawn | 启动新的进程 |
interact | 允许用户交互 |
常用命令
`# 命令行参数`
`# $argv,参数数组,使用[lindex $argv n]获取,$argv 0为脚本名字`
`# $argc,参数个数`
`set` `username [lindex $argv 1]` `# 获取第1个参数`
`set` `passwd` `[lindex $argv 2]` `# 获取第2个参数`
`set` `timeout 30` `# 设置超时`
`# spawn是expect内部命令,开启ssh连接`
`spawn` `ssh` `-l username 192.168.1.1`
`# 判断上次输出结果里是否包含“password:”的字符串,如果有则立即返回,否则就等待一段时间(timeout)后返回`
`expect` `"password:"`
`# 发送内容ispass(密码、命令等)`
`send` `"ispass\r"`
`# 发送内容给用户`
`send_user` `"$argv0 [lrange $argv 0 2]\n"`
`send_user` `"It's OK\r"`
`# 执行完成后保持交互状态,控制权交给控制台(手工操作)。否则会完成后会退出。`
`interact`
命令介绍
-
close:关闭当前进程的连接。
-
debug:控制调试器。
-
disconnect:断开进程连接(进程仍在后台运行)。
-
- 定时读取密码、执行priv_prog
`send_user` `"password?\ "`
`expect_user -re` `"(.*)\n"`
`for` `{} 1 {} {`
`if` `{[fork]!=0} {``sleep` `3600;``continue``}`
`disconnect`
`spawn priv_prog`
`expect Password:`
`send` `"$expect_out(1,string)\r"`
`. . .`
`exit`
`}`
- exit:退出expect。
- exp_continue [-continue_timer]:继续执行下面的匹配。
- exp_internal [-f file] value:
expect范例
1.远程登录并创建文件后退出
#!/usr/bin/expect
##注意路径,使用 [whereis expect] 查看
set user "hadoop"
##设定参数,注意",'的区别
set pwd "yangkun"
set host "48.93.36.144"
set timeout -1
##;号可有可无
spawn ssh -p 2020 $user@$host
expect { ##expect后有空格
"*yes/no" {send "yes\r"; exp_continue}
"*password:" {send "$pwd\r"}
}
expect "]*" ## 通配符,使用 ]* 有效, 使用 *# 无效
send "touch /home/hadoop/aa.txt\r"
expect "]*"
send "echo hello world >> /home/hadoop/aa.txt\r"
expect "]*"
[interact] ##人为交互
send "exit\r" ##退出
2.配置免密登录并安装JDK
#!/bin/bash
#!/usr/bin/expect
SERVERS="114.114.114.114" ##数组以空格分隔,可以为目标ip 或者hostName
PASSWORD="yangkun"
## 实现免密登录配置的函数
auto_ssh_copy_id() {
expect -c "set timeout -1;
spawn ssh-copy-id \"-p 2020 $1\"; ## 这里要注意,使用'或\'不可行
expect {
*(yes/no)* {send -- yes\r;exp_continue;}
*password:* {send -- $2\r;exp_continue;}
eof {exit 0;}
}";
}
## 循环执行,配置主机到从节点所有免密
ssh_copy_id_to_all() {
for SERVER in $SERVERS ## 取值需要加$
do
auto_ssh_copy_id $SERVER $PASSWORD
done
}
## 调用循环配置函数
ssh_copy_id_to_all
## 批量部署for SERVER in $SERVERSdo
scp install.sh root@$SERVER:/root
ssh root@$SERVER /root/install.sh
done
- 读取文件中的host配置
让脚本自动读取slaves文件中的机器名来批量安装
cat slaves | while read host
do
echo $host
expect -c "set timeout -f
spawn ssh-copy-id $host"
done
3.批量配置JDK,install.sh
#!/bin/bash
BASE_SERVER=master
BASE_PATH=/home/hadoop/soft
TARGET_PATH=/usr/local
JAVA_PATH=$TARGET_PATH/java
## 1.判断是否存在文件夹,不存在则创建soft文件夹#
if [ ! -d "$BASE_PATH" ]; then
mkdir "$BASE_PATH"
fi
## 2.从指定host拷贝jdk到目标机器上(已经拷贝文件夹)
scp -r $BASE_SERVER:$BASE_PATH $BASE_PATH
## 2.解压jdk到指定目录
if [ ! -d "$JAVA_PATH" ]; then
sudo -S mkdir -p "$JAVA_PATH"
fi
## 赋予权限
sudo -S chmod -R hadoop:hadoop $JAVA_PATH
tar -zxvf $BASE_PATH/jdk1.8.0_121.tar.gz -C $JAVA_PATH
#### 3.配置环境变量
sudo -S cat>>/etc/profileexport JAVA_HOME=$JAVA_PATH/jdk1.8.0_121
export PATH=\$PATH:\$JAVA_HOME/bin
EOF
- 自动telnet会话
#!/usr/bin/expect -f
set ip [lindex $argv 0 ]
# 接收第1个参数,作为IP
set userid [lindex $argv 1 ]
# 接收第2个参数,作为userid
set mypassword [lindex $argv 2 ]
# 接收第3个参数,作为密码
set mycommand [lindex $argv 3 ]
# 接收第4个参数,作为命令
set timeout 10
# 设置超时时间# 向远程服务器请求打开一个telnet会话,并等待服务器询问用户名
spawn telnet $ip
expect "username:"
# 输入用户名,并等待服务器询问密码
send "$userid\r"
expect "password:"
# 输入密码,并等待键入需要运行的命令
send "$mypassword\r"
expect "%"
# 输入预先定好的密码,等待运行结果
send "$mycommand\r"
expect "%"
# 将运行结果存入到变量中,显示出来或者写到磁盘中
set results $expect_out(buffer)
# 退出telnet会话,等待服务器的退出提示EOF
send "exit\r"
expect eof
4.自动建立FTP会话
#!/usr/bin/expect -f
set ip [lindex $argv 0 ]
# 接收第1个参数,作为IP
set userid [lindex $argv 1 ]
# 接收第2个参数,作为Userid
set mypassword [lindex $argv 2 ]
# 接收第3个参数,作为密码
set timeout 10
# 设置超时时间# 向远程服务器请求打开一个FTP会话,并等待服务器询问用户名
spawn ftp $ip
expect "username:"
# 输入用户名,并等待服务器询问密码
send "$userid\r"
expect "password:"
# 输入密码,并等待FTP提示符的出现
send "$mypassword\r"
expect "ftp>"
# 切换到二进制模式,并等待FTP提示符的出现
send "bin\r"
expect "ftp>"
# 关闭ftp的提示符
send "prompt\r"
expect "ftp>"
# 下载所有文件
send "mget *\r"
expect "ftp>"
# 退出此次ftp会话,并等待服务器的退出提示EOF
send "bye\r"
expect eof
- 自动登录ssh执行命令
#!/usr/bin/expect
set IP [lindex $argv 0]
set USER [lindex $argv 1]
set PASSWD [lindex $argv 2]
set CMD [lindex $argv 3]
spawn ssh $USER@$IP $CMD
expect {
"(yes/no)?" {
send "yes\r"
expect "password:"
send "$PASSWD\r"
}
"password:" {send "$PASSWD\r"}
"* to host" {exit 1}
}
expect eof
5.自动登录ssh
#!/usr/bin/expect -f
set ip [lindex $argv 0 ]
# 接收第1个参数,作为IP
set username [lindex $argv 1 ]
# 接收第2个参数,作为username
set mypassword [lindex $argv 2 ]
# 接收第3个参数,作为密码
set timeout 10 # 设置超时时间
spawn ssh $username@$ip # 发送ssh请求
expect {
# 返回信息匹配
"*yes/no" { send "yes\r"; exp_continue} # 第一次ssh连接会提示yes/no,继续
"*password:" { send "$mypassword\r" } # 出现密码提示,发送密码
}
interact # 交互模式,用户会停留在远程服务器上面
6.批量登录ssh服务器执行操作范例,设定增量的for循环
#!/usr/bin/expect
for {set i 10}
{
$i set timeout 30
set ssh_user [lindex $argv 0]
spawn ssh -i .ssh/$ssh_user abc$i.com
expect_before "no)?" {
send "yes\r" }
sleep 1
expect "password*"
send "hello\r"
expect "*#"
send "echo hello expect! > /tmp/expect.txt\r"
expect "*#"
send "echo\r"
}
exit
7.批量登录ssh并执行命令,foreach语法
#!/usr/bin/expect
if {$argc!=2} {
send_user "usage: ./expect ssh_user password\n"
exit
}
foreach i {11 12} {
set timeout 30
set ssh_user [lindex $argv 0]
set password [lindex $argv 1]
spawn ssh -i .ssh/$ssh_user root@xxx.yy.com
expect_before "no)?" {
send "yes\r" }
sleep 1
expect "Enter passphrase for key*"
send "password\r"
expect "*#"
send "echo hello expect! > /tmp/expect.txt\r"
expect "*#"
send "echo\r"
}
exit
8.另一自动ssh范例,从命令行获取服务器IP,foreach语法,expect嵌套
#!/usr/bin/expect
# 使用方法: script_name ip1 ip2 ip3 ...set timeout 20
if {$argc "Usage: script IPs"
exit 1
}
# 替换你自己的用户名set user "username"
# 替换你自己的登录密码set password "yourpassword"
foreach IP $argv {
spawn ssh $user@$IP
expect \
"(yes/no)?" {
send "yes\r"
expect "password:?" {
send "$password\r"
}
} "password:?" {
send "$password\r"
}
expect "\$?"# 替换你要执行的命令
send "last\r"
expect "\$?"
sleep 10
send "exit\r"
expect eof
}
9.批量ssh执行命令,用shell调用tclsh方式、多进程同时执行
* tclsh - Simple shell containing Tcl interpreter
1
#!/bin/sh
# -*- tcl -*- \exec tclsh $0 "$@"
package require Expect
set username [lindex $argv 0]
set password [lindex $argv 1]
set argv [lrange $argv 2 end]
set prompt "(%|#|\\$) $"
foreach ip $argv {
spawn ssh -t $username@$ip sh
lappend ids $spawn_id
}
expect_before -i ids eof {
set index [lsearch $ids $expect_out(spawn_id)]
set ids [lreplace $ids $index $index]
if [llength $ids] exp_continue
}
expect -i ids "(yes/no)\\?" {
send -i $expect_out(spawn_id) yes\r
exp_continue
} -i ids "Enter passphrase for key" {
send -i $expect_out(spawn_id) \r
exp_continue
} -i ids "assword:" {
send -i $expect_out(spawn_id) $password\r
exp_continue
} -i ids -re $prompt {
set spawn_id $expect_out(spawn_id)
send "echo hello; exit\r"
exp_continue
} timeout {
exit 1
}
10.ssh登录过程常规提示文字
The authenticity of host '192.168.17.35 (192.168.17.35)' can't be established.
RSA key fingerprint is 25:e8:4c:89:a3:b2:06:ee:de:66:c7:7e:1b:fa:1c:c5.
Are you sure you want to continue connecting (yes/no)?
Warning: Permanently added '192.168.17.35' (RSA) to the list of known hosts.
Enter passphrase for key '/data/key/my_dsa':
Last login: Sun Jan 26 13:39:37 2014 from 192.168.11.143
[root@master003 ~]#
root@192.168.16.90's password:
Last login: Thu Jan 23 17:50:43 2014 from 192.168.11.102
[root@lvsmaster ~]#
11.ssh自动登录expect脚本:ssh.expect
#!/usr/bin/expect -f
# Auther:YuanXing
# Update:2014-02-08if {$argc "Usage:\n $argv0 IPaddr User Passwd Port Passphrase\n"
puts stderr "argv error!\n"
sleep 1
exit 1
}
set ip [lindex $argv 0 ]
set user [lindex $argv 1 ]
set passwd [lindex $argv 2 ]
set port [lindex $argv 3 ]
set passphrase [lindex $argv 4 ]
set timeout 6
if {$port == ""} {
set port 22
}
#send_user "IP:$ip,User:$user,Passwd:$passwd,Port:$port,Passphrase:$passphrase"
spawn ssh -p $port $user@$ip
expect_before "(yes/no)\\?" {
send "yes\r"}
expect \
"Enter passphrase for key*" {
send "$passphrase\r"
exp_continue
} " password:?" {
send "$passwd\r"
exp_continue
} "*\[#\\\$]" {
interact
} "* to host" {
send_user "Connect faild!"
exit 2
} timeout {
send_user "Connect timeout!"
exit 2
} eof {
send_user "Lost connect!"
exit
}
12.Mikrotik backup script using ssh and expect
#!/bin/bash
# TAG: mikrotik, ssh, expect, lftp
BACKUP_DIR="/var/backups"
HOSTNAME="192.168.88.1"
PORT="22"
USER="admin"
PASS="123456"
TMP=$(mktemp)
TODAY=$(date +%F)
FILENAME="$HOSTNAME-$TODAY"
PATH="/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin"# create expect script
cat > $TMP #exp_internal 1 # Uncomment for debug
set timeout -1
spawn ssh -p$PORT $USER@$HOSTNAME
match_max 100000
expect -exact "password:"
send -- "$PASS\r"
sleep 1
expect " > "
send -- "/export file=$FILENAME\r"
expect " > "
send -- "/system backup save name=$FILENAME\r"
expect " > "
send -- "quit\r"
expect eof
EOF
# run expect script#cat $TMP # Uncomment for debug
expect -f $TMP# remove expect script
rm $TMP# download and remove backup files# "xfer:clobber on" means overwrite existing filescd ${BACKUP_DIR}echo "
set xfer:clobber on
get ${FILENAME}.rsc
rm ${FILENAME}.rsc
get ${FILENAME}.backup
rm ${FILENAME}.backup" |
lftp -u $USER,$PASS $HOSTNAME
有一些需要自动输入密码的场景,可以用expect
自动与交互式程序会话
脚本
#!/usr/bin/expect -f
argv:接收参数
argc:参数个数
argv0:脚本名
选项
-c "cmd":指定在执行脚本之前执行的命令,这些命令最好用双引号括起,防止被shell分开解释,可以反复使用-c
-d:输出调试信息,报告expect和interact等命令执行时的内部行为,脚本开头写"exp_internal 1"也可以达到同样的效果
-D:交互式调试器
-f:指定命令文件
-i:交互式提示输入命令
--:划定选项尾,如脚本中写 #! /usr/bin/expect - ,则任何命令行的选项都会被解释为参数,被argv接收
命令
setfile “xxx” 设置参数file的值为"xxx"
close:关闭与当前进程的链接
expect和interact都可以检查到进程的退出,隐含执行一个close命令,如果用exec kill pid方式杀进程,则需要显示调用一次close指令
-slave 同时关闭从属进程
-onexec 0 保持spawn_id 开启,1关闭当前的spawn_id
-i 指定关闭的spawn_id
debug:调试
now立即启动调试器,0停止,1启动
disconnect:从终端断开与克隆进程的连接,克隆进程会在后台作为独立进程组继续运行,IO被重定向到/dev/null
send_user "password?\ "
expect_user -re "(.*)\n"
for {} 1 {} {
if [fork]!=0 {sleep 3600;continue}
disconnect #克隆进程继续运行
spawn priv_prog
expect Password:
send "$expect_out(1,string)\r"
. . .
exit
}
Exp_continue[-continue_timer]
这个命令可以使expect继续执行而不是正常的返回.默认情况下,exp_continue会重高超时时钟,-continue_timer选项会阻止时钟重新计数(连续计数).
Exp_internal [-f file] value
如果是value非零的话,使接下来的命令将调试信息输出到Expect和标准错误输出.如果是0的话,输出的信息将会被屏蔽.调试信息包括收到的每条信息和每次尝试用当前输出与脚本中的模式相匹配的信息.如果设置了输出文件,那么正常的和调试的信息都会被写到这个文件当中.(忽略上面value选项的值).任何之前打开的调试输出文件将会被关闭.-info选项使exp_internal返回最近关于non-info参数的描述
Exp_open [args] [-I spawn_id]
它返回对应于原始spawn id的文件描述符.这样这个文件描述符就可以被使用了,就好像这个文件是被Tcl的open指令打开的一样.(这个spawn id将不再使用,wait指令将不能用在这个进程.).-leaveopen选项使spawn id保持打开,以便供Expect命令使用
Exp_pid [-i spawn_id]
它将返回对应于当前被跟踪进程的ID.如果使用-i选项,将返回对应于指定的spawn id的进程ID.
Exp_send:send别名
Exp_send_error:它是Send_error
Exp_send_log:Send_log
Exp_send_tty:Send_tty
Exp_send_user:Send_user
Exp_version [[-exit] version]
它用于确保脚本程序与当前的Expect兼容。在没有参数的情况下,返回当前Expect的版本.这个版本就会编译到脚本中.如果你确切的知道你的脚本程序不需要最新版本的特性,可以指定一个以前的版本
expect_tty [expect_args ],输入是一个tty
expect_user [expect_args],输入是stdin
expect [[-opts] pat1 body1] ... [-opts] patn [bodyn]
等待被监控进程的输出,如果匹配到指定输出字符串、遇到文件尾、或超时时,则执行对应的body体,如果body是空的,将被忽略
-nocase 匹配时不区分大小写
-timeout:选项使得Expect使用选项后面的数值做为超时时间,而不是timeout变量中设置的时间
expect_befor模式,在所有匹配之前隐含调用
expect_after模式,在所有匹配之后隐含调用
expect {
busy {puts busy\n ; exp_continue} # 输出值有busy执行{}里面的语句
failed abort # 输出有failed,执行abort,abort是预定义的一个函数
"invalid password" abort #输出字符串中间有空格,需要用双引号括起来
-re "failed|invalid password" abort #使用正则表达式
-ex "failed|invalid *password" abort #精准正则,不对* ^等转义
-i $proc2 busy {puts busy\n ; exp_continue} #匹配$proc2进程的输出,默认是当前进程的输出
-i $proc3 # 匹配到执行XXX
-i $proc4 # 匹配到执行XXX
any_spawn_id XXX
timeout abort #超时时触发
full_buffer abort #输出的值超过最大的match_max设置的值是触发
null #输出ascii 0时触发
connected
}
匹配时,任何输出都会被保存到expect_out缓冲区中,匹配到的9个字串分别被保存在expect_out(1, string) ~ expect_out(9, string),如果在模式前面使用了 -indeces 选项,则这9个字串的起始位置和结束位置保存在expect_out(X, start)和expect(X, end)中,X∈[0, 9]。expect(0,*)是匹配到的整个字符串,下面三条语句等效
expect "cd"
Set expect_out(0,string) cd
Set expect_out(buffer) abcd
-i选项还可以定义一个全局变量,里面存储着spawn_id列.当变量内容发生变化时,它会被重新读取.这样就可以在程序执行的时候改变I/O源.以这种方式提供的spawn_id被称为”indirect spawn_id”
Fork
复制一个进程,返回新进程ID,失败返回-1
interact [string1 body1] ... [stringn [bodyn]]
将控制权给用户,根据用户的输入,触发相应的动作
set CTRLZ \032
interact {
-reset $CTRLZ {exec kill -STOP [pid]} # 按下ctrl+z触发
\001 {send_user "you typed a control-A\n"; # 发送一个字符串到终端
send "\001"
}
$ {send_user "The date is [exec date]."}
\003 exit
foo {send_user "bar"} # 用户输入foo时触发
~~
}
send:发送字符串给当前进程
send_user:发送字符串到标准输出
send_error:发送字符串到标准错误
send_log:发送字符串到日志文件
send_tty:发送字符串到tty
sleep seconds:休眠
spawn [args] program [args]
创建一个执行program命令的进程,这个进程的三个标准IO都被重定向到expect,
spawn su - ftomcat -c "tar -xzf /tmp/ftomcat.tgz . 1>/dev/null 2>/dev/null"
expect "Password"
send "ftomcat\r"
expect eof
exit
_close.pre_expect
after
append
apply
array
auto_execok
auto_import
auto_load
auto_load_index
auto_qualify
binary
break
case
catch
cd
chan
clock
close
close_on_eof
concat
continue
coroutine
debug
dict
disconnect
encoding
eof
error
eval
exec
exit
exp_close
exp_close_on_eof
exp_configure
exp_continue
exp_debug
exp_disconnect
exp_exit
exp_fork
exp_getpid
exp_inter_return
exp_interact
exp_internal
exp_interpreter
exp_log_file
exp_log_user
exp_match_max
exp_open
exp_overlay
exp_parity
exp_pid
exp_remove_nulls
exp_send
exp_send_error
exp_send_log
exp_send_tty
exp_send_user
exp_sleep
exp_spawn
exp_strace
exp_stty
exp_system
exp_timestamp
exp_trap
exp_version
exp_wait
expect
expect_after
expect_background
expect_before
expect_tty
expect_user
expr
fblocked
fconfigure
fcopy
file
fileevent
flush
for
foreach
fork
format
getpid
gets
glob
global
history
if
incr
info
inter_return
interact
interp
interpreter
join
lappend
lassign
lindex
linsert
list
llength
lmap
load
log_file
log_user
lrange
lrepeat
lreplace
lreverse
lsearch
lset
lsort
match_max
namespace
open
overlay
package
parity
pid
proc
prompt1
prompt2
puts
pwd
read
regexp
regsub
remove_nulls
rename
return
scan
seek
send
send_error
send_log
send_tty
send_user
set
sleep
socket
source
spawn
split
strace
string
stty
subst
switch
system
tailcall
tclLog
tell
throw
time
timestamp
trace
trap
try
unknown
unload
unset
update
uplevel
upvar
variable
vwait
wait
while
yield
yieldto
zlib
============ End