Shell 还有一种特殊形式的重定向叫做“Here Document”,目前没有统一的翻译,你可以将它理解为“嵌入文档”“内嵌文档”“立即文档”。
所谓文档,就是命令需要处理的数据或者字符串;所谓嵌入,就是把数据和代码放在一起,而不是分开存放(比如将数据放在一个单独的文件中)。有时候命令需要处理的数据量很小,将它放在一个单独的文件中有点“大动干戈”,不如直接放在代码中来得方便。
command <<END
document
END
command
是 Shell 命令,<
END
是结束标志,document
是输入的文档(也就是一行一行的字符串)。
这种写法告诉 Shell 把 document 部分作为命令需要处理的数据,直到遇见终止符END
为止(终止符END
不会被读取)。
注意,终止符END
必须独占一行,并且要定顶格写。
分界符(终止符)可以是任意的字符串,由用户自己定义,比如 END、MARKER 等。分界符可以出现在正常的数据流中,只要它不是顶格写的独立的一行,就不会被作为结束标志。
标记可以使用任意合法字符
结尾的标记一定要顶格写,前面不能有任何字符!!
结尾的标记后面也不能有任何字符(包括空格)
开头标记前后的空格会被省略掉
【实例1】cat 命令一般是从文件中读取内容,并将内容输出到显示器上,借助 Here Document,cat 命令可以从键盘上读取内容。
[mozhiyan@localhost ~]$ 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也是允许的
[root@hua opt]# read i <<EOF
> hello boy
> my name is lisi
> EOF
[root@hua opt]# echo $i
hello boy
#只能赋予第一行的内容
[root@shuai ~]# vim shuai.sh
#!/bin/bash
passwd liu <<EOF
123123 '每一行对应每一次的交互'
123123
EOF
[root@shuai ~]# bash shuai.sh
更改用户 liu 的密码 。
新的 密码:无效的密码: 密码少于 8 个字符
重新输入新的 密码:passwd:所有的身份验证令牌已经成功更新。
[root@hua opt]# vim txt.sh
#!/bin/bash
shuai="1.txt"
lisi="kgc"
cat > $shuai <<EOF
this is $lisi .com
EOF
[root@hua opt]# ls
1.txt EOF hao.sh httpd.txt rh ss.sh txt.sh xx.txt
[root@hua opt]# cat 1.txt
this is kgc .com
[root@hua opt]# num1=20
[root@hua opt]# echo $num1
20
[root@hua opt]# num2=$(cat <<EOF
> 100
> $num1
> EOF
> )
[root@hua opt]# echo $num2
100 20
[root@hua opt]# echo $num1
20
[root@hua opt]# cat <<'EOF'
> 100
> $num1
> EOF
100
$num1
[root@hua opt]# vim b.sh
#!/bin/bash
cat <<-'EOF' '-表示抑制行首的TAB作用'
this is
$shuai .com
EOF
[root@hua opt]# bash b.sh
this is
$shuai .com
[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"i
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是一个自动化交互套件,主要应用于执行命令和程序时,系统以交互形式要求输入指定字符串,实现交互通信。
spawn启动指定进程—expect获取指定关键字—send向指定程序发送指定字符—执行完成退出.
注意该脚本能够执行的前提是安装了expect
yum install -y expect
spawn:启动进程,并跟踪后续交互信息
send:向进程发送字符串,用于模拟用户的输入
该命令不能自动回车换行,一般要加、r(回车)
expect
expect的一个内部命令,判断上次输出结果里是否包含指定的字符串,如果有则立即返回,否则就等待超时时间后返回
只能捕捉有spawn启动的进程的输出
interact:执行完成后保持交互状态,把控制权交给控制台
$argv参数数组
Expect脚本可以接受从bash传递的参数,可以使用[lindex $argc n]获得,n从0开始,分别表示第一个,第二个,第三个…参数
arg:参数
v:value
Expect脚本的结尾
expect脚本必须以interact或expect eof结束,执行自动化任务通常expect eof就够了
expect eof是在等待结束标志。由spawn启动的命令在结束时会产生一个eof标记,expect eof就是在等待这个标记
单一分支语法
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参数表示匹配正则表达式
其中,$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
[root@hua opt]# vim xx.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@hua opt]# bash xx.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:10:07 2020 from 192.168.1.21
[root@shuai ~]# exit
登出
Connection to 192.168.1.20 closed.
创建用户lisi6 密码123123
[root@promote ~]# vim ss.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@promote ~]# bash ss.sh lisi6 123123
spawn passwd lisi6
更改用户 lisi6 的密码 。
新的 密码:
无效的密码: 密码少于 8 个字符
重新输入新的 密码:
passwd:所有的身份验证令牌已经成功更新。
expect: spawn id exp6 not open
while executing
"expect "成功""
#!/bin/bash
hostname=$1
password=$2
/usr/bin/expect <<-EOF
spawn ssh $hostname
expect {
"connection refused" exit
"Name or service not known" exit
"to continue" {send "yes\r";exp_continue}
"password:" {send "$password\r"}
}
expect "*]#"
send "exit\r"
expect eof
EOF
#正常登录
[root@hua opt]# ./ssh.sh 192.168.1.20 123123
spawn ssh 192.168.1.20
root@192.168.1.20's password:
Last failed login: Tue Jul 28 22:56:10 CST 2020 from 192.168.1.21 on ssh:notty
There was 1 failed login attempt since the last successful login.
Last login: Tue Jul 28 22:30:52 2020 from 192.168.1.21
[root@shuai ~]# exit
登出
Connection to 192.168.1.20 closed.
[root@hua opt]# vim ssh.sh