# 打包指令:tar

前面讲解的 gzip、bzip2、xz 也能够针对目录进行压缩,但是是将目录内所有文件 分别 压缩的。而在 windows 下可以使用 winRAR 之类的压缩文件,将好多数据包成一个文件的样式。

这种将多个文件或目录包成一个大文件的指令功能,就可以称呼为 打包指令,tar 就是这样一个功能的打包指令,同时还可以通过压缩指令将该文件进行压缩。windows 中的 WinRAR 也支持 .tar.gz 的解压缩

tar 的选项与参数非常多,这里只接受几个常用的选项

打包与压缩:`tar [-z|-j|-J][cv][-f 待建立的文件名] filename`
观察文件:  `tar [-z|-j|-J][tv][-f file.tar]`
解压缩:	  `tar [-z|-j|-J][xf][-f file.tar] [-C 目录]`

特别注意:`[-z|-j|-J]` 不可同时出现在一串指令中
特殊注意:c、t、x 也不可同时出现在一串指令中
1
2
3
4
5
6

选项与参数

  • c:建立打包文件,可搭配 -v来观察过程中被打包的文件名
  • t:查看打包文件的内容含有哪些文件,重点在查看文件名
  • x:接打包或解压缩的功能,可搭配 -C 在特定目录解开,特别注意 c、t、x 不能同时出现在一起
  • z:通过 gzip 的支持进行压缩、解压缩;此时文件名最好为 *.tar.gz
  • j:通过 bzip2 的支持进行压缩、解压缩;此时文件名最好为 *.tar.bz2
  • J:通过 xz 的支持进行压缩、解压缩;此时文件名最好为 *.tar.xz
  • v:在压缩、解压缩的过程中,将正在处理的文件名显示出来
  • f:后面要立刻接要被处理的文件名,建议 -f 单独写一个选项(不容易忘记)
  • C:在指定目录解压缩
  • p:保留备份数据的原本权限与属性,常用语备份(-c)重要的配置文件
  • P:保留绝对路径,保留 root 跟路径
  • --exclude=FILE:在压缩过程中,排除指定的文件,不打包

最常用的是以下命令:

  • 压 缩:tar -jcv -f filename.tar.bz2 要被压缩的文件或目录
  • 查 询:tar -jtv -f filename.tar.bz2
  • 解压缩:tar -jxv -f filename.tar.bz2 -C 指定目录解开

小提示:上面 -jcvf 可以写一起,但是阅读起来就没有上面这样分开好理解

# 使用 tar 加入 -z、-j 或 -J 的参数备份 /etc/ 目录

# 备份 /etc/ 需要 root 权限,否则会出现一堆错误
[mrcode@study ~]$ su -
Password:
Last login: Sun Oct 27 20:38:34 CST 2019 on pts/0

[root@study ~]# time tar -zpcv -f /root/etc.tar.gz /etc
tar: 从成员名中删除开头的“/”		# 注意这里的警告
/etc/
/etc/fstab
/etc/crypttab
...
real    0m2.329s		# 耗时 2.329 秒
user    0m1.322s
sys     0m0.308s

# -p 重点是保留文件的权限与属性
# 下面去掉了 -v,所以不会显示处理的文件名
[root@study ~]# time tar -jpc -f /root/etc.tar.bz2 /etc
tar: 从成员名中删除开头的“/”

real    0m3.012s
user    0m2.710s
sys     0m0.078s

[root@study ~]# time tar -Jpc -f /root/etc.tar.xz /etc
tar: 从成员名中删除开头的“/”

real    0m14.836s
user    0m13.511s
sys     0m0.224s

[root@study ~]# ll -h /root/etc*
-rw-r--r--. 1 root root  11M 1029 00:05 /root/etc.tar.bz2
-rw-r--r--. 1 root root  12M 1029 00:01 /root/etc.tar.gz
-rw-r--r--. 1 root root 8.2M 1029 00:06 /root/etc.tar.xz

# etc 占用 42M
[root@study ~]# du -sh /etc/
42M     /etc/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

前面讲解 cp 指令复制的时候也涉及到复制后的文件权限与属性问题,这里的 -p 选项也是这样

# 查阅 tar 文件的数据内容(可查看文件名)与备份文件名是否有根目录的意义

