2. Linux终端录屏和回放

cript命令简介 当你在终端或控制台上工作时,你可能想记录下自己做了些什么。这种记录可以看成是保存了终端痕迹的文档。假设你跟一些Linux管理员同时在系统上干活。或者说你让别人远程到你的服务器。你就会想记录下终端发生过什么。要实现它,你可以使用script命令。

2.1 什么script命令

script 是一个神奇命令,可以使用script工具记录用户在当前终端的所有的操作,已经输出到屏幕的内容。将这些信息保存到指定的文本文件中。 
也就是说,script命令在你需要记录或者存档终端活动时可能很有用,记录文件会存储为文本文件,所以可以很方便地用文本编辑器打开。 
在使用script命令将终端的会话过程录制下来之后,可以使用 scriptreplay将其录制的结果播放给他人观看。 
script 的好处就在于你在终端中的所有操作、敲过的命令和打印出的结果它都可以原原本本地进行录制。可以应用于教学、演示、审计。

2.2 script命令操作

使用 script --help 命令来查看 script命令的用法

leco@leco:~$ script --help
Usage:
 script [options] [file]
Make a typescript of a terminal session.
选项:
 -a, --append            append the output
 -c, --command <command> run command rather than interactive shell
 -e, --return            return exit code of the child process
 -f, --flush             run flush after each write
     --force             use output file even when it is a link
 -q, --quiet             be quiet
 -t, --timing[=<file>]   output timing data to stderr (or to FILE)
 -V, --version           output version information and exit
 -h, --help              display this help and exit
For more details see script(1).

解释
-a选项  在现有输出录制的文件的内容上追加新的内容
-c选项  后面可以加上需要执行的命令,而不是交互式shell上执行的命令
-r选项  子进程中返回退出代码
-f选项  如果需要在输出到日志文件的同时,也可以查看日志文件的内容,可以使用 -f 参数。PS:可以用于教学,两个命令行接-f可以实时演示
-q选项  可以使script命令以静默模式运行
-t选项  指明输出录制的时间数据
-V选项  输出script的版本信息,然后退出
-h选项  输出script的help信息,然后退出
  • [file] 选项

    • 1、当file为空时,操作内容将记录到当前目录中名称为typescript的文本文件中。

      root@leco:~/summer# ls      # 之前是没有文件的
      root@leco:~/summer# script  # 启动,默认保存是typescript文件
      脚本已启动,文件为 typescript
      root@leco:~/summer# exit    #退出
      exit
      脚本完成,文件为  typescript
      root@leco:~/summer# ls
      typescript
      查看内容
      root@leco:~/summer# cat typescript
      脚本启动于 2019年01月23日 星期三 09时56分56秒
      root@leco:~/summer# exit
      exit
      
      脚本完成,于 2019年01月23日 星期三 09时57分03秒
      

    • 2、如果指定file,那么将把终端的操作内容记录到file文件中。

      root@leco:~/summer# rm typescript           # 删除之前做实验的默认输出文件
      root@leco:~/summer# ls
      root@leco:~/summer# script summerout.log    # 指定输出文件
      脚本已启动,文件为 summerout.log
      root@leco:~/summer# ls                      # 开始操作
      summerout.log
      root@leco:~/summer# echo 'my love from summer' 
      my love from summer
      root@leco:~/summer# exit                   # 退出
      exit
      脚本完成,文件为  summerout.log
      root@leco:~/summer# ls                      # 显示记录的指定文件内容
      summerout.log
      root@leco:~/summer# cat summerout.log       # 查看记录的内容,以下是记保存了  操作历史
      脚本启动于 2019年01月23日 星期三 09时59分02秒
      root@leco:~/summer# ls
      summerout.log
      root@leco:~/summer# echo 'my love from summer'
      my love from summer
      root@leco:~/summer# exit
      exit
      
      脚本完成,于 2019年01月23日 星期三 09时59分25秒
      


  • 查看版本
    root@leco:~/summer# script -V
    script,来自 util-linux 2.27.1
    

  • script -q 选项 静默输出
