Linux中的Expect命令

时间:2020-03-21 11:43:31  来源:igfitidea点击:

自动执行重复性任务是系统管理员一生的习惯。
通常,管理员会使用不同的编程语言编写脚本来处理这些重复性任务。

大多数Linux系统管理员的默认选择是利用“ BASH Shell”及其脚本功能。
与Python,Ruby和Perl不同,Bash不是完全成熟的编程语言。

但是它确实提供了许多编程功能来完成工作(例如条件语句,case语句以及许多内置功能可以在脚本中进行决策)。
由于它不是一门成熟的语言,因此我们必须使用其他小程序来完成工作。

基本上,脚本不过是安排在文件中的一系列命令(通常是由不同程序提供的命令),可以达到最终结果。

有些程序本质上是交互式的(基本上,这些程序是通过牢记人类交互来构建的)。
例如,在Linux中更改用户密码。
这就要求最终用户在“ passwd”程序要求时输入新密码,然后再次重新输入一次以进行第二次验证。

另一个示例可以运行诸如“ fsck”之类的程序来验证一个文件系统。
诸如“ fsck”之类的程序将提示我们一些问题,并且它会在继续进行之前期待它的答案(主要是诸如“是”或者“否”之类的答案)。

因此,如果要在脚本中包含这些程序,我们需要一种自动为这些程序提供答案的方法。

这是一个名为“ Expect”的程序的介入之处。

程序本身的名称为我们提供了有关其用例的想法。
我们告诉“期望”在执行另一个交互式程序时要寻找什么。
我们还告诉响应应该是什么。

安装Expect很简单直接。
我们可以使用apt-get或者yum,具体取决于我们所使用的Linux的风格。
对于基于Red Hat的系统,执行以下命令。

yum install expect

对于基于Debian的或者Ubuntu的,执行以下命令。

apt-get install expect

首先创建一个交互式脚本,然后创建一个“期望”程序来处理我们的交互式脚本。
这样,我们将能够理解Expect的用例。
首先使用以下内容创建一个名为“ interactive_program.sh”的文件。

#!/bin/bash
echo "What is your name?"
read ANSWER
echo "How many dogs you have?"
read ANSWER
echo "How many cats you have?"
read ANSWER

现在,我们需要授予上述文件的可执行权限。
可以使用以下命令完成此操作。

chmod +x interactive_program.sh

现在,我们有一个交互式脚本,提示了几个问题。
Expect将执行此脚本,并将提供这些问题的答案。
实际上,我们本身并不关心答案,我们关心的是“期望”如何处理此交互式脚本。

现在,让我们编写另一个小的期望脚本,该脚本将执行上述脚本并自动提交问题的答案。
第一步是找出“期望”命令的位置。

whereis expect
expect: /usr/bin/expect /usr/bin/X11/expect /usr/share/man/man1/expect.1.gz

