脚本自动化
脚本信号
信号基础
基础知识
当我们在构建一些更高级的脚本的时候,就会涉及到如何在linux系统上来更好的运行和控制它们,到目前为止,我们运行脚本的方式都是以实时的模式,在命令行来运行它。但是这并不是脚本唯一的运行方式,我们可以在linux系统中以更丰富的方式来运行它们,甚至在脚本遇到不可查的异常中止时,以关闭linux终端界面的方式终止脚本。这些能力都是基于信号的机制来实现了
linux使用信号与系统上运行的进程进行通信,想要对shell的脚本控制,只需要传递相关信号给shell脚本即可
信号 | 值 | 描述 | 信号 | 值 | 描述 |
---|---|---|---|---|---|
1 | SIGHUP | 挂起进程 | 15 | SIGTERM | 优雅的终止进程 |
2 | SIGINT | 终止进程 | 17 | SIGSTOP | 无条件停止进程,不终止进程 |
3 | SIGQUIT | 停止进程 | 18 | SIGTSTP | 停止或暂停进程,不终止进程 |
9 | SIGKILL | 无条件终止进程 | 19 | SIGCONT | 继续运行停止的进程 |
默认情况下,bash shell会忽略收到的任何SIGQUIT(3)和SIGTERM(15)信号(正因为这样交互式shell才不会被意外终止)。但是bash shell会处理收到的SIGHUP(1)和SIGINT(2)信号
如果bash shell收到SIGHUP信号,它会退出。但在退出之前,它会将信号传给shell启动的所有进程(比如shell脚本)。通过SIGINT信号,可以中断shell,Linux内核停止将CPU的处理时间分配给shell,当这种情况发生时,shell会将SIGINT信号传给shell启动的所有进程
生成信号
- 终止进程,
ctrl+c
- 暂停进程,
ctrl+z
,停止的进程继续保留在内存中,并能从停止的位置继续运行 - 恢复进程,jobs查看运行任务,fg num 重新执行
- 杀死进程,
kill -9 pid
简单实践
# 实践1-终止进程
sleep 1000
^C
# 实践2-挂起进程
sleep 1000
^Z
[1]+ Stopped sleep 1000
ps aux | grep sleep
root 14980 0.0 0.0 108052 360 pts/3 T 09:28 0:00 sleep 1000
root 14982 0.0 0.0 112808 976 pts/3 R+ 09:28 0:00 grep --color=auto sleep
# 实践3-恢复进程
# 查看所有挂起进程
jobs
[1]+ Stopped sleep 1000
# 恢复挂起进程的id
fg 1
sleep 1000
^C
# 实践4-杀死进程
# 后台执行命令
sleep 1000 &
[1] 14983
ps aux | grep sleep | grep -v grep
root 14983 0.0 0.0 108052 356 pts/3 S 09:30 0:00 sleep 1000
# 强制杀死进程
kill -9 14983
jobs
[1]+ Killed sleep 1000
信号捕捉
基础知识
shell编程提供了一种方式,让我们可以随意的控制脚本的运行状态,这就需要涉及到信号的捕获操作。在shell编程中,我们可以借助于 trap命令实现指定shell脚本要watch哪些linux信号并从shell中拦截。如果脚本收到了trap命令中列出的信号,它会阻止它被shell处理,而在本地处理
trap命令格式
命令格式
trap commands signals
命令示例:
# 收到指定信号后,执行自定义指令,而不会执行原操作
trap '触发指令' 信号
# 忽略信号的操作
trap '' 信号
# 恢复原信号的操作
trap '-' 信号
# 列出自定义信号操作
trap -p
# 当脚本退出时,执行finish函数
trap finish EXIT
简单实践
实践1-捕获终止信号
signal_trap_test1.sh
#!/bin/bash
# 功能:脚本信号捕捉
# 捕获关闭信号
trap "你敢关我,就不关,气死你" SIGINT SIGTERM
trap "走了,不送" EXIT
# 检测逻辑效果
while true
do
read -p "请输入一个数据:" value
echo "您输入的数据是: ${value}"
done
/bin/bash signal_trap_test1.sh
请输入一个数据:4
您输入的数据是: 4
请输入一个数据:^Csignal_trap_test1.sh: line 1: 你敢关我,就不关,气死你: command not found
您输入的数据是:
请输入一个数据:^Z
[1]+ Stopped /bin/bash signal_trap_test1.sh
jobs
[1]+ Stopped /bin/bash signal_trap_test1.sh
fg 1
/bin/bash signal_trap_test1.sh
您输入的数据是:
请输入一个数据:3
您输入的数据是: 3
请输入一个数据:
# 另开一个终端,直接kill进程
ps aux | grep sign | grep -v grep
root 15132 0.0 0.1 113288 1452 pts/3 S+ 10:00 0:00 /bin/bash signal_trap_test1.sh
kill -9 15132
# 回到之前的终端查看效果
fg 1
/bin/bash signal_trap_test1.sh
您输入的数据是:
请输入一个数据:3
您输入的数据是: 3
请输入一个数据:Killed
实践2-捕获正常退出
signal_trap_test2.sh
#!/bin/bash
# 功能:脚本信号捕捉
# 捕获关闭信号
trap "echo '走了.不送'" EXIT
value="0"
# 检测逻辑效果
while true
do
read -p "请输入一个数据:" value
if [ ${value} == "9" ]
then
exit
else
echo "您输入的数据是: ${value}"
fi
done
/bin/bash signal_trap_test2.sh
请输入一个数据:3
您输入的数据是: 3
请输入一个数据:9
走了.不送
实践3-移除捕获
signal_trap_test3.sh
#!/bin/bash
# 功能:移除脚本信号捕捉
# 捕获关闭信号
trap "echo '走了.不送'" EXIT
i=1
# 检测逻辑效果
while [ $i -le 3 ]
do
read -p "请输入一个数据:" value
if [ ${value} == "9" ]
then
exit
else
echo "您输入的数据是: ${value}"
fi
let i+=1
done
# 移除捕获信号
trap - EXIT
echo "移除了捕获信号"
# 结果显示:在没有走到信号捕获移除的时候,捕获仍然生效
/bin/bash signal_trap_test3.sh
请输入一个数据:9
走了.不送
/bin/bash signal_trap_test3.sh
请输入一个数据:1
您输入的数据是: 1
请输入一个数据:2
您输入的数据是: 2
请输入一个数据:3
您输入的数据是: 3
移除了捕获信号
expect
expect基础
基础知识
场景需求
在日常工作中,经常会遇到各种重复性的"手工交互"操作,虽然没有什么技术含量,但是相当的重要。在实际的工作场景中,这种重复性的手工操作动作,非常的繁多,但是对于量大的工作来说,效率就非常低效了。所以我们就需要有一种工具,能够简化我们重复的手工操作。
expect简介
expect是一个免费的编程工具,由DonLibes制作,作为Tcl脚本语言的一个扩展,它可以根据程序的提示,模拟标准输入提供给程序,从而实现自动的交互式任务,而无需人为干预,可以用作Unix系统中进行应用程序的自动化控制和测试的软件工具。
说白了,expect就是一套用来实现自动交互功能的软件。它主要应用于执行命令和程序时,系统以交互形式要求输入指定字符串,实现交互通信。在使用的过程中,主要是以脚本文件的样式来存在
官方网站:www.nist.gov/services-re… 工具手册:man expect
软件部署
yum -y install expect
expect -v
expect version 5.45
# 进入专用的命令交互界面
expect
expect1.1> pwd
/root
expect1.2> exit
选项 | 说明 |
---|---|
-c | 执行脚本前先执行的命令,该命令应该加引号,以防止被shell打断。此选项可能被多次使用。可以用一个-c执行多个命令,用分号分隔它们。命令按照它们出现的顺序执行。(当使用Expectk时,该选项被指定为-command) |
-d | -d标志启用一些诊断输出,主要报告诸如expect和interaction等命令的内部活动。该标志与Expect脚本开头的"exp_internal 1"具有相同的效果,并且会打印Expect的版本。(strace命令可用于跟踪语句,trace命令可用于跟踪变量赋值)(使用Expectk时,此选项指定为-diag) |
-D | -D标志启用交互式调试器。后面应该是一个整数值。如果该值不为零,或者按下了^C(或命中了断点,或脚本中出现了其他适当的调试器命令),调试器将在下一个Tcl过程之前取得控制权(当使用Expectk时,该选项被指定为-Debug) |
-f | 从文件读取命令,该标志本身是可选的,因为它仅在使用#!符号时才有用,因此可以在命令行上提供其他参数(当使用Expectk时,该选项被指定为-file)。如果字符串"-"作为文件名提供,则读取标准输入。(使用". /-"从实际名为"-"的文件中读取) |
-b | 默认情况下,命令文件被读入内存并完整地执行。有时需要一次一行地读取文件。例如,stdin是这样读取的。为了强制以这种方式处理任意文件,请使用-b标志。(使用Expectk时,此选项指定为-buffer。)注意,stdio缓冲可能仍然会发生,但这应该不会导致从fifo或stdin读取时出现问题 |
-i | -i标志使Expect以交互方式提示用户输入命令,而不是从文件中读取命令。提示通过退出命令或EOF终止。如果既不使用命令文件也不使用-c,则假定使用-i(当使用Expectk时,这个选项被指定为-interactive) |
-- | --可用于分隔选项的末尾(标示选项结束)。如果你希望向脚本传递一个类似选项的参数,而不需要Expect对其进行解释,那么这一点非常有用。这可以有效地放在#!行中,以防止Expect进行任何类似于标志的解释。例如,下面的代码将把原始参数(包括脚本名称)保留在变量argv中,#!/usr/local/bin/expect -- |
-v | 显示expect版本信息(当使用Expectk时,该选项被指定为-version) |
可选参数被构造到一个列表中,并存储在名为argv的变量中。argc被初始化为argv的长度。argv0被定义为脚本的名称(如果未使用脚本,则为二进制)。例如,下面打印出脚本的名称和前三个参数:send_user "$argv0 [lrange $argv 0 2]\n"
简单实践
语法解读
在进行expect脚本编写的时候,我们需要记住 -- expect 用的不是我们普通的shell或者python语法,它使用的是Tcl语法
Tcl 全称是 Tool command Language。它是一个基于字符串的命令语言,基础结构和语法非常简单,易于学习和掌握。Tcl 语言是一个解释性语言,所谓解释性是指不像其他高级语言需要通过编译和连接,他像其他 shell 语言一样,直接对每条语句顺次解释执行
Tcl 数据类型简单。对 Tcl 来说,它要处理的数据只有一种即字符串。Tcl 将变量值以字符串的形式进行存储,不关心它的实际使用类型
输出语法
输出:tcl使用”puts"关键字来作为输出语句 样式:puts <-nonewline> string 属性解析: 如果string中间有特殊字符,可以使用 {} 或者 "" 将其作为一个小组,共同输出 -nonewline 代表输出结果的时候,不输出换行符 put 和 puts 都可以在命令行使用,但是脚本中,最好用puts
expect
expect1.1> puts hello # 输出一个字符串内容
hello
expect1.2> puts "hello world" # 输出包含特殊字符的字符串,不能用单引号
hello world
expect1.3> puts {hello world} # 输出包含特殊字符的字符串
hello world
expect1.4> puts -nonewline "hello world" # 输出内容的时候,不换行
hello worldexpect1.5>
脚本基础
- 文件名后缀,.expect 作为标识符
- 文件首行,要指定命令的执行解释器 ,
#!/usr/bin/expect
- 脚本文件的执行,
expect 脚本名
expect_test.expect
#!/usr/bin/expect
# 设定一个环境变量
set var nihao
# 输出环境变量
puts $var
expect expect_test.expect
nihao
语法实践
基础知识
赋值语法
赋值:tcl 使用set关键字来定义参数,不必指定变量值的类型,因为变量值的类型仅一种(字符串) 样式:set varName [value] 注意:变量名是由 数字、下划线、字符组成,数字不能开头,大小写敏感
expect1.7> set a Hello # 设定一个变量名a
Hello
expect1.8> put $a # 使用$ 符号获取变量名的存储值
Hello
expect1.9> put "$a" # 使用 "" 方式打印变量的值
Hello
expect1.10> put {$a} # {} 有别于"" 的特点在于原字符输出
$a
expect1.11> set b $a # 变量的传递
Hello
expect1.12> puts $b
Hello
替换语法
- 不于理睬
- []方括号完成命令替换,用[]将一条命令括起来,命令执行完成后,返回结果
expect1.13> set b [set a 5] # 相当于 set b $a,传递赋值
5
expect1.14> puts $b
5
expect1.15> set c [expr 5 * 10 ] # expr是执行系统命令,将计算结果交给c
50
expect1.16> puts $c
50
# 注意事项
# 变量的设定
expect1.17> set var value # 设定一个普通变量名
value
expect1.18> puts $var # 获取变量名的值
value
# 不支持嵌套$
expect1.19> set var1 $$value # TCL不支持嵌套的$
can't read "value": no such variable
while executing
"set var1 $$value"
expect1.20> set var1 $$var # 由于$var 已经是变量,所以前面的$就无效了
$value
expect1.21> puts $var1
$value
# 原字符输出
expect1.22> set var2 {$var1} # {} 代表原字符输出
$var1
expect1.23> puts $var2
$var1
脚本实践
内置变量
对于tcl来说,它内部包含了大量的内置变量,可以让我们实现快速的功能操作,常见的内置变量有:
- argc,指命令行参数的个数
- argv,指包含命令行参数的列表
- argv0,是指被解释的文件或由调用脚本的名称的文件名
- env,用于表示是系统环境变量的内容,普通变量我们还是使用$即可
- tcl_version,返回Tcl解释器的版本,注意不是expect的版本号
expect_test1.expect
#!/usr/bin/expect
# 查看当前文件传递的参数数量
puts "当前文件传递的参数数量: $argc"
# 查看当前文件传递的参数
puts "当前文件传递的参数: $argv"
# 查看当前文件名称
puts "当前文件名称: $argv0"
# 获取变量值
puts "当前系统变量PATH的值是: $env(PATH)"
set key value
puts "普通变量 key 的值是: $key"
# 查看版本信息
puts "当前tcl版本信息: $tcl_version"
expect expect_test1.expect
当前文件传递的参数数量: 0
当前文件传递的参数:
当前文件名称: expect_test1.expect
当前系统变量PATH的值是: /data/server/java/bin:/usr/local/sbin:/usr/local/bin:/us r/sbin:/usr/bin:/root/bin
普通变量 key 的值是: value
当前tcl版本信息: 8.5
交互基础
脚本基础
命令解释器
命令解释器
#!/usr/bin/expect
#!/usr/bin/expect -f 从文件中读取自动化命令
#!/usr/bin/expect - 如果文件名为 - ,那么从终端输入中读取
#!/usr/bin/expect -i 交互式输入命令
#!/usr/bin/expect -- 脚本传递的选项参数和expect选项相似的参数给脚本
注意:
#!是对脚本的解释器程序路径,脚本的内容是由解释器解释的
注释信息
# 被注释的信息
常见符号
{ }:
作用1:保留所有字符原有的意思,而不做解释,类似于shell中的单引号
样式:set var {"nihao hehehe"}
作用2:代码块儿,但是两个 {} 边界必须在一起。
正确样式:
if {代码块1 } {
代码块2
}
错误示例:
if {$count < 0}
{
break;
}
注意:
无论什么时候,{}边界符号与其他内容都最好有空格隔开,尤其是边界外的内容
[]:
作用:执行命令,类似shell中的 ``(反引号)或者 $()
样式:set count [expr $count - 1 ]
注意:
在expect 中,没有小括号的概念和应用
常用命令
set 设定环境变量
格式:set 变量名 变量值
样式:set host "192.168.8.12"
spawn 启动新的进程,模拟手工在命令行启动服务
格式:spawn 手工执行命令
样式:spawn ssh python@$host
expect 接收一个新进程的反馈信息,我们根据进程的反馈,再发送对应的交互命令
格式:expect "交互界面用户输入处的关键字"
样式:expect "*password*"
send 接收一个字符串参数,并将该参数发送到新进程。
格式:send "用户输入的信息"
样式:send "$password\r"
interact 退出自动化交互界面,进入用户交互状态,如果需要用户交互的话,这条命令必须在最后一行
格式:interact
样式:interact
其他命令
exit 退出expect脚本
expect eof expect执行内容的结束标识符,退出当前脚本,与interact只能存在一个
puts 输出变量的信息,相当于linux命令中的echo
wait 退出程序后,等待时间,回收僵尸进程
disconnect 断开一个进程连接,但让它在后台继续运行。
exp_continue expect获取期望后,还会有另外的期望,那么我们就把多个期望连续执行(允许expect本身继续执行)
简单实践
实践1-简单的登录交互脚本
login_test.expect
#!/usr/bin/expect
# 1 设定环境变量
set username zhang
# 2 发起远程登录请求
spawn ssh $username@192.168.91.101
# 3 识别用户输入的位置关键字
expect "yes/no"
# 4 发送正确的信息
send "yes\r"
# 5 识别密码关键字,并传递密码信息
# 由于password前面会涉及到一次Enter操作,所以在password匹配前,输入一次 \r
send "\r"
expect "password:"
send "zhangjiabao\r"
# 6 切换回用户交互界面
interact
# 清理历史记录
rm -f .ssh/know_hosts
# 执行脚本内容
expect login_test.expect
spawn ssh zhang@192.168.91.101
The authenticity of host '192.168.91.101 (192.168.91.101)' can't be established.
ECDSA key fingerprint is SHA256:fFK1C9qzpNWRtBAq1dZHIEGT1fMtDD3gAQ2gWAslYTw.
ECDSA key fingerprint is MD5:ed:76:a9:08:3f:0a:a5:9a:3a:ea:f3:81:26:25:95:9d.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.91.101' (ECDSA) to the list of known hosts.
zhang@192.168.91.101's password:
Last login: Wed Dec 6 14:41:27 2023 from localhost
实践2-脚本结合
expect 除了使用专用的expect脚本来实现特定功能之外,它还可以与其他脚本嵌套在一起进行使用。最常用的结合方式就是 shell结合。
在与shell结合使用的时候,无非就是将expect的执行命令使用 <<-EOF ... EOF 包装在一起即可。
样式:
/usr/bin/expect<<-EOF
spawn ...
...
expect eof
EOF
注意:
由于expect在shell中是作为一个子部分而存在的,所以,一般情况下,expect结束的时候,使用eof命令表示expect的内容到此结束
expect_auto_login.sh
#!/bin/bash
# 功能:shell自动登录测试
# 版本:v0.1
# 作者:example
# 联系:www.example.com
# 定制普通变量
host="$1"
username="$2"
password="$3"
/usr/bin/expect <<-EOF
# 发出连接进程
spawn ssh ${username}@${host}
# - 正常登陆
expect {
"yes/no*" { send "yes\n"; exp_continue }
"password:" { send "${password}\n"; }
}
puts "测试完毕!!!"
expect eof
EOF
/bin/bash expect_auto_login.sh 192.168.91.101 zhang zhangjiabao
spawn ssh zhang@192.168.91.101
zhang@192.168.91.101's password: 测试完毕!!!
Last login: Thu Dec 14 08:40:47 2023 from test
综合案例
基础知识
当系统配置完毕后,我们可以采用fdisk命令对额外的磁盘进行磁盘分区。而expect可以实现这个效果
环境准备
关闭虚拟机,并添加磁盘,步骤如下:
编辑虚拟机设置->添加->硬盘->下一步->下一步->下一步->下一步->完成->确定
# 添加完磁盘后,开启虚拟机查看,/dev/sdb就是新添加的磁盘
ls /dev/sd*
/dev/sda /dev/sda1 /dev/sda2 /dev/sdb
# 手工演示
fdisk /dev/sdb
Welcome to fdisk (util-linux 2.23.2).
Changes will remain in memory only, until you decide to write them. # 更改将停留在内存中,直到您决定将更改写入磁盘
Be careful before using the write command. # 使用写入命令前请三思
Device does not contain a recognized partition table # 设备不包含可识别的分区表
Building a new DOS disklabel with disk identifier 0x7fd3f70c. # 使用磁盘标识符 0x7fd3f70c 创建新的 DOS 磁盘标签
Command (m for help): n
Partition type:
p primary (0 primary, 0 extended, 4 free)
e extended
Select (default p): p
Partition number (1-4, default 1): # 分区号 (1-4,默认 1)
First sector (2048-41943039, default 2048): # 起始扇区(2048-41943039,默认为 2048)
Using default value 2048 # 将使用默认值 2048
Last sector, +sectors or +size{K,M,G} (2048-41943039, default 41943039):
Using default value 41943039 # 将使用默认值 41943039
Partition 1 of type Linux and of size 20 GiB is set # 分区 1 已设置为 Linux 类型,大小设为 20 GiB
Command (m for help): wq
The partition table has been altered! # 分区表已更改
Calling ioctl() to re-read partition table. # 调用ioctl()重新读取分区表
Syncing disks. # 同步磁盘
mkfs -t ext4 /dev/sdb
mke2fs 1.42.9 (28-Dec-2013)
/dev/sdb is entire device, not just one partition!
Proceed anyway? (y,n) y # 无论如何也要继续
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
1310720 inodes, 5242880 blocks
262144 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=2153775104
160 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000
Allocating group tables: done
Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done
mkdir /haha
mount /dev/sdb /haha
echo nihao > /haha/h.txt
ls /haha
h.txt lost+found
# 卸载
umount /haha
rm -rf /haha
脚本实践
expect_auto_partition.sh
#!/bin/bash
# 功能:shell自动磁盘分区格式化测试
# 版本:v0.1
# 作者:example
# 联系:www.example.com
# 定制普通变量
mount_dir='/disk_check'
# 检测基本环境
[ -f /usr/bin/expect ] && echo "expect 环境正常" || ("expect 环境异常" && exit)
# 查看磁盘列表
fdisk -l | grep "Disk /dev/s"
# 定制磁盘分区操作
read -p "请输入需要挂载得硬盘路径: " disk_name
# expect自动分区操作
/usr/bin/expect << EOF
set timeout 30
# spawn fdisk ${disk_name}
spawn bash -c "fdisk ${disk_name}"
expect "Command*" {send "n\r"}
expect "Select*" {send "p\r"}
expect "Partition*" {send "\r"}
expect "First sector*" {send "\r"}
expect "Last sector*" {send "\r"}
expect "Command*" {send "wq\r"}
expect eof
EOF
# expect自动格式化操作
read -p "请输入硬盘的类型: " disk_type
/usr/bin/expect << EOF
set timeout 30
# spawn mkfs -t ${disk_type} ${disk_name}
spawn bash -c "mkfs -t ${disk_type} ${disk_name}"
expect "*y,n*" {send "y\r"}
expect eof
EOF
# 磁盘挂载测试
[ -d ${mount_dir} ] && rm -rf ${mount_dir}
mkdir ${mount_dir}
mount ${disk_name} ${mount_dir}
echo disk_check > ${mount_dir}/check.txt
[ -f ${mount_dir}/check.txt ] && echo "${mount_dir} 挂载成功" || (echo "${mount_dir} 挂载失败" && exit)
umount ${mount_dir}
[ ! -f ${mount_dir}/check.txt ] && echo "${mount_dir} 卸载成功" || (echo "${mount_dir} 卸载失败" && exit)
rm -rf ${mount_dir}
/bin/bash expect_auto_partition.sh
expect 环境正常
Disk /dev/sdb: 21.5 GB, 21474836480 bytes, 41943040 sectors
Disk /dev/sda: 42.9 GB, 42949672960 bytes, 83886080 sectors
请输入需要挂载得硬盘路径: /dev/sdb
spawn bash -c fdisk /dev/sdb
Welcome to fdisk (util-linux 2.23.2).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Device does not contain a recognized partition table
Building a new DOS disklabel with disk identifier 0x38f5949d.
Command (m for help): n
Partition type:
p primary (0 primary, 0 extended, 4 free)
e extended
Select (default p): p
Partition number (1-4, default 1):
First sector (2048-41943039, default 2048):
Using default value 2048
Last sector, +sectors or +size{K,M,G} (2048-41943039, default 41943039):
Using default value 41943039
Partition 1 of type Linux and of size 20 GiB is set
Command (m for help): wq
The partition table has been altered!
Calling ioctl() to re-read partition table.
Syncing disks.
请输入硬盘的类型: ext4
spawn bash -c mkfs -t ext4 /dev/sdb
mke2fs 1.42.9 (28-Dec-2013)
/dev/sdb is entire device, not just one partition!
Proceed anyway? (y,n) y
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
1310720 inodes, 5242880 blocks
262144 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=2153775104
160 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000
Allocating group tables: done
Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done
/disk_check 挂载成功
/disk_check 卸载成功
创建用户实践
案例需求:借助于expect实现在特定的主机上批量创建用户
expect_auto_register.sh
#!/bin/bash
# 功能:shell自动远程主机创建用户测试
# 版本:v0.1
# 作者:example
# 联系:www.example.com
# 定制初始变量
login_user='root'
login_pass='zhangjiabao'
host_file='ip.txt'
new_user='test-1'
new_pass='123456'
# 批量创建用户
cat ${host_file} | while read ip
do
expect <<-EOF
set timeout 30
spawn ssh $login_user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "${login_pass}\n" }
}
expect "]#" { send "useradd ${new_user}\n" }
expect "]#" { send "echo ${new_pass} | passwd --stdin ${new_user}\n" }
expect "]#" { send "who\n" }
expect "]#" { send "exit\n" }
expect eof
EOF
done
echo "192.168.91.101" > ip.txt
/bin/bash expect_auto_register.sh
spawn ssh root@192.168.91.101
root@192.168.91.101's password:
Last login: Thu Dec 14 13:42:51 2023 from 192.168.91.1
[root@test ~]# useradd test-1
[root@test ~]# echo 123456 | passwd --stdin test-1
Changing password for user test-1.
passwd: all authentication tokens updated successfully.
[root@test ~]# who
root pts/0 2023-12-14 10:05 (192.168.91.1)
root pts/1 2023-12-14 13:42 (192.168.91.1)
root pts/3 2023-12-14 13:57 (test)
[root@test ~]# exit
logout
Connection to 192.168.91.101 closed.
# 校验用户创建
id test-1
uid=1001(test-1) gid=1001(test-1) groups=1001(test-1)
# 删除创建的用户
userdel -r test-1