#1. 不带-q的操作 
root@leco:~/summer# ls
root@leco:~/summer# script
脚本已启动,文件为 typescript  # 此时会提示这个。带-q就不输出
root@leco:~/summer# ls
typescript
root@leco:~/summer# echo 'hello'
hello
root@leco:~/summer# exit
exit
脚本完成,文件为  typescript

#2. 带-q的操作
root@leco:~/summer# ls
typescript
root@leco:~/summer# rm typescript
root@leco:~/summer# ls
root@leco:~/summer# script -q
root@leco:~/summer# echo 'hello'
hello
root@leco:~/summer# exit
exit
root@leco:~/summer# ls
typescript

  • script -a 选项

    -a选项 在现有输出录制的文件的内容上追加新的内容

root@leco:~/summer# ls
typescript
root@leco:~/summer# rm typescript    # 删除之前操作历史
root@leco:~/summer# ls
root@leco:~/summer# script           # 开始记录
脚本已启动,文件为 typescript 
root@leco:~/summer# echo '123'       
123
root@leco:~/summer# echo '345'
345
root@leco:~/summer# exit             # 退出
exit
脚本完成,文件为  typescript
root@leco:~/summer# ls
typescript
root@leco:~/summer# cat typescript   # 查看操作历史
脚本启动于 2019年01月23日 星期三 10时07分56秒
root@leco:~/summer# echo '123'
123
root@leco:~/summer# echo '345'
345
root@leco:~/summer# exit
exit

脚本完成,于 2019年01月23日 星期三 10时08分08秒

# 以上都是第一次操作的历史‘


root@leco:~/summer# script -a       # 开始第二次,也就是-a 就是追加内容到第一次操作文件中
脚本已启动,文件为 typescript
root@leco:~/summer# echo '第二次操作123'
第二次操作123
root@leco:~/summer# echo '第二次操作456'
第二次操作456
root@leco:~/summer# exit
exit
脚本完成,文件为  typescript
root@leco:~/summer# ls
typescript
root@leco:~/summer# cat typescript
脚本启动于 2019年01月23日 星期三 10时07分56秒
root@leco:~/summer# echo '123'
123
root@leco:~/summer# echo '345'
345
root@leco:~/summer# exit
exit

脚本完成,于 2019年01月23日 星期三 10时08分08秒
脚本启动于 2019年01月23日 星期三 10时08分20秒
root@leco:~/summer# echo '第二次操作123'
第二次操作123
root@leco:~/summer# echo '第二次操作456'
第二次操作456
root@leco:~/summer# exit
exit

脚本完成,于 2019年01月23日 星期三 10时08分35秒

  • script -t 选项

    -t选项 指明输出录制的时间数据

root@leco:~/summer# ls
root@leco:~/summer# script -t 2>datas.time -aq commands.his
root@leco:~/summer# echo '123'
123
root@leco:~/summer# echo '456'
456
root@leco:~/summer# exit
exit
root@leco:~/summer# ls
commands.his  datas.time
root@leco:~/summer# cat commands.his
脚本启动于 2019年01月23日 星期三 10时16分38秒
root@leco:~/summer# echo '123'
123
root@leco:~/summer# echo '456'
456
root@leco:~/summer# exit
exit
root@leco:~/summer# cat datas.time
0.016874 44
1.117511 4
0.213036 12
0.189993 8
0.639560 2
0.000082 5
0.000225 44
0.316049 10
0.155868 16
0.388290 12
0.719740 2
0.000032 5
0.000301 44
1.598040 1
0.222087 1
0.368449 1
0.256182 1
0.269758 2
0.000071 6
root@leco:~/summer#
解释

选项-t用于存储时序文件,这里导入到stderr,再重定向到datas.time, 
选项-a用于将命令输出信息,追加到commands.his文件。