现在,我们知道Expect位于/usr/bin/expect(此位置可能因系统而异。
因此,我建议启动上述“ whereis”命令以查找计算机上的实际位置。

创建一个名为“ expect_script.sh”的文件,其中包含以下内容。

#!/usr/bin/expect
spawn ./interactive_program.sh
expect -exact "What is your name?\r"
send -- "Sam\r"
expect -exact "How many dogs you have?\r"
send -- "2\r"
expect -exact "How many cats you have?\r"
send -- "2\r"
expect eof

现在,像在使用以下命令之前一样,赋予可执行文件权限。

chmod +x expect_script.sh

第一行是shebang。
在这种情况下,它使用/usr/bin/expect而不是/bin/bash。
该脚本中的所有命令都将在exe程序而不是bash下执行。
spawn将启动我们的交互式脚本。
然后,我们告诉“期望”寻找一条写着“我们叫什么名字?”的行。
后跟换行符(由\ r表示)。

然后,我们要求“期望”发送sam的输入值,后跟换行符/返回字符(再次用\ r指示)。
然后,我们要求期望寻找一条显示“我们有几只狗?”的行。
后跟换行符(\ r)。
然后,使用send发送输入值2,然后按Enter键(用\ r表示)。
下一个问题也使用相同的方法。

Expect eof表示脚本到此结束。

现在,我们可以执行文件“ expect_script.sh”,并查看期望自动给出的所有响应。

./expect_script.sh
spawn ./interactive_program.sh
What is your name?
Sam
How many dogs you have?
2
How many cats you have?
2

现在让我们写一个更实际的Expect例子。
带有一些变量和值。

众所周知,Linux passwd程序(可让我们更改用户密码。
如果我们是root用户,则可以使用passwd程序更改任何用户密码)提示最终用户输入。

它要求输入密码,然后重新输入以进行验证。
让我们创建一个期望脚本,该脚本支持最终用户的两个参数。
这些参数将是用户名和密码。
然后,Expect应该注意更改该用户的密码。
创建具有以下内容的名为password_change.sh的文件。

#!/usr/bin/expect
set user_name [lindex $argv 0]
set pass_word [lindex $argv 1]
spawn passwd $user_name
expect -exact "Enter new UNIX password: "
send -- "$pass_word\r"
expect -exact "\rRetype new UNIX password: "
send -- "$pass_word\r"
expect eof

不要忘记执行chmod + x password_change.sh

这次,我们在Expect脚本中使用了两个变量。
前两行set user_name [lindex $argv 0]和set pass_word [lindex $argv 1]从提供的命令行参数创建两个名为user_name和pass_word的变量。
第一个参数是用户名,第二个参数是密码

生成行在触发passwd程序时使用user_name变量。
在Expect中使用变量的方式与在bash中几乎相同。

与我们之前的期望脚本类似,该脚本也以期望eof结 tail,并使用\ r表示输入/返回字符。
现在让我们执行脚本,如下所示。

./password_change.sh testuser tfgh3425k
spawn passwd testuser
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully

我们可能会认为构造一个期望脚本确实很痛苦,因为需要为其提供完全相同的换行符,返回字符以及其他输入格式等。
我同意。
在第一次尝试中获得期望脚本并不容易。
为了使我们的生活更轻松,还有一个名为“自动预期”的帮助程序。

autoexpect是一个命令,我们可以使用该命令来构造期望脚本。
基本上使用autoexpect命令执行一个交互式程序,它将为该交互式程序创建一个期望脚本。

对于Ubuntu,autoexpect是另一个名为Expect-dev的软件包的一部分。
因此,我们需要先安装它。
在基于Red Hat的系统中,必须已经与Expect一起安装了autoexpect。

apt-get install expect-dev

使用autoexpect非常简单明了。

我们只需在自动程序前面加上自动预期就可以执行该程序。
例如,我们先前编写的脚本可以作为自动预期的参数提供,如下所示。
然后,将创建一个名为script.exp的文件。
如下所示。

autoexpect ./interactive_program.sh
autoexpect started, file is script.exp
What is your name?
Sarath
How many dogs you have?
3
How many cats you have?
0
autoexpect done, file is script.exp

基本上,autoexpect将捕获作为参数传递的交互式程序的行为,然后创建一个自动执行相同操作的Expect脚本。
我们需要基本更改script.exp的内容以符合要求(可能是更改值或者添加变量等)。

除了所有这些之外,expect还支持常规的编程功能,例如if else语句,case语句等。
Expect支持的这些条件语句与标准bash shell条件语句略有不同。

实际期望使用TCL语言提供的大多数功能。
TCL支持许多不同类型的编程方法。
它简单而强大。

让我们修改密码更改脚本,并包含一个for循环,以供多个用户使用。
请记住,此for循环与bash for循环不同。
这是TCL的循环语言(由Expect使用,并且由于脚本以Expect的开头开始,所以该tcl语句可以很好地工作。
这是因为文件中的所有内容都将由Expect程序解释。

#!/usr/bin/expect
set l [list testuser testuser1 testuser2 testuser3]
set pass_word sf345234
foreach user_name $l {
    spawn passwd $user_name
    expect -exact "Enter new UNIX password: "
    send -- "$pass_word\r"
    expect -exact "\rRetype new UNIX password: "
    send -- "$pass_word\r"
    expect eof
}

请注意,以上脚本假定系统中存在用户testuser,testuser1,testuser2和testuser3.
行集l [list item1 item2]创建一个名称为“ l”的列表。
foreach user_name $l行遍历列表“ l”的每个元素,并将所有密码设置为“ sf345234”。

现在让我们看一看带有Expect的while循环(基本上,Expect提供的这些条件来自TCL语言。
因此,在tcl中工作的任何条件在理想情况下也应该与Expect一起工作)。

#!/usr/bin/expect
set pass_word sf345234
set m 0
while {$m<10} {
    spawn passwd "testuser$m"
    expect -exact "Enter new UNIX password: "
    send -- "$pass_word\r"
    expect -exact "\rRetype new UNIX password: "
    send -- "$pass_word\r"
    expect eof
        incr m
}

在上面的示例中,我们创建了一个名为m的变量,其初始值为0。
然后构造了一个while循环,该循环运行到9,并且在每次迭代过程中,m的值均增加1(由incr m表示)。
这也期望系统中有10个用户,其格式为testuser0,testuser1,testuser2,依此类推,直到testuser9.
上面的脚本应如下所示执行。

./password_change.sh
spawn passwd testuser0
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
spawn passwd testuser1
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
spawn passwd testuser2
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully

让我们在上面显示的脚本中包含一个if语句。

#!/usr/bin/expect
set m 0
while {$m<10} {
        set pass_word sf345234
        if { $m == 2 } {
               set pass_word asfjhfajhakasfa234
        }
    spawn passwd "testuser$m"
    expect -exact "Enter new UNIX password: "
    send -- "$pass_word\r"
    expect -exact "\rRetype new UNIX password: "
    send -- "$pass_word\r"
    expect eof
        incr m
}

如果我们之前使用过条件,则上面使用的if语句很容易说明。
它只是将用户“ testuser2”的密码更改为复杂的密码。