# -v 把权限属性也列出来了
# 这里查看文件名前面无根路径的
[root@study ~]# tar -jtv -f /root/etc.tar.bz2
drwxr-xr-x root/root         0 2019-10-04 18:38 etc/
-rw-r--r-- root/root       808 2019-10-27 22:43 etc/fstab
-rw------- root/root         0 2019-10-04 18:20 etc/crypttab
lrwxrwxrwx root/root         0 2019-10-04 18:20 etc/mtab -> /proc/self/mounts
-rw-r--r-- root/root        51 2019-10-04 18:20 etc/resolv.conf
1
2
3
4
5
6
7
8

为什么需要拿到根目录呢?主要是为了安全,使用 tar 备份的数据可能会需要解压缩回来使用,在 tar 所记录的文件名(上面 -jtv 显示的文件名)就是解压缩后的实际文件名。如果拿到了根目录,则会在当前目录解压。比如现在在 /tmp ,解压后就变成 /tmp/etc/xxx;如果不拿掉根目录,源文件就被覆盖了

[root@study ~]# tar -jPc -f /root/etc.and.root.tar.bz2 /etc
[root@study ~]# tar -jtv -f /root/etc.and.root.tar.bz2
[root@study ~]# tar -jtv -f /root/etc.and.root.tar.bz2
tar: 从成员名中删除开头的“/”
drwxr-xr-x root/root         0 2019-10-04 18:38 /etc/
-rw-r--r-- root/root       808 2019-10-27 22:43 /etc/fstab
-rw------- root/root         0 2019-10-04 18:20 /etc/crypttab
lrwxrwxrwx root/root         0 2019-10-04 18:20 /etc/mtab -> /proc/self/mounts
# 对比下,确实是带上了根路径
1
2
3
4
5
6
7
8
9

# 将备份的数据解压缩,并考虑指定目录压缩(-C 选项的应用)

[root@study ~]# pwd
/root
[root@study ~]# tar -jx -f etc.tar.bz2
[root@study ~]# ll -d etc*
drwxr-xr-x. 143 root root     8192 104 18:38 etc
-rw-r--r--.   1 root root 10520237 1029 00:15 etc.and.root.tar.bz2
-rw-r--r--.   1 root root 10518433 1029 00:05 etc.tar.bz2
-rw-r--r--.   1 root root 12212046 1029 00:01 etc.tar.gz
-rw-r--r--.   1 root root  8580036 1029 00:06 etc.tar.xz

# 解压到指定目录
tar -zx -f etc.tar.gz -C /tmp
# 记得删除解压后的文件
rm -rf /tmp/etc/ /root/etc
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 仅解开单一文件

前面讲解的都是解开该压缩包中的所有文件。

# 利用 -t 查看文件名,接管道查找 shadow
[root@study ~]# tar -jtv -f /root/etc.tar.bz2 | grep 'shadow'
---------- root/root      1271 2019-10-04 18:31 etc/shadow-
---------- root/root       797 2019-10-04 18:31 etc/gshadow
---------- root/root      1266 2019-10-04 18:31 etc/shadow 		# 假设要提取出这个文件
---------- root/root       791 2019-10-04 18:31 etc/gshadow-

# 后面接需要提取出来的文件路径
[root@study ~]# tar -jxv -f /root/etc.tar.bz2 etc/shadow
etc/shadow
[root@study ~]# ll etc
总用量 4
----------. 1 root root 1266 104 18:31 shadow

1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 打包某目录,但不包含该目录下的某些文件

[root@study ~]# tar -jc -f /root/system.tar.bz2 --exclude=/root/etc* --exclude=/root/system.tar.bz2 /etc /root
tar: 从成员名中删除开头的“/”
tar: 从硬连接目标中删除开头的“/”

1
2
3
4

# 仅备份比某个时刻还要新的文件

# 先找出比 /etc/passwd 还要新的文件
# 前面 touch 中介绍过 --newer 和 --newer-mtime
# newer 包含 mtime 和 ctime,而 --newer-mtime 只包含 mtime
[root@study ~]# find /etc -newer /etc/passwd
/etc
/etc/fstab
/etc/group
/etc/gshadow
...

[root@study ~]# ls --full-time  /etc/passwd
-rw-r--r--. 1 root root 2323 2019-10-04 18:31:08.332738182 +0800 /etc/passwd

[root@study ~]# tar -jcv -f /root/etc.newer.the.passwd.tar.bz2 --newer-mtime="2019-10-04" /etc/*
tar: 选项 --newer-mtime: 将日期 ‘2019-10-04’ 当作 2019-10-04 00:00:00
tar: 从成员名中删除开头的“/”
/etc/abrt/
tar: /etc/abrt/abrt-action-save-package-data.conf: 文件未改变;未输出
tar: /etc/abrt/abrt.conf: 文件未改变;未输出
tar: /etc/abrt/gpg_keys.conf: 文件未改变;未输出