这样录制视屏就很方便啦,而且这两个文件很小,可以拷贝到需要播放的机器上进行播放。
以上是录制,下面开始回放之前操作

# 回放命令,接下来后面就像放电影一样,重复你之前操作,自己操作一波,体会效果
root@leco:~/summer# scriptreplay datas.time  commands.his  
root@leco:~/summer# echo '123'
123
root@leco:~/summer# echo '456'
456
root@leco:~/summer# exit
exit
注意:

scriptreplay 后面数据文件,后命令文件


  • script -f 选项

    -f选项 如果需要在输出到日志文件的同时,也可以查看日志文件的内容,可以使用 -f 参数。PS:可以用于教学,两个命令行接-f可以实时演示

root@leco:~/summer# ls
root@leco:~/summer# script -t 2>datas.time -a commands.his
脚本已启动,文件为 commands.his
root@leco:~/summer# echo '开始教学了'
开始教学了
root@leco:~/summer# script -f
脚本已启动,文件为 typescript
root@leco:~/summer# echo '你在认真看吗'
你在认真看吗
使用script -f 实时刷新数据,记录文件分别为commands.his和datas.time


  • script -c 选项

    -c选项 后面可以加上需要执行的命令,而不是交互式shell上执行的命令

root@leco:~/summer# script -a "commands.his" -c "ifconfig|grep 192.168.5.110"
脚本已启动,文件为 commands.his
          inet 地址:192.168.5.110  广播:192.168.5.255  掩码:255.255.255.0
脚本完成,文件为  commands.his
root@leco:~/summer# exit
exit
root@leco:~/summer# ls
commands.his
root@leco:~/summer# cat commands.his
脚本启动于 2019年01月23日 星期三 10时34分58秒
          inet 地址:192.168.5.110  广播:192.168.5.255  掩码:255.255.255.0

脚本完成,于 2019年01月23日 星期三 10时34分58秒

2.3 退出script

要退出记录活动,我们可以在终端中按下Ctrl+D,或者输入exit。在退出script前,你会发现记录文件的大小为0 Kb,而在退出之后,文件大小会发生改变。

记录历史的文件在执行过程中都是0字节,退出的时候才写入该文件。

root@leco:~/summer# script exit.his
脚本已启动,文件为 exit.his
root@leco:~/summer# ls
exit.his
root@leco:~/summer# echo '123'
123
root@leco:~/summer# du -sh exit.his
0   exit.his
root@leco:~/summer# echo '12312'
12312
root@leco:~/summer# du -sh exit.his
0   exit.his
root@leco:~/summer# exit
exit
脚本完成,文件为  exit.his
root@leco:~/summer# du -sh exit.his
4.0K    exit.his
root@leco:~/summer# cat exit.his
脚本启动于 2019年01月23日 星期三 10时37分44秒
root@leco:~/summer# ls
exit.his
root@leco:~/summer# echo '123'
123
root@leco:~/summer# du -sh exit.his
0   exit.his
root@leco:~/summer# echo '12312'
12312
root@leco:~/summer# du -sh exit.his
0   exit.his
root@leco:~/summer# exit
exit

脚本完成,于 2019年01月23日 星期三 10时38分05秒

三、Script命令结合实际使用场景

3.1 案例1 定时备份

先在终端执行script命令记录scp命令过程,先在终端敲个命令

script datas.his -c 'sh for.sh'
我是记录我脚本的执行过程的记录。

root@leco:~/summer# ls
for.sh
root@leco:~/summer# cat for.sh
for((i=0;i<10;i++))
do
    echo $i
done
root@leco:~/summer# script datas.his -c 'sh for.sh'
脚本已启动,文件为 datas.his
0
1
2
3
4
5
6
7
8
9
脚本完成,文件为  datas.his
root@leco:~/summer# ls
datas.his  for.sh
root@leco:~/summer# cat datas.his  # 查看历史记录
脚本启动于 2019年01月23日 星期三 10时41分52秒
0
1
2
3
4
5
6
7
8
9

