一: Shell Here Document(内嵌文档/嵌入文档)
Shell 还有一种特殊形式的重定向叫做“Here Document”,目前没有统一的翻译,你可以将它理解为“嵌入文档”“内嵌文档”“立即文档”。
所谓文档,就是命令需要处理的数据或者字符串;所谓嵌入,就是把数据和代码放在一起,而不是分开存放(比如将数据放在一个单独的文件中)。有时候命令需要处理的数据量很小,将它放在一个单独的文件中有点“大动干戈”,不如直接放在代码中来得方便。
1.1:Here Document 的基本用法为:
command <<END
document
END
command是 Shell 命令,<<END是开始标志,END是结束标志,document是输入的文档(也就是一行一行的字符串)。
这种写法告诉 Shell 把 document 部分作为命令需要处理的数据,直到遇见终止符END为止(终止符END不会被读取)。
注意,终止符END必须独占一行,并且要定顶格写。
分界符(终止符)可以是任意的字符串,由用户自己定义,比如 END、MARKER 等。分界符可以出现在正常的数据流中,只要它不是顶格写的独立的一行,就不会被作为结束标志。
1.2:注意事项
标记可以使用任意合法字符
结尾的标记一定要顶格写,前面不能有任何字符!!
结尾的标记后面也不能有任何字符(包括空格)
开头标记前后的空格会被省略掉
【实例1】cat 命令一般是从文件中读取内容,并将内容输出到显示器上,借助 Here Document,cat 命令可以从键盘上读取内容。
[root@server1 ~]# cat <<END
> Shell教程
> http://c.biancheng.net/shell/
> 已经进行了四次改版
> END
Shell教程
http://c.biancheng.net/shell/
已经进行了四次改版
<是第二层命令提示符。
正文中也可以出现结束标志END,只要它不是独立的一行,并且不顶格写,就没问题。
[mozhiyan@localhost ~]$ cat <<END
> END可以出现在行首
> 出现在行尾的END
> 出现在中间的END也是允许的
> END
END可以出现在行首
出现在行尾的END
出现在中间的END也是允许的
1.3:通过read命令接受输入并打印
[root@server1 ~]# read i <<EOF
> hello
> mysql
> EOF
[root@server1 ~]# echo $i
hello
1.4:通过passwd给用户设置密码
[root@server1 ~]# vim shuai.sh
#!/bin/bash
passwd tom <<EOF
123123 '每一行对应每一次的交互'
123123
EOF
[root@server1 ~]# bash a.sh
更改用户 tom 的密码 。
新的 密码:无效的密码: 密码少于 8 个字符
重新输入新的 密码:passwd:所有的身份验证令牌已经成功更新。
1.5:变量替换
#!/bin/bash
tom="1.txt"
lisi="2.txt"
cat > $tom <<EOF
this is $lisi .com
EOF
[root@server1 ~]# cat 1.txt
this is 2.txt .com
1.6:变量设定
[root@server1 test]# num1=10
[root@server1 test]# echo $num1
10
[root@server1 test]# num2=$(cat <<EOF
> 100
> $num1
> EOF
> )
[root@server1 test]# echo $num2
100 10
1.7:关闭变量的替换功能
[root@server1 test]# cat <<'EOF'
> 100
> $num1
> EOF
100
$num1
1.8:去除每行之前的TAB字符
[root@server1 test]# vim b.sh
#!/bin/bash
cat <<-'EOF' '-表示抑制行首的TAB作用'
this is
$tom .com
EOF
[root@server1 test]# bash b.sh
this is
$tom .com
1.9:通过Here Document方式是Bash支持多行注释
[root@hua opt]# vim n.sh
#!/bin/bash
echo "hello"
echo "girl"
cat <<-'EOF'
this is
$school .ch
EOF
[root@hua opt]# bash n.sh
hello
girl
this is
$school .ch
###注释后
[root@hua opt]# vim n.sh
#!/bin/bash
: <<DD
echo "hello"
echo "girl"
DD
cat <<-'EOF'
this is
$school .ch
EOF
[root@hua opt]# bash n.sh
this is
$school .ch
如果你尝试在脚本嵌入一小块多行数据,使用 Here Document 是很有用的,而嵌入很大的数据块是一个不好的习惯。你应该保持你的逻辑(你的代码)和你的输入(你的数据)分离,最好是在不同的文件中,除非是输入一个很小的数据集。
Here Document 最常用的功能还是向用户显示命令或者脚本的用法信息,例如类似下面的函数:
usage(){
cat <<-END
usage: command [-x] [-v] [-z] [file ...]
A short explanation of the operation goes here.
It might be a few lines long, but shouldn't be excessive.
END
}
二:Expect 介绍与用法
expect是一个自动化交互套件,主要应用于执行命令和程序时,系统以交互形式要求输入指定字符串,实现交互通信。
2.1:expect自动交互流程:
spawn启动指定进程—expect获取指定关键字—send向指定程序发送指定字符—执行完成退出.
注意该脚本能够执行的前提是安装了expect
yum install -y expect
2.2:基本命令(expect流程命令)
spawn:启动进程,并跟踪后续交互信息
send:向进程发送字符串,用于模拟用户的输入
该命令不能自动回车换行,一般要加、r(回车)
expect
expect的一个内部命令,判断上次输出结果里是否包含指定的字符串,如果有则立即返回,否则就等待超时时间后返回
只能捕捉有spawn启动的进程的输出
interact:执行完成后保持交互状态,把控制权交给控制台
2.3:基本命令(expect内容命令)
Timeout:指定超时时间,过期则继续执行后续指令
单位是:秒
timeout -1为永不超时
默认情况下,timeout是10秒
exp_continue
允许expect继续向下执行指令
send_user
回显命令,相当于echo
2.4:基本命令(expect其他命令)
$argv参数数组
Expect脚本可以接受从bash传递的参数,可以使用[lindex $argc n]获得,n从0开始,分别表示第一个,第二个,第三个…参数
arg:参数
v:value
Expect脚本的结尾
expect脚本必须以interact或expect eof结束,执行自动化任务通常expect eof就够了
expect eof是在等待结束标志。由spawn启动的命令在结束时会产生一个eof标记,expect eof就是在等待这个标记
2.5:Expect执行方式
单一分支语法
expect "password:" {send "mypassword\r"}
多分支模式语法第一种
expect "aaa" {send "AAA\r"}
expect "aaa" {send "AAA\r"}
expect "aaa" {send "AAA\r"}
'send命令不具备回车换行功能,所以需要自己添加\r 或 \n'
多分支模式语法第二种
expect {
"aaa" {send "AAA\r"}
"bbb" {send "BBB\r"}
"ccc" {send "CCC\r"}
}
'只要匹配了aaa 或bbb或ccc中的任何一个,执行相应的send语句后就会退出该expect语句'
expect {
"aaa" {send "AAA\r";exp_continue}
"bbb" {send "BBB\r";exp_continue}
"ccc" {send "CCC\r"}
}
'exp_continue表示继续后面的匹配,如果匹配了aaa,执行完send语句后还会继续向下匹配bbb'
'捕捉内容要用双引号引起来'
'send要写在{}中,输出信息也要用双引号引起来,分号“;”要写在}里面'
-re参数表示匹配正则表达式
2.6: Expect直接执行(以ssh为例)
其中,$argv 0 代表位置变量$1
$argv 1 代表位置变量$2
#!/usr/bin/expect 是Expect二进制文件的路径 预加载环境
#!/usr/bin/expect
#超时时间
set timeout 10
#开启日志
log_file test.log
#显示信息
log_user 1
#定义变量
set hostname [lindex $argv 0]
set password [lindex $argv 1]
#追踪指令
spawn ssh root@${hostname}
#捕捉提示信息
expect {
"connecting (yes/no)"
{send "yes\r";exp_continue}
"*password:"
{send "${password}\r";}
}
#转交控制台
interact
-----------执行----------------
[root@hua opt]# chmod +x 2.sh
[root@hua opt]# ./2.sh 192.168.1.20 123123
spawn ssh root@192.168.1.20
root@192.168.1.20's password:
Last login: Tue Jul 28 22:01:09 2020 from 192.168.1.21
2.7:Expect嵌入执行(以ssh为例)
[root@server1 test]# vi ssh.sh
#!/bin/bash
hostname=$1
password=$2
#expect嵌入
/usr/bin/expect <<-EOF
spawn ssh root@${hostname}
#捕捉提示信息
expect {
"connecting (yes/no)"
{send "yes\r";exp_continue}
"*password:"
{send "${password}\r";}
}
expect "*]#"
send "exit\r"
expect eof
EOF
~
[root@server1 test]# sh ssh.sh 192.168.158.60 123123
spawn ssh root@192.168.158.60
The authenticity of host '192.168.158.60 (192.168.158.60)' can't be established.
ECDSA key fingerprint is SHA256:I3pGxKAQd78NEtldEi1ezgKT9uiA+cIcXz72pNUrfi8.
ECDSA key fingerprint is MD5:61:b6:d5:1f:7f:61:8a:40:4d:1f:28:23:42:1e:25:ab.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.158.60' (ECDSA) to the list of known hosts.
root@192.168.158.60's password:
Last login: Wed Nov 11 20:17:21 2020 from 192.168.158.1
[root@localhost ~]# exit
登出
Connection to 192.168.158.60 closed.
[root@server1 test]#
2.8:Expect实操
[root@server1 test]# vi tom.sh
#!/bin/bash
user=$1
password=$2
useradd $user
/usr/bin/expect <<-EOF
spawn passwd $user
expect {
"新的 密码:"
{send "$password\r";exp_continue}
"重新输入新的 密码:"
{send "${password}\r";}
}
expect "成功"
send "exit\r"
expect eof
EOF
[root@server1 test]# sh tom.sh tom2 123123
spawn passwd tom2
更改用户 tom2 的密码 。
新的 密码:
无效的密码: 密码少于 8 个字符
重新输入新的 密码:
passwd:所有的身份验证令牌已经成功更新。
expect: spawn id exp6 not open
while executing
"expect "成功""