# 验证下是否被打进去了,这里搜索都搜不到,确实没有被打进去
[root@study ~]# tar -jtv -f etc.newer.the.passwd.tar.bz2 | grep 'etc/abrt/abrt.conf'
[root@study ~]# tar -jtv -f etc.newer.the.passwd.tar.bz2 | grep 'abrt.conf'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

可以进行差异文件的记录与备份,例如:在一个月前进行过一次完整的数据备份,那么这个月备份至需要备份更改过的文件即可,直接写到上次备份的时间点即可

# 基本名称:tarfile、tarball?

tar 可以只打包不压缩 tar -c -f file.tar,这种文件称为 tarfile,如果有压缩就称为 tarball。

此外 tar 还可以将文件打包到特别的装置中去,例如,tar -c -f /dev/st0/home /root/etc ,把 etc 打包到磁带机去(磁带机是一次性读取、写入装置,因此不能使用 cp 等指令)

# 特殊应用:利用管线命令与数据流

关于数据流重导向与管线命令在胡须 bash 章节再详细讲解

[mrcode@study ~]$ cd /tmp/
[mrcode@study tmp]$ tar -cv -f - /etc/ | tar -xv -f -
# 前面是将 /etc/ 打包到 - ,后面是吧 - 解压
# 这里的 - 表示标准的输出 和输出,可以吧 - 想成是内存中的一个缓冲区
# 这里命令像  cp -r /etc /tmp 的效果
# 这里不想用 -r 命令,所以使用 tar 打包到特殊的装置 - 中,然后管线前面输出的作为后面用来解压,没有产生中间文件,完成了复制的功能
1
2
3
4
5
6

# # 例题:系统备份范例

系统上有非常多的目录需要进行备份,也不建议将备份数据放到 /root 目录下,假设目前已经知道重要的目录有:

  • /etc/:配置文件
  • /home/ :用户的家目录
  • /var/spool/mail/:系统中所有的邮件信箱
  • /var/spool/cron/:所有账户的工作排成配置文件
  • /root/:系统管理员的家目录

前面做过的练习,/home/loop* 不需要备份,/root 下的压缩文件也不需要备份,假设需要将备份的数据放到 /backups 中,并且该目录仅有 root 权限进入,此外,每次备份的文件名希望不相同。

# 创建备份目录,并修改权限
[root@study ~]# mkdir /backups
[root@study ~]# chmod 700 /backups/
[root@study ~]# ll -d /backups/
drwx------. 2 root root 6 1029 01:33 /backups/

# 这里的 xxx 需要手动写上想要的日期等字符串每次就不一样了,并不是用脚本变量啥的
tar -zcv -f /backups/xxx.tar.gz --exclude="/home/loop*" --exclude="/root/*.gz" --exclude="/root/*.bz2" --exclude="/root/*.xz" /etc/ /home/ /var/spool/mail /var/spool/cron /root 

[root@study ~]# ll -h /backups/
总用量 13M
-rw-r--r--. 1 root root 13M 1029 01:37 xxx.tar.gz

1
2
3
4
5
6
7
8
9
10
11
12
13

# 解压缩后的 SELinux 课题

假如你的系统必须要以备份的数据来回填到原本的系统中,那么需要特别注意复原后的系统 SELinux 问题,尤其是在系统文件上面。比如:/etc 下的文件群。SElinux 是比较特别的细部权限设定,具体的会在第 16 章介绍。SELinux 的权限问题,可能让你的系统无法存取某些配置文件内容,导致影响到系统的正常使用权。

有一个例子,通过上面的 tar 备份,然后在另外一部系统上还原回来,但是无法正常的登录系统,在单位维护模式去操作系统,看起来一切都正常,但是这里就是无法登录。大部分原因就是因为 /etc/shadow 密码文件的 SELinux 类型在还原时被更改了,简单的处理方式有如下几个:

  • 通过各种可行的救援方式登录系统,修改 /etc/seliux/config 文件,将 SELinux 改成 permissive 模式,重新启动系统就可以了
  • 在第一次复原系统后,不要立即重新启动,先使用 restorecon -Rv /etc 自动修复下 SELinux 的类型即可
  • 通过各种可行的方式登录系统,建立 /.autorelabel 文件,重新启动后系统会自动修复 SELinux 的类型,并且又会再次重新启动,之后就正常了