脚本完成,于 2019年01月23日 星期三 10时41分52秒

script记录整个过程结束。 从结果来看,整个过程使用script命令记录是没有问题的

看下以下实例 auto_backup_to_remote.sh

#!/bin/bash

# gitlab 机房备份路径
LocalBackDir=/data/gitlabData/backups

# 远程备份服务器(FTP服务器) gitlab备份文件存放路径
RemoteBackDir=/data/gitlabDataBackup

# 远程备份服务器 登录账户
RemoteUser=root

# 远程机房代码备份服务器IP地址
RemoteIP1=远程服务器IP地址

#当前系统日期
DATE=`date +"%Y-%m-%d"`

#Log存放路径
LogFile=$LocalBackDir/log/$DATE.log

#邮件写入的文件
mailcontent=$LocalBackDir/mail/mailcontent_$DATE

mailToUser=ouyangpeng的邮箱
mailToUser1=领导1的邮箱
mailToUser2=领导2的邮箱

#新建日志文件
touch $LogFile

#追加日志到日志文件
echo "Gitlab auto backup to remote server, start at  $(date +"%Y-%m-%d %H:%M:%S")" >  $LogFile
echo "---------------------------------------------------------------------------" >> $LogFile

# 查找 本地备份目录下 时间为180分钟之内的,并且后缀为.tar的gitlab备份文件
BACKUPFILE_SEND_TO_REMOTE=$(find /data/gitlabData/backups -type f -mmin -180  -name '*.tar*')

# 输出日志,打印出每次scp的文件名
echo "---------------------The file to scp to remote server is: $BACKUPFILE_SEND_TO_REMOTE-------------------------------" >> $LogFile

#备份到 远程机房代码备份服务器
scp -v $BACKUPFILE_SEND_TO_REMOTE $RemoteUser@$RemoteIP1:$RemoteBackDir

# $?符号显示上一条命令的返回值,如果为0则代表执行成功,其他表示失败
if [ $? -eq 0 ];then
   #追加日志到日志文件
   echo "-----------------------------------Success!----------------------------------------" >> $LogFile
   echo "Gitlab auto backup to remote server, end at  $(date +"%Y-%m-%d %H:%M:%S")" >>  $LogFile

   #写Email的正文内容
   > "$mailcontent"
   echo "GitLab Backup Daily Report, backup to remote server Success ! Please Check your Email and read the following log file" >> $mailcontent

   #读取mailcontent内容当做邮件正文 ,附件为Log文件
   #cat $mailcontent | mail -s "Congratulation! GitLab backup to remote server Success Report."  -t $mailToUser $mailToUser1 $mailToUser2 -A $LogFile

   #成功的话,只发送给我一个人即可,不需要发送给其他人
   cat $mailcontent | mail -s "Congratulation! GitLab backup to remote server Success Report." $mailToUser -A $LogFile
else
   #追加日志到日志文件
   echo "-----------------------------------Failed!---------------------------------------" >> $LogFile
   echo "Gitlab auto backup to remote server failed at  $(date +"%Y-%m-%d %H:%M:%S")" >>  $LogFile

   #写Email的正文内容
   > "$mailcontent"
   echo "GitLab Backup Daily Report,Backup to remote server Failed !  Please Check your Email and read the following log file !" >> $mailcontent

   #读取mailcontent内容当做邮件正文 ,附件为Log文件
   cat $mailcontent | mail -s "Warning! GitLab Backup to remote server Failed Report."  -t $mailToUser $mailToUser1 $mailToUser2 -A $LogFile
fi
auto_backup_to_remote_script.sh*文件源代码是

#!/bin/bash

# gitlab 机房备份路径
LocalBackDir=/data/gitlabData/backups

#当前系统日期
DATE=`date +"%Y-%m-%d"`

#Log存放路径
LogFile=$LocalBackDir/log/$DATE.log

#Script命令记录的存放路径和文件名
ScriptLogFile=$LocalBackDir/script_log/$DATE.log
ScriptTimeFile=$LocalBackDir/script_log/$DATE.date

#新建日志文件
touch $ScriptLogFile
touch $ScriptTimeFile

#开始启动script记录整个过程,执行 /data/gitlabData/backups/auto_backup_to_remote.sh 脚本
script -a $ScriptLogFile -c"/data/gitlabData/backups/auto_backup_to_remote.sh"
这样就可以了,然后将原来的定时任务执行的脚本文件修改下即可。 crontab -e

# edited by summer 2019-01-23 添加定时任务,每天凌晨1点,执行gitlab备份到远程服务器
0 1 * * *   /bin/bash   /scripts/gitlab/auto_backup_to_remote_script.sh 

3.2 案例2 服务器安全审计

直接操作线上的服务器有很大隐患,所以一般都是通过登录跳板机,然后连接线上服务器,跳板机可以访问控制和安全审计,查看记录每个人对线上服务器的操作 用户家目录下,修改环境变量,使得用户登录就会触发录像

root@leco:/datas/commands# tail -1 /etc/profile
script -t -f -q 2>/datas/commands/$USER-$UID-`date +%Y%m%d%H%M%S`.time -a /datas/commands/$USER-$UID-`date +%Y%m%d%H%M%S`.his
root@leco:/datas/commands# chmod 777 /datas/commands/
root@leco:/datas/commands# ls
root@leco:/datas/commands#
此时还没有日志,接下来,重新登录用户
测试登录

新开一个终端

*** 需要重启系统 ***
Last login: Wed Jan 23 11:03:09 2019 from 192.168.5.1
leco@leco:~$ ip a|grep 192.168.5.
    inet 192.168.5.110/24 brd 192.168.5.255 scope global enp3s0
leco@leco:~$ echo 'hello'
hello
在看保存的文件

leco@leco:~$ root@leco:/datas/commands# ls
leco-1000-20190123110350.his  leco-1000-20190123110350.time
# 1. 单独查看 命令历史
root@leco:/datas/commands# cat leco-1000-20190123110350.his
脚本启动于 2019年01月23日 星期三 11时03分50秒
leco@leco:~$ ip a|grep 192.168.5.
    inet 192.168.5.110/24 brd 192.168.5.255 scope global enp3s0
leco@leco:~$ echo 'hello'
hello

# 查看动态历史
root@leco:/datas/commands# scriptreplay leco-1000-20190123110350.time leco-1000-20190123110350.his
leco@leco:~$ ip a|grep 192.168.5.
    inet 192.168.5.110/24 brd 192.168.5.255 scope global enp3s0
leco@leco:~$ echo 'hello'
hello
leco@leco:~$
自己操作,查看效果

这样搞完后,发现有一个问题是每次退出,我习惯Ctrl+D,然后按第一遍停止录像,第二遍才能用户退出,如何解决在后面添加

if [ "$SHLVL" = 1 ]; then
            exit
fi
完整版

root@leco:/datas/commands# tail -4 /etc/profile
script -t -f -q 2>/datas/commands/$USER-$UID-`date +%Y%m%d%H%M%S`.time -a /datas/commands/$USER-$UID-`date +%Y%m%d%H%M%S`.his
if [ "$SHLVL" = 1 ]; then
    exit
fi
这样就可以一遍Ctrl+D停止录像和退出用户。

区别

SHLVL 这个变量是干什么的? shell中的$SHLVL,记录当前shell的嵌套层次,初始为1 比如下面的例子:当前是在第1层,执行bash命令之后,$SHLVL变量变为2

root@leco:/datas/commands# echo $SHLVL
1
root@leco:/datas/commands# bash
root@leco:/datas/commands# echo $SHLVL
2
root@leco:/datas/commands# exit
exit
root@leco:/datas/commands# echo $SHLVL
1
不知道的话,去百度

SHLVL 和 BASH_SUBSHELL 两个变量的区别
SHLVL 和 BASH_SUBSHELL 两个变量的区别参考链接