LJKのBlog

学无止境

进程和内存管理

什么是进程?

process:程序是静态文件,程序启动后会载入到内存中,运行中的程序就是进程,进程是一个指令合集,是资源分配的单位。

第一个进程是 init,centos7 以后为 systemd

进程都在/proc/目录下,例如有不明进程 724 占用 CPU 过高,可以cat /dev/null > /proc/724/exe先清空进程,然后再排查问题,不直接 kill 是怕这个进程是木马程序,kill 掉还会重启。

进程都是由父进程创建,使用 fork()函数创建子进程。fork()会产生一个和父进程完全相同的子进程,只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。这种机制就叫写时复制

写时复制在 php 中的应用:

1
2
$a = 'this is string';
$b = $a;

此时,$a和$b 在内存中指向同一地址, 当修改$a或者$b 的时候, 才会复制一份, 然后对复制的这份进行修改。

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
146 typedef struct _zend_refcounted_h {
147 uint32_t refcount; /* reference counter 32-bit
148 php7.1中常量字符串refcount=0,依靠u来标记常量,在7.3中已经改掉了*/
149 union {
150 struct {
151 ZEND_ENDIAN_LOHI_3(
152 zend_uchar type,
153 zend_uchar flags, /* used for strings & objects
154 存储字符串的类型,例如:常量字符串2,变量字符串0 */
155 uint16_t gc_info) /* keeps GC root number (or 0) and color */
156 } v;
157 uint32_t type_info;
158 } u;
159 } zend_refcounted_h;
160
161 struct _zend_refcounted {
162 zend_refcounted_h gc;
163 };
165 // 写时复制:对于整型或者其他简单类型的,因为zval 16个字节就可以表示了,所以是直接复制的
166 // 而对于zend_string, `$a = 'string'; $b = $a;` $a和$b的zval指向的是同一个zend_string,用zend_string中的
167 // gc中的refcount进行标记,2,表示这个zeng_string被两个zval引用了,当我们修改$b的时候,就是修改$b指向的zval,
168 // $b指向zval的地址没变,但是zval的内容变了
169 // 同时原先的zend_string的gc也会修改,refcount减1
170 struct _zend_string {
171 zend_refcounted_h gc;
172 zend_ulong h; /* hash value 以空间换时间 */
173 size_t len;
174 char val[1]; // 柔性数组一定放在结构体的尾部
175 };

进程结构

内核把进程存放在任务队列中,任务队列是双向循环链表结构。

链表中的每一项都是结构体 task_struck,这种结构体称为进程控制块 PCB,PCB 中包含了一个具体进程的所有信息:

  • 进程 id、用户 id 和组 id
  • 程序计数器
  • 进程的状态(有就绪、运行、阻塞)
  • 进程切换时需要保存和恢复的 CPU 寄存器的值
  • 描述虚拟地址空间的信息
  • 描述控制终端的信息
  • 当前工作目录
  • 文件描述符表,包含很多指向 file 结构体的指针
  • 进程可以使用的资源上限(ulimit –a 命令可以查看)
  • 输入输出状态:配置进程使用 I/O 设备

进程、线程、协程

查看进程中的线程:cat /proc/PID/status | grep -i threads

  1. 进程是资源分配的单位
  2. 线程是操作系统调度的单位
  3. 进程切换需要的资源很大, 效率很低;线程稍好;协程最好
  4. 多进程、线程根据 cpu 核数不一样可能是并行的,但是协程是在一个线程中,所以肯定是并发

进程相关概念

进程运行在内存中,所以有必要了解一下内存。磁盘给文件分配空间的最小单位是块,一般是 4K,内存给进程分配空间的最小单位是,又叫页框,大小也是 4K

1
2
3
4
[root@centos7 kernels]# getconf -a | grep -i size
...
PAGE_SIZE 4096
...

getconf 命令可以查看系统参数。比如 系统位数、页面大小等。-a 获取所有系统参数信息。

范例:

1
2
3
4
[root@centos7 kernels]# getconf LONG_BIT
64
[root@centos7 kernels]# getconf PAGE_SIZE
4096

物理地址空间和虚拟地址空间

物理地址空间:你插了两根 8G 的内存条,就有 16G 的物理地址空间。

虚拟地址空间 和 交换空间 swap 没有关系,是进程“需要的”虚拟内存大小,包括进程使用的库、代码、数据,以及 malloc、new 分配的堆空间和分配的栈空间等。假如进程申请 100m 的内存,但实际上只使用 了 10m,那么它会增长 100m,而不是实际的使用量。现代操作系统里面分配虚拟地址空间操作不同于分配物理内存。在 64 位操作系统上,可用的最大虚拟地址空间有 16EB,即大概 180 亿 GB。那么在一台只有 16G 的物理内存的机器上,也可以要求获得 4TB 的地址空间以备将来使用:

1
void *mem = mmap(0, 4ul * 1024ul * 1024ul * 1024ul * 1024ul, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);

当使用 mmap 并设置 MAP_NORESERVE 标志时,并不会要求实际的物理内存和 swap 空间存在。执行上述代码,可以在 top 中看到使用了 4096g 的 VIRT 虚拟内存,这当然不是真实的内存,它只是表示使用了 4096GB 的地址空间而已。

一般来说不用太在意 VIRT 太高,因为你有 16EB 的空间可以使用。

MMU

Memory Management Unit 内存管理单元,是硬件,负责虚拟地址转换为物理地址。

程序在访问一个内存地址指向的内存时,CPU 不是直接把这个地址送到内存总线上,而是被送到 MMU,然后把这个内存地址映射到实际的物理内存地址上,最后通过总线再去访问内存,程序操作的地址就是虚拟内存地址。

TLB

Translation Lookaside Buffer 翻译后备缓冲器,用于保存虚拟地址和物理地址映射关系的缓存

用户和内核空间

C 代码和内存布局之间的对应关系

每个进程都包括 5 种不同的数据段:

  1. 代码段:可执行程序在内存中的镜像,只读,不可写。
  2. 数据段:存放已初始化全局变量
  3. BSS 段:Block Started by Symbol”的缩写,意为“以符号开始的块,BSS 段包含了程序中未初始化的全局变量,在内存中 bss 段全部置零。
  4. 堆:heap,存放数组和对象,堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用 malloc 等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用 free 等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)
  5. 栈:stack,栈是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括 static 声明的变量,static 意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的后进先出特点,所以栈特别方便用来保存/恢复调用现场。可以把堆栈看成一个寄存、交换临时数据的内存区
堆和栈的区别
  • 程序内存布局场景下,堆与栈表示两种内存管理方式;
  • 数据结构场景下,堆与栈表示两种常见的数据结构;

程序内存分区中的堆和栈:

  • 栈 由操作系统自动分配释放,用与存放函数的参数值,局部变量等,其操作方式类似与数据结构中的栈。栈区向地址减小的方向增长。程序员无法控制,若分配失败,则提示栈移出错误。
  • 堆 由开发人员分配和释放,若开发人员不释放,程序结束时由 OS 回收,分配方式类似与链表。php 中 new 等可以申请内存,delete 等可以删除内存。

数据结构中的堆和栈

  • 栈的基本操作包括初始化、判断栈是否为空、入栈、出栈及获取栈顶元素等。

进程使用内存问题

内存泄露:Memory Leak

指程序中用 malloc 或 new 申请了一块内存,但是没有用 free 或 delete 将内存释放,导致这块内存一直处于占用状态

内存溢出:Memory Overflow

指程序申请了 10M 的空间,但是在这个空间写入 10M 以上字节的数据,就是溢出

内存不足:OOM

OOM 即 Out Of Memory,“内存用完了”,在情况在 java 程序中比较常见。

在日志 messages 中看到类似下面的提示:

1
Jul 10 10:20:30 kernel: Out of memory: Kill process 9527 (java) score 88 or sacrifice child

当 JVM 因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时,就会选一个进程杀掉。

导致原因:

  • 给应用分配的内存太小
  • 内存泄露和内存溢出导致内存不够用

相关内核参数:

1
2
3
echo 2 > /proc/sys/vm/overcommit_memory
echo 80 > /proc/sys/vm/overcommit_ratio
echo 2 > /proc/sys/vm/panic_on_oom

Linux 默认是允许 memory overcommit 的,只要你来申请内存我就给你,寄希望于进程实际上用不到那么多内存,但万一用到那么多了呢?Linux 设计了一个 OOM killer 机制挑选一个进程出来杀死,以腾出部分内存,如果还不够就继续。也可通过设置内核参数 vm.panic_on_oom 使得发生 OOM 时自动重启系统。这都是有风险的机制,重启有可能造成业务中断,杀死进程也有可能导致业务中断。所以 Linux
2.6 之后允许通过内核参数 vm.overcommit_memory 禁止 memory overcommit。

  • overcommit_memory

    • 0:默认值,允许值,允许 overcommit,但是过于明目张胆的 overcommit 会被拒绝,内核利用某种算法猜测你的内存申请是否合理,它认为不合理就会拒绝 overcommit。
    • 1:允许 overcommit,对内存申请来者不拒。
    • 2:禁止 overcommit,申请的内存不允许超过可用内存的大小。
  • overcommit_ratio

    overcomit_memory= 2 时,该参数有效,默认值 50,表示物理内存的 50%。

  • panic_on_oom

    • 0:默认值,出现 oom 时,触发 oom killer
    • 1:程序在有 cpuset、memory policy、memcg 的约束情况下的 OOM,可以考虑不 panic,而是启动 OOM killer。其它情况触发 kernel panic,即系统直接重启
    • 2:当出现 oom,直接触发 kernel panic,即系统直接重启

进程状态

进程的生命周期:

使用 prtstat 或 top 可以查看命令状态,或者直接查看文件/proc/PID/status

top 命令的 S 字段表示进程的状态:S 表示休眠,R 表示正在运行,Z 表示僵死状态,N 表示该进程优先值是负数

进程的基本状态:

  • 创建:创建 PCB,如果无法无法创建(比如资源无法满足)
  • 就绪:ready,进程已分配到所需资源,等待 CPU 的调度
  • 执行:running,CPU 调度,进入到执行状态
  • 阻塞:执行的进程由于某些事件而暂时无法运行,进入阻塞状态,等阻塞解决了,进入到就绪状态。CPU 是不会等你的。
  • 终止:进程结束,正常结束或非正常结束。

进程更多状态

  • 睡眠:可以当成阻塞处理,区别在于阻塞状态是被动进入,睡眠状态是主动进入,而且可以定时。准确的说,阻塞是一种状态,而睡眠和挂起是一种行为。
    • 可中断:interruptable
    • 不可中断:uninterruptable
  • 停止:stopped,暂停于内存,但不会被调度,除非手动启动
  • 挂起:暂停于外存,需要先激活才能使用
  • 孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被 init 进程(进程号为 1)所收养,并由 init 进程对它们完成状态收集工作。
  • 僵尸进程:zombie,如果一个进程已经终止,但是它的父进程尚未调用 wait() 或 waitpid() 对它进行清理,这时的进程状态称为僵死状态,处于僵死状态的进程称为僵尸进程(zombie process)。任何进程在刚终止时都是僵尸进程,正常情况下,僵尸进程都立刻被父进程清理了。
    • 杀死父进程可以关闭僵死态 的子进程

LRU 算法

IPC 进程间通信

同一主机

1
2
3
4
5
6
7
pipe 管道,单向传输
socket 套接字文件
Memory-maped file 文件映射,将文件中的一段数据映射到物理内存,多个进程共享这片内存
shm shared memory 共享内存
signal 信号
Lock 对资源上锁,如果资源已被某进程锁住,则其它进程想修改甚至读取这些资源,都将被阻塞,直到锁被打开
semaphore 信号量,一种计数器

不同主机:socket=ip:port

1
2
RPC remote procedure call
MQ 消息队列,生产者和消费者,如:kafka、RabbitMQ,ActiveMQ

进程优先级

top 命令的 NI 字段表示 nice 优先级,-20 到 19,取值越大,优先级越低。就像人一样,越 nice 的人越被欺负,越 nice 的进程优先级越低。

进程分类

  • 守护进程:daemon,在系统引导过程中启动的进程,和终端无关。
  • 前台进程:和终端相关,通过终端启动的进程。

IO 调度算法

  • NOOP
  • Deadline scheduler
  • Anticipatory scheduler
  • CFQ

查看 IO 调度算法:cat /sys/block/sda/queue/scheduler

任务计划

一次性任务

周期性任务计划 cron

系统 cron 计划任务

anacron

管理临时文件

用户计划任务

通常的 linux 发行版对于网络的配置方法一般会同时支持 network.service 和 NetworkManager.service(简称 NM)。默认情况下,这 2 个服务都有开启,而且功能上是平行的,可以通过任意一个来配置网络,正常的情况下通过 NM 来配置网络后它会自动把配置同步到 network.service 的配置中。

nmcli 是 NetworkManager 的命令行工具, 该命令可以完成网卡上所有的配置工作,绝大部分可以写入配置文件,永久生效 。

1
nmcli [OPTIONS] OBJECT { COMMAND | help }

OPTIONS:

1
2
3
4
5
6
7
8
-t                                # terse output 简洁的输出
-p # pretty output 漂亮的输出
-m tabular|multiline # output mode 输出模式
-f <field1,field2,...>|all|common # specify fields to output 指定要输出的字段
-e yes|no # escape columns separators in values 在值中转义列分隔符
-n # 不要检查nmcli和NetworkManager版本
-a # 要求缺少参数
-w <seconds> # 设置超时等待整理操作

OBJECT

1
2
3
4
5
6
7
g[eneral]      # 显示NM状态和权限;获取和更改系统主机名、NetworkManager logging level and domains
n[etworking] # 查询NM网络状态,启用和禁用网络
r[adio] # 无线网相关
c[onnection] # NM的连接
d[evice] # 显示和管理网络接口
a[gent] # Run nmcli as a NetworkManager secret agent, or polkit agent.
m[onitor] # 观察NM活动,监视连接状态、设备或连接配置文件的更改

最常用的是 device 和 connection,主要是 connection

device

设备,可理解为实际存在的网卡(包括物理网卡和虚拟网卡)。可以简写为 nmcli d

在 NM 里,有 2 个层级:连接(connection)和设备(device),通常 NM 的管理是以连接为单位的,就像你的手机可以记住多个 wifi 连接,但一个时刻只能连接一个 wifi。一个网卡可以关联多个连接,但是一个网卡只能有一个连接处于活跃状态,通过 nmcli connetion up切换连接。

1
2
3
4
5
6
# 查看网卡状态
[root@centos7 network-scripts]#nmcli device
DEVICE TYPE STATE CONNECTION
ens33 ethernet connected ens33
ens37 ethernet connected ens37
lo loopback unmanaged --

connection

连接,可理解为配置文件,相当于 ifcfg-ethX。可以简写为nmcli c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 修改网卡名,注意修改的是NAME不是DEVICE
[root@centos7 network-scripts]#nmcli connection modify ens33 con-name 'ens33 name'
[root@centos7 network-scripts]#nmcli connection
NAME UUID TYPE DEVICE
ens33 name c96bc909-188e-ec64-3a96-6a90982b08ad ethernet ens33
ens37 4a5516a4-dfa4-24af-b1c4-e843e312e2fd ethernet ens37
[root@centos7 network-scripts]#nmcli connection modify ens33\ name con-name ens33
# 修改网卡地址,只修改文件不生效,修改之前先使用 ipv4.method manual 将BOOTPROTO=dhcp改成BOOTPROTO=none
# 否则不能修改,只能添加或删除
[root@centos7 network-scripts]#nmcli connection modify ens33 ipv4.addresses 10.0.0.8/24
# 使修改网卡地址生效
[root@centos7 network-scripts]#nmcli connection reload
[root@centos7 network-scripts]#nmcli connection up ens33
# 给网卡添加地址
[root@centos7 network-scripts]#nmcli connection modify ens33 +ipv4.addresses 11.0.0.7/24
# 从网卡删除地址
[root@centos7 network-scripts]#nmcli connection modify ens33 -ipv4.addresses 11.0.0.7/24

ubuntu 使用 nmcli

ubuntu 默认情况下,无法使用 nmcli

  1. 修改 etc/netplan/*.yaml

    1
    2
    3
    4
    root@u2:~# vim /etc/netplan/00-installer-config.yaml
    network:
    version: 2
    renderer: NetworkManager # 注意此行
  2. /etc/NetworkManager/NetworkManager.conf

    1
    2
    3
    4
    5
    6
    7
    8
    9
    root@u2:~# vim /etc/NetworkManager/NetworkManager.conf
    [main]
    plugins=ifupdown,keyfile

    [ifupdown]
    managed=flase

    [device]
    wifi.scan-rand-mac-address=no
  3. 应用配置

    1
    root@u2:~# netplan apply

路由器

交换机主要用于组建局域网,路由主要功能是将由交换机组好的局域网相互连接起来,或者接入 Internet

交换机能做的,路由器都能做

同一网段的 ip 可以直接通信,不同网段的 ip 需要通过路由器进行通信

路由器是属于网络互联设备,每一个端口连接的都是不同的网络 。在隔绝广播域方面,路由器做了以下工作:

对外端口:① 路由器接收到广播,直接拒绝;② 路由器接收到单播,先校验 MAC 地址,如果是接口的 MAC 地址,则解封去看三层的 ip 地址,查看目标地址(匹配路由表),重新封装二层帧头,转发

对内端口:判断信息目的地,如果是

  • 工作在网络层
  • 隔离广播域和冲突域
  • 选择路由表中到达目标的最好路径
  • 维护和检查路由信息
  • 连接广域网

有的交换机会集成路由器的功能,又叫三层交换机

磁盘结构

一切皆文件:open(),read(),write(),close()

设备文件:关联至驱动程序,进而能够与对应的硬件设备进行通信

设备号码

  • 主设备号:major number, 标识设备类型
  • 次设备号:minor number, 标识同一类型下的不同设备

设备类型

  • 块设备:block,存取设备单位“块”,磁盘等
  • 字符设备:char,存取设备单位“字符”,键盘等

机械硬盘的结构有两种,如下图所示:

硬盘寻址方式有两种:CHS 和 LBA

CHS:24 bit 位寻址,其中前 10 位表示柱面,中间 8 位表示磁头,后面 6 位表示扇区,最大寻址空间 8 GB

LBA:逻辑块寻址,LBA 是一个整数,表示从第几个扇区开始,到第几个扇区结束就完了,没有柱面磁头等概念,扇区这个概念也是逻辑上的,一个扇区通常是 512 个字节

创建设备文件

如果两个设备文件的类型、主次号码都相同,那这俩就是同一个设备

touch 创建普通文件,mknod 创建设备文件,删除用 rm,不能 cp 直接拷贝,如果要拷贝,使用cp -a保留属性即可拷贝

1
2
3
4
5
6
7
8
9
[root@centos7 ~]#ll /dev/null /dev/zero
crw-rw-rw-. 1 root root 1, 3 Aug 18 14:39 /dev/null
crw-rw-rw-. 1 root root 1, 5 Aug 18 14:39 /dev/zero
[root@centos7 ~]#mknod ./testnull c 1 3
[root@centos7 ~]#mknod ./testzero c 1 5
[root@centos7 ~]#ll
total 0
crw-r--r--. 1 root root 1, 3 Aug 18 17:16 testnull
crw-r--r--. 1 root root 1, 5 Aug 18 17:17 testzero

分区类型

两种分区方式:mbr、gpt

给虚拟机添加硬盘

虚拟机->添加->硬盘->scsi->虚拟硬盘->单个文件->完成

1
2
echo - - - > /sys/class/scsi_host/host0/scan
# 如果lsblk还不显示,再试试host1和host2

MBR

mbr 最多四个主分区,可以 3 个主分区+1 扩展分区(N 个逻辑分区)

主分区和扩展分区对应 1~4,/dev/sda3,逻辑分区从 5 开始,/dev/sda5

每个分区项占 16 个字节。

  • 第 1 个字节:活动分区启动计算机,非活动分区只是数据分区,所以四个分区只有一个 80,其他三个都是 00。
  • 第 2、3、4 个字节:这三个字节说明分区在硬盘上的起始位置,CHS 寻址。
  • 第 5 个字节:说明分区是否使用,0 表示未使用。
  • 第 6、7、8 个字节:这三个字节说明分区在硬盘上的结束位置,CHS 寻址。我们现在已经不使用 CHS 寻址了,虽然仍然给它保留了位置。
  • 第 9、10、11、12 个字节:这四个字节说明分区在硬盘上的起始位置,LBA 寻址。
  • 第 12、13、14、15、16 个字节:最后这四个字节说明分区在硬盘上的结束位置,LBA 寻址。按照每个扇区 512 个字节计算,4 个字节 32 位最多表示 2^32*512=2199023255552 字节=2T,所以 mbr 下 LBA 的寻址范围最大就是 2T,所以 mbr 分区的硬盘容量最大 2T,注意是硬盘 2T,不是分区 2T。

逻辑分区的分区信息储存在自己的分区内,所以逻辑分区可以有无数个。具体分区信息看图:

hexdump -C /dev/sda -n 512 查看硬盘的前 512 个字节,55 aa 这两个字节是结束标志位,高亮的 64 个字节是分区表,每 16 个字节是一个分区项

mbr 中的分区表 DPT:

GPT

gpt 支持 128 个分区,一般来说,windows 只能是 bios+mbr 或者 uefi+gpt 的组合,而 Linux 可以 bios+gpt 通过 grub 启动

磁盘常用命令

df

查看文件系统空间实际占用等信息

1
df [OPTION]... [FILE]...
  • -H 进制为 1000,而不是 1024
  • -T 显示文件系统类型
  • -h human-readable
  • -i 查看 inodes 的使用情况
  • -P 以 Posix 兼容的格式输出

du

查看某目录总体空间实际占用状态

1
du [OPTION]... DIR
  • -h human-readable
  • -s summary 只显示目录占用的空间,不一一列出目录下所有的文件
  • –max-depth=# 指定显示的目录层级,该选项和-s 冲突
  • -x 忽略不在同一个文件系统的目录

问题:如果 df 和 du 统计的结果差距过大,是为什么?

1
2
3
如果差距比较小,只有几十或者几十百m,是正常情况。如果差距过大,则是因为删除大文件导致。df直接统计分区,du对目录下的文件逐个统计,如果删除大文件,但是还有进程引用这个文件,此时df还会统计到它,而du是统计不到它的,这样就会造成df和du统计的结果差距过大

解决方法:`lsof | grep delete` 找出占用已删除文件的进程,然后杀掉就行了

dd

从目标文件 1 拷贝内容写入到目标文件 2

1
dd if=/PATH/FROM/SRC of=/PATH/TO/DEST bs=# count=#
  • if=file1:in file = file1 从 file1 读取内容
  • of=file2:out file=file2 将读取的内容写入到 file2
  • bs=size:block size 指定块的大小,默认单位是字节
  • count=# 读取#个块,如果要读取 1G 的内容,可以bs=1024M count=1,也可以bs=1M count=1024,或者bs=64M count=16。不指定 bs 和 count,可以理解为备份
  • skip=# 读 file1 的时候,忽略前#个块,从#+1 个块开始读取
  • seek=# 写 file2 的时候,忽略前#个块,从#+1 个块开始写入
  • conv=conversion[,conversion…] 用指定的参数转换文件
    • conversion 转换参数:
    • ascii 转换 EBCDIC 为 ASCII
    • ebcdic 转换 ASCII 为 EBCDIC
    • lcase 把大写字符转换为小写字符
    • ucase 把小写字符转换为大写字符
    • nocreat 不创建输出文件
    • noerror 出错时不停止
    • notrunc 不截短输出文件,file2 被写入内容的位置开始,默认先截断成 0 字节大小,这个参数可以保证不截断。例如 file2 内容是 123456789,从开头位置写入 ab,默认从开头往后,都截断成 0 字节,然后再写入 ab,添加 notrunc 参数,写完后是 ab3456789。
    • sync 把每个输入块填充到 ibs 个字节,不足部分用空(NUL)字符补齐
    • fdatasync 写完成前,物理写入输出文件

范例:

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
41
42
43
#备份MBR
dd if=/dev/sda of=/tmp/mbr.bak bs=512 count=1

#破坏MBR中的bootloader
dd if=/dev/zero of=/dev/sda bs=64 count=1 seek=446

#有一个大与2K的二进制文件fileA。现在想从第64个字节位置开始读取,需要读取的大小是128Byts。又有fileB, 想把上面读取到的128Bytes写到第32个字节开始的位置,替换128Bytes,实现如下
dd if=fileA of=fileB bs=1 count=128 skip=63 seek=31 conv=notrunc

#将本地的/dev/sdx整盘备份到/dev/sdy
dd if=/dev/sdx of=/dev/sdy

#将/dev/sdx全盘数据备份到指定路径的image文件
dd if=/dev/sdx of=/path/to/image

#备份/dev/sdx全盘数据,并利用gzip压缩,保存到指定路径
dd if=/dev/sdx | gzip >/path/to/image.gz

#将备份文件恢复到指定盘
dd if=/path/to/image of=/dev/sdx

#将压缩的备份文件恢复到指定盘
gzip -dc /path/to/image.gz | dd of=/dev/sdx

#将内存里的数据拷贝到root目录下的mem.bin文件
dd if=/dev/mem of=/root/mem.bin bs=1024

#拷贝光盘数据到root文件夹下,并保存为cdrom.iso文件
dd if=/dev/cdrom of=/root/cdrom.iso

#销毁磁盘数据
dd if=/dev/urandom of=/dev/sda1

#通过比较dd指令输出中命令的执行时间,即可确定系统最佳的block size大小
dd if=/dev/zero of=/root/1Gb.file bs=1024 count=1000000
dd if=/dev/zero of=/root/1Gb.file bs=2048 count=500000
dd if=/dev/zero of=/root/1Gb.file bs=4096 count=250000

#测试硬盘写速度
dd if=/dev/zero of=/root/1Gb.file bs=1024 count=1000000

#测试硬盘读速度
dd if=/root/1Gb.file bs=64k | dd of=/dev/null

练习:

  1. 创建一个 2G 的文件系统,块大小为 2048byte,预留 1%可用空间,文件系统 ext4,卷标为 TEST,要求
    此分区开机后自动挂载至/test 目录,且默认有 acl 挂载选项
  2. 写一个脚本,完成如下功能:
    (1) 列出当前系统识别到的所有磁盘设备
    (2) 如磁盘数量为 1,则显示其空间使用信息
    否则,则显示最后一个磁盘上的空间使用信息
  3. 将 CentOS6 的 CentOS-6.10-x86_64-bin-DVD1.iso 和 CentOS-6.10-x86_64-bin-DVD2.iso 两个文件,
    合并成一个 CentOS-6.10-x86_64-Everything.iso 文件,并将其配置为 yum 源

管理分区

lsblk

列出块设备

1
lsblk [options] [<device> ...]
  • -f:列出文件系统相关信息

blkid

查看块设备(包括交换分区)所使用的文件系统类型、LABEL、UUID 等信息

1
blkid [OPTION]... [DEVICE]

-U UUID 根据指定的 UUID 来查找对应的设备
-L LABEL 根据指定的 LABEL 来查找对应的设备

fdisk

管理 mbr 分区。交互式,根据提示操作即可。

1
2
3
4
5
6
m:查看帮助
p:查看分区
n:增加分区
d:删除分区
w:保存并退出
q:不保存并退出

对硬盘进行分区操作,如果硬盘上已有其他已经挂载的分区,w 保存操作后,centos7 及以前的系统识别不到分区更改,需要手动通知内核重新读取硬盘分区表

硬盘增加分区,前提是硬盘要有未分配的空间

重新加载分区表

centos7 和 centos5:

1
partprobe

centos6:

1
2
3
4
# 增加分区
partx -a /dev/DEVICE
# 删除分区
partx -d --nr M-N /dev/DEVICE

gdisk

管理 gpt 分区,操作和 fdisk 雷同

parted

高级分区操作,可以使交互式或非交互式,parted 的操作都是实时生效的,所以一般不用

管理文件系统

文件系统组成

以 ls 命令为例,它的作用是列出文件,它并不会与 ext4、xfs 等文件系统直接交互,而是和 vfs 进行交互,由 vfs 处理各种文件系统的兼容性

  • 内核中的模块:ext4, xfs, vfat
  • Linux 的虚拟文件系统:VFS
  • 用户空间的管理工具:mkfs.ext4、mkfs.xfs、mkfs.vfat

新硬盘,先 fdisk 分区,然后 mkfs 创建文件系统,最后 mount 挂载

文件系统是操作系统用于明确存储设备或分区上的文件的方法和数据结构;即在存储设备上组织文件的方法。操作系统中负责管理和存储文件信息的软件结构称为文件管理系统,简称文件系统从系统角度来看,文件系统是对文件存储设备的空间进行组织和分配,负责文件存储并对存入的文件进行保护和检索的系统。具体地说,它负责为用户建立文件,存入、读出、修改、转储文件,控制文件的存取,安全控制,日志,压缩,加密等

当前系统支持的文件系统:

1
[root@centos7 ~]# rpm -qf /lib/modules/3.10.0-1127.el7.x86_64/kernel-3.10.0-1127.el7.x86_64

上面显示的不全,我不知道为什么,通过查看内核包可以看到所有支持的文件系统,每个 xz 压缩包都是对应文件系统的驱动。

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
# centos7
[root@centos7 ~]# rpm -qf /lib/modules/3.10.0-1127.el7.x86_64/kernel/fs/
kernel-3.10.0-1127.el7.x86_64
[root@centos7 ~]# rpm -ql `!!` | grep '/fs/'
rpm -ql `rpm -qf /lib/modules/3.10.0-1127.el7.x86_64/` | grep '/fs/'
/lib/modules/3.10.0-1127.el7.x86_64/kernel/fs/binfmt_misc.ko.xz
/lib/modules/3.10.0-1127.el7.x86_64/kernel/fs/btrfs
/lib/modules/3.10.0-1127.el7.x86_64/kernel/fs/btrfs/btrfs.ko.xz
/lib/modules/3.10.0-1127.el7.x86_64/kernel/fs/cachefiles
/lib/modules/3.10.0-1127.el7.x86_64/kernel/fs/cachefiles/cachefiles.ko.xz
/lib/modules/3.10.0-1127.el7.x86_64/kernel/fs/ceph
/lib/modules/3.10.0-1127.el7.x86_64/kernel/fs/ceph/ceph.ko.xz
/lib/modules/3.10.0-1127.el7.x86_64/kernel/fs/cifs
/lib/modules/3.10.0-1127.el7.x86_64/kernel/fs/cifs/cifs.ko.xz
...
...

# ubuntu18.04
lujinkai@Z510:~$ dpkg -S /lib/modules/5.4.0-42-generic/kernel/fs/
linux-modules-extra-5.4.0-42-generic, linux-modules-5.4.0-42-generic: /lib/modules/5.4.0-42-generic/kernel/fs
lujinkai@Z510:~$ dpkg -L linux-modules-extra-5.4.0-42-generic | grep '/fs/'
/lib/modules/5.4.0-42-generic/kernel/fs/9p
/lib/modules/5.4.0-42-generic/kernel/fs/adfs
/lib/modules/5.4.0-42-generic/kernel/fs/adfs/adfs.ko
/lib/modules/5.4.0-42-generic/kernel/fs/affs
/lib/modules/5.4.0-42-generic/kernel/fs/affs/affs.ko
/lib/modules/5.4.0-42-generic/kernel/fs/afs
/lib/modules/5.4.0-42-generic/kernel/fs/afs/kafs.ko
/lib/modules/5.4.0-42-generic/kernel/fs/aufs
/lib/modules/5.4.0-42-generic/kernel/fs/autofs
/lib/modules/5.4.0-42-generic/kernel/fs/befs
/lib/modules/5.4.0-42-generic/kernel/fs/befs/befs.ko
/lib/modules/5.4.0-42-generic/kernel/fs/bfs
/lib/modules/5.4.0-42-generic/kernel/fs/bfs/bfs.ko
...
...
...

或者查看 /proc/filesystems,nodev 标注的文件系统不需要块文件,也就是说不需要硬盘,文件就可以

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
[root@centos7 ~]# cat /proc/filesystems
nodev sysfs
nodev rootfs
nodev ramfs
nodev bdev
nodev proc
nodev cgroup
nodev cpuset
nodev tmpfs
nodev devtmpfs
nodev debugfs
nodev securityfs
nodev sockfs
nodev dax
nodev bpf
nodev pipefs
nodev configfs
nodev devpts
nodev hugetlbfs
nodev autofs
nodev pstore
nodev mqueue
nodev selinuxfs
xfs
iso9660
ext3
ext2
ext4
vfat

超级块和 inode table

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@centos7 ~]# mkfs.ext4 /dev/sdb5
mke2fs 1.42.9 (28-Dec-2013)
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
Stride=0 blocks, Stripe width=0 blocks
128016 inodes, 512000 blocks
25600 blocks (5.00%) reserved for the super user
First data block=1
Maximum filesystem blocks=34078720
63 block groups
8192 blocks per group, 8192 fragments per group
2032 inodes per group
Superblock backups stored on blocks:
8193, 24577, 40961, 57345, 73729, 204801, 221185, 401409

Allocating group tables: done
Writing inode tables: done
Creating journal (8192 blocks): done
Writing superblocks and filesystem accounting information: done

文件系统把每个分区分成多个块组(block group),每个块组中有很多块,每个块的大小通常为 4K,具体块组的组成看下图。超级块(super block)中存储了所有块组的元数据,超级块存储在块组 0 中,并在其他部分块组有备份;块组描述符表(GDT)是块组描述符的集合,和超级组一样,也是储存块组 0 中,在其他部分块组中备份;块位图(block bitmap)描述了块的使用情况;节点位图(inode bitmap)描述了节点的使用情况。节点表(inode table)存储节点;数据块(data blocks)存储数据

块组描述符
Ext3 文件系统的每个块组描述符占用 32 字节,用以描述块组中的位图起始地址、i-节点表起始地址、空闲块数、空闲 i-节点数等信息,每个块组都有这样的一个块组描述符,所有的块组描述符集中存放,组成块组描述符表。

mkfs

创建文件系统

1
mkfs.FS_TYPE /dev/DEVICE

FS_TYPE:ext4、xfs 等,具体 tab 可以提示

tune2fs 、dumpefs、xfs_info

tune2fs 查看超级块信息,dumpe2fs 查看超级块和各个块组的信息,tune2fs 和 dump2fs 只适用于 ext 文件系统,xfs 文件系统使用 xfs_info 命令

1
2
3
[root@centos7 ~]# tune2fs -l /dev/sdb5
[root@centos7 ~]# dumpe2fs /dev/sdb5
[root@centos7 ~]# xfs_info /dev/sda1

fsck、e2fsck、xfs_repair

tune2fs、dumpe2fs 查看分区的 ext 文件系统信息,Filesystem state 标记为 no clean,说明文件系统发生故障。使用 e2fsck 可以修复。如果是 xfs 文件系统发生故障,使用 xfs_repair 修改。fsck 通用,不管什么文件系统都可以修复

注意:1. 修复之前一定要 umount 先取消挂载

  1. 数据不一定能恢复
1
2
3
fsck.FS_TYPE  /dev/DEVICE
e2fsck /dev/DEVICE
xfs_repair /dev/DEVICE

挂载设备

mount

1
2
3
4
5
6
mount  device MOUNT_POINT

mount [-lhV]
mount -a [-fFnrsvw] [-t vfstype] [-O optlist]
mount [-fnrsvw] [-o option[,option]...] device|dir
mount [-fnrsvw] [-t vfstype] [-o options] device dir

device:设备文件:例如:/dev/sda5、伪文件系统名称:proc, sysfs, devtmpfs, configfs

MOUNT_POINT:挂载点目录必须事先存在,建议使用空目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 查看当前已挂载的所有设备
[root@centos7 ~]# mount
sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime,seclabel)
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)
devtmpfs on /dev type devtmpfs (rw,nosuid,seclabel,size=487128k,nr_inodes=121782,mode=755)
securityfs on /sys/kernel/security type securityfs (rw,nosuid,nodev,noexec,relatime)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev,seclabel)
devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,seclabel,gid=5,mode=620,ptmxmode=000)
...
...
...
# 通过查看/etc/mtab文件显示当前已挂载的所有设备
[root@centos7 ~]# ll /etc/mtab
lrwxrwxrwx. 1 root root 17 Aug 14 19:22 /etc/mtab -> /proc/self/mounts

# 查看内核追踪到的已挂载的所有设备
[root@centos7 ~]# ll /proc/mounts
lrwxrwxrwx. 1 root root 11 Aug 20 18:06 /proc/mounts -> self/mounts

mount -o options

挂载不同的文件系统,-o 的 options 是不同的,具体有哪些 option,参考对应的文件系统的挂载选项,例如:要挂载 nfs,man mount.nfs 查看对应 option,要挂载 cifs,man mount.cifs查看对应的 option …

umount

1
umount MOUNT_POINT|device

findmnt

查看挂载点信息

1
findmnt MOUNT_POINT|device

lsof、fuser

查看正在访问指定文件系统的进程

1
lsof MOUNT_POINT

终止所有正在访问指定挂载点的进程

1
fuser -km MOUNT_POINT

持久挂载

mount 命令是临时挂载,将挂载信息写入到/etc/fstab 中可以下次开机时自动启用挂载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@centos7 ~]# cat /etc/fstab

#
# /etc/fstab
# Created by anaconda on Wed Jul 29 17:08:37 2020
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
UUID=28eb957c-1e98-4175-9a00-3efdef247355 / xfs defaults 0 0
UUID=25ea1f67-ee88-44b1-935e-8af77eadf856 /boot xfs defaults 0 0
UUID=91664c04-1540-4e35-8711-c3f183a556ca /data xfs defaults 0 0
UUID=a1966abb-acf9-4f53-8a86-733a93c2d576 swap swap defaults 0 0

/dev/sr0 /data/repo/centos/7/x86_64 iso9660 defaults 0 0

注意/etc/fstab 中 device 一般用 UUID 指代,而不是路径,因为 UUID 稳定,一般不会更改,/dev/sr0 这种不是特别合适。

man 5 fstab可以查看/etc/fstab 文件的格式,最后两个选项:

  • 转储频率:0:不做备份 1:每天转储 2:每隔一天转储
  • fsck 检查的文件系统的顺序:允许的数字是 0 1 2
    • 0:不自检
    • 1:首先自检;一般只有 rootfs 才用
    • 2:非 rootfs 使用

添加新的挂载项,执行mount -a使挂载生效

管理 swap 空间

交换分区实现过程

  1. 创建交换分区或者文件
  2. 使用 mkswap 写入特殊签名
  3. 在/etc/fstab 文件中添加适当的条目
  4. 使用swapon -a 激活交换空间

范例:以文件实现 swap 功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@centos7 /]# dd if=/dev/zero of=/swapfile bs=1M count=1024
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB) copied, 2.04882 s, 524 MB/s

[root@centos7 /]# mkswap /swapfile
Setting up swapspace version 1, size = 1048572 KiB
no label, UUID=8ae1b7bc-7038-4f6a-9940-b75ec91beb21

[root@centos7 /]# chmod 600 /swapfile
[root@centos7 /]# swapon /swapfile

[root@centos7 /]# swapon -s
Filename Type Size Used Priority
/dev/sda5 partition 2097148 0 -2
/swapfile file 1048572 0 -3

swap 的使用策略

/proc/sys/vm/swappiness 的值决定了当内存占用达到一定的百分比时,会启用 swap 分区的空间,例如 swappiness 的值为 30,说明当物理内存使用到 70%,就开始使用 swap,应该将这个值设置为 0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 临时修改
[root@centos7 /]# echo 0 > /proc/sys/vm/swappiness

# 永久修改
# centos7
[root@centos7 /]# echo 'vm.swappiness=0' >> /etc/sysctl.conf
[root@centos7 ~]# sysctl -p
vm.swappiness = 0

# ubuntu18.04
lujinkai@Z510:~$ echo 'vm.swappiness=0' | sudo dd of=/etc/sysctl.conf oflag=append conv=notrunc
[sudo] password for lujinkai:
0+1 records in
0+1 records out
16 bytes copied, 0.000102837 s, 156 kB/s
lujinkai@Z510:~$ sudo sysctl -p
vm.swappiness = 0

RAID 管理

RAID:独立硬盘冗余阵列,简称磁盘阵列,利用虚拟化存储技术把多个硬盘组合起来,成为一个或多个硬盘阵列组,目的为提升性能或数据冗余,或是两者同时提升。

简单来说,RAID 把多个硬盘组合成为一个逻辑硬盘,因此,操作系统只会把它当作一个实体硬盘。

RAID 层级不同,数据会以多种模式分散于各个硬盘,常用的 RAID 层级:RAID 0、RAID 1、RAID 5、RAID 6、RAID 10、RAID 50、RAID 60

raid0

数据分散在多个硬盘,读写速度都有提升,但是一个硬盘坏了,所有数据都被破坏

raid 1

数据备份到多个硬盘,有几个硬盘,就有几个备份,理论上,两个硬盘组 raid1,空间利用率有 50%,三个硬盘组 raid 1,空间利用率只有 33%

raid 4

设置单独校验盘,数据分散在其他的硬盘,其他硬盘通过亦或运算得出的值,最多允许一块硬盘损坏,当存储数据的盘有坏的,其他盘可以计算出丢失的那部分数据。缺点是校验盘最容易坏。

raid 5

是 raid 4 的升级版,校验不是单独一块盘,而是将校验分散在所有硬盘,最多允许一块硬盘损坏

raid 6

是 raid 5 的升级版,校验由一份升级为两份,增加容错性,允许两块硬盘损坏。

生产中,通常会准备热备盘,例如 raid5 就需要额外 1 块盘做热备

raid 10

是 raid1 和 raid0 的组合,解决 raid 0 没有容错能力的缺点。

raid 50

是 raid 5 和 raid 0 的组合,对比 raid 10,读写的速度更快

raid 60

是 raid 6 和 raid 0 的组合,增加 raid 50 的容错能力

JBOD

将多块磁盘的空间合并一个大的连续空间使用

raid7

RAID 7 并非公开的 RAID 标准,而是 Storage Computer Corporation 的专利硬件产品名称,RAID 7 是以 RAID 3 及 RAID 4 为基础所发展,但是经过强化以解决原来的一些限制。另外,在实现中使用大量的缓冲存储器以及用以实现异步数组管理的专用即时处理器,使得 RAID 7 可以同时处理大量的 IO 要求,所以性能甚至超越了许多其他 RAID 标准的实际产品。但也因为如此,在价格方面非常的高昂.RAID7 可以理解为一个独立存储计算机,自身带有操作系统和管理工具,可以独立运行,理论上性能最高的 RAID 模式。

软件实现 raid

可以通过软件实现 raid,但是企业中不用,所以略过。。。

LVM 管理和 LVM 快照

LVM 逻辑卷管理器,本质上是一个虚拟设备驱动,在磁盘分区和文件系统之间添加一个逻辑层。底层的物理磁盘不再由内核直接控制,而是由 LVM 层来控制。LVM 维持着逻辑盘区和物理盘区之间的映射。LVM 逻辑设备向上层应用提供了和物理磁盘相同的功能,如文件系统的创建和数据的访问等

基本术语准备

  • 物理存储介质(PhysicalStorageMedia)
    指系统的物理存储设备:磁盘,是存储系统最底层的存储单元

  • 物理卷(Physical Volume,PV)
    物理卷是由磁盘分区转化而来

  • 卷组(Volume Group,VG)
    类似于非 LVM 系统中的物理磁盘,其由一个或多个物理卷 PV 组成。可以在卷组上创建一个或多个 LV(逻辑卷)

  • 逻辑卷(Logical Volume,LV)
    类似于非 LVM 系统中的磁盘分区,逻辑卷建立在卷组 VG 之上。在逻辑卷 LV 之上可以建立文件系统

  • 物理块(Physical Extent,PE)
    PE 是物理卷 PV 的基本划分单元,具有唯一编号的 PE 是可以被 LVM 寻址的最小单元。PE 的大小是可配置的,默认为 4MB。所以物理卷(PV)由大小等同的基本单元 PE 组成

  • 逻辑块(Logical Extent,LE)
    逻辑卷 LV 也被划分为可被寻址的基本单位,称为 LE。在同一个卷组中,LE 的大小和 PE 是相同的,并且一一对应

物理卷:还是/dev/sda、/dev/sdb 等,名称系统分配

逻辑卷:/dev/vg0/lv1 等,名称自定义

实现过程

1. 安装 lvm2 包

1
[root@centos7 ~]# yum -y install lvm2

2. 将设备指定为物理卷

  • pvs 简要显示 pv 信息
  • pvdisplay 详细显示 pv 信息
  • pvcreate /dev/DEVICE 创建 pv
  • pvremove /dev/DEVICE 删除 pv
  • pvmove 搬移 PV 中的资料(只限于同一 VG 中)
    • pvmove /dev/hda5 /dev/hda6 将 VG 中 pv hda5 的内容搬移到 hda6 中
    • pvmove /dev/hda5 也可以这样,lvm 决定 hda5 的内容被复制到哪里
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
41
42
43
44
45
46
47
48
[root@centos7 ~]# pvcreate /dev/sdb
WARNING: dos signature detected on /dev/sdb at offset 510. Wipe it? [y/n]: y
Wiping dos signature on /dev/sdb.
Physical volume "/dev/sdb" successfully created.
[root@centos7 ~]# pvcreate /dev/sdc
Physical volume "/dev/sdc" successfully created.
[root@centos7 ~]# lsblk -f
NAME FSTYPE LABEL UUID MOUNTPOINT
sda
|-sda1 xfs 25ea1f67-ee88-44b1-935e-8af77eadf856 /boot
|-sda2 xfs 28eb957c-1e98-4175-9a00-3efdef247355 /
|-sda3 xfs 91664c04-1540-4e35-8711-c3f183a556ca /data
|-sda4
|-sda5 swap a1966abb-acf9-4f53-8a86-733a93c2d576 [SWAP]
|-sda6
`-sda7
sdb LVM2_mem F4zIYp-IM1g-gJS6-ktqS-OZOD-u46a-z6YrQd
sdc LVM2_mem rAL8Wi-ln8P-LmUK-FaQn-40Mk-iGw4-ocXIUe
sr0 iso9660 CentOS 7 x86_64 2020-04-22-00-51-40-00 /data/repo/
[root@centos7 ~]# pvs
PV VG Fmt Attr PSize PFree
/dev/sdb lvm2 --- 10.00g 10.00g
/dev/sdc lvm2 --- 5.00g 5.00g
[root@centos7 ~]# pvdisplay
"/dev/sdb" is a new physical volume of "10.00 GiB"
--- NEW Physical volume ---
PV Name /dev/sdb
VG Name
PV Size 10.00 GiB
Allocatable NO
PE Size 0
Total PE 0
Free PE 0
Allocated PE 0
PV UUID F4zIYp-IM1g-gJS6-ktqS-OZOD-u46a-z6YrQd

"/dev/sdc" is a new physical volume of "5.00 GiB"
--- NEW Physical volume ---
PV Name /dev/sdc
VG Name
PV Size 5.00 GiB
Allocatable NO
PE Size 0
Total PE 0
Free PE 0
Allocated PE 0
PV UUID rAL8Wi-ln8P-LmUK-FaQn-40Mk-iGw4-ocXIUe

3. 用一个或多个物理卷来创建一个卷组

  • vgs 显示卷组
  • vgdisplay 显示卷组
  • vgcreate 卷组名 物理卷列表 创建卷组
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
# 指定PE大小为16M,默认4M
[root@centos7 ~]# vgcreate -s 16M vg0 /dev/sdb /dev/sdc
Volume group "vg0" successfully created
[root@centos7 ~]# vgs
VG #PV #LV #SN Attr VSize VFree
vg0 2 0 0 wz--n- <14.97g <14.97g
[root@centos7 ~]# vgdisplay
--- Volume group ---
VG Name vg0
System ID
Format lvm2
Metadata Areas 2
Metadata Sequence No 1
VG Access read/write
VG Status resizable
MAX LV 0
Cur LV 0
Open LV 0
Max PV 0
Cur PV 2
Act PV 2
VG Size <14.97 GiB
PE Size 16.00 MiB
Total PE 958
Alloc PE / Size 0 / 0
Free PE / Size 958 / <14.97 GiB
VG UUID zszcPb-urGs-jKX8-ZOCu-Xlb8-JPrD-b5bVt8
  • vgextend 卷组名 物理卷列表 向卷组中添加物理卷
  • vgreduce 卷组名 物理卷列表 从卷组中删除物理卷
  • vgremove 卷组名 删除卷组
    • 删除卷组之前应该先使用 pvmove 将数据迁移

4. 在卷组上创建逻辑卷

  • lvs

  • lvdisplay

  • lvcreate 创建逻辑卷

    • n 后面跟逻辑卷名
    • s 创建快照
    • l 指定逻辑卷的大小(LE 数)
      1
      2
      3
      4
      5
      # 卷组vg0分配100个PE给新建逻辑卷lv0,这样lv0的大小就是100个LE
      [root@centos7 ~]# lvcreate -l 100 -n lv0 vg0
      # 其他范例
      lvcreate -l 60%VG -n mylv testvg
      lvcreate -l 100%FREE -n yourlv testvg
    • L 指定逻辑卷的大小,单位为“kKmMgGtT”
      1
      2
      # 卷组vg0分配1G给新建逻辑卷lv1
      [root@centos7 ~]# lvcreate -L 1G -n lv1 vg0
  • lvremove 逻辑卷 删除逻辑卷

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    [root@centos7 ~]# lvs
    LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
    lv0 vg0 -wi-a----- 1.56g
    lv1 vg0 -wi-a----- 1.00g
    lv2 vg0 -wi-a----- <12.41g
    [root@centos7 ~]# lvremove lv2
    Volume group "lv2" not found
    Cannot process volume group lv2
    [root@centos7 ~]# lvremove /dev/vg0/lv2
    Do you really want to remove active logical volume vg0/lv2? [y/n]: y
    Logical volume "lv2" successfully removed
    [root@centos7 ~]# lvs
    LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
    lv0 vg0 -wi-a----- 1.56g
    lv1 vg0 -wi-a----- 1.00g
  • lvextend 扩展逻辑卷

    • -L [+]kKmMgGtT 没有+表示扩展至指定大小,有+表示增加指定大小
    • -l [+]Number[PERCENT]
    • -r 如果逻辑卷上已经有文件系统,同步扩展文件系统
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 两步实现:
    # 1. 实现逻辑卷的空间扩展
    lvextend -L [+]#[mMgGtT] /dev/VG_NAME/LV_NAME
    # 2. 实现文件系统的扩展
    resize2fs /dev/VG_NAME/LV_NAME # 针对ext
    # 或:
    xfs_growfs MOUNTPOINT # 针对xfs

    # 一步实现
    lvresize -r -l +100%FREE /dev/VG_NAME/LV_NAME
    1
    [root@centos7 ~]# lvextend -r -L +1G /dev/vg0/lv1
  • lvreduce 缩减逻辑卷,只针对 ext 文件系统,xfs 不支持缩减,缩减有风险,最好先备份数据

    • -l 同 lvextend
    • -L 同 lvextend

5. 在逻辑卷上创建文件系统并挂载

1
2
mkfs.ext4 /dev/vg0/lv1
mount /dev/vg0/lv1 /mnt/lv1/

6. 跨主机迁移卷组

  1. 在旧系统中,umount 所有卷组上的逻辑卷

  2. 禁用卷组

    vgchange 用于修改卷组的属性,经常被用来设置卷组是处于活动状态或者非活动状态。

1
2
vgchange –a n vg0
lvdisplay
  1. 导出卷组
1
2
3
vgexport vg0
pvscan
vgdisplay
  1. 拆下旧硬盘在目标计算机上,并导入卷组
1
vgimport vg0
  1. 启用
1
vgchange –ay vg0
  1. mount 所有卷组上的逻辑卷

7. 拆除指定的 pv 存储设备

  1. pvmove 迁移物理卷数据,注意只能迁移到同一卷组下的其他物理卷
  2. vgreduce 从卷组中移除物理卷
  3. pvremove 从机器上移除物理卷

逻辑卷快照

范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
mkfs.xfs /dev/vg0/data
mount /dev/vg0/data/ /mnt/data

#为现有逻辑卷创建快照,快照大小一般指定为十分之一
# -p|--permission rw|r
lvcreate -l 64 -s -n data-snapshot -p r /dev/vg0/data

#挂载快照
mkdir -p /mnt/snap
mount -o ro,nouuid /dev/vg0/data-snapshot /mnt/snap # -o ro,nouuid 只读,不校验重复uuid

#恢复快照
umount /dev/vg0/data-snapshot
umount /dev/vg0/data
lvconvert --merge /dev/vg0/data-snapshot

#删除快照
umount /mnt/snap
lvremove /dev/vg0/data-snapshot

软件运行环境

POSIX

POSIX:Portable Operating System Interface 可移植操作系统接口,定义了操作系统应该为应用程序
提供的接口标准,是 IEEE 为要在各种 UNIX 操作系统上运行的软件而定义的一系列 API 标准的总称。
Linux 和 windows 都要实现基本的 posix 标准,程序就在源代码级别可移植了

C 语言程序的实现过程

源代码->预处理->编译->汇编->链接

预处理:略
编译:将 C 转译成汇编
汇编:将汇编转译成机器指令
链接:调用链接器 ld 来链接程序运行需要的一大堆目标文件,以及所依赖的其它库文件,最后生成可执行文件

静态链接和动态链接

ldd

查看二进制程序所依赖的库文件

1
2
3
4
5
6
7
8
lujinkai@Z510:~$ ldd /bin/ls
linux-vdso.so.1 (0x00007fff7ccf9000)
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007ffadd6d3000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffadd2e2000)
libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007ffadd070000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007ffadce6c000)
/lib64/ld-linux-x86-64.so.2 (0x00007ffaddb1d000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007ffadcc4d000)

动态库路径的配置文件:

1
2
/etc/ld.so.conf
/etc/ld.so.conf.d/*.conf

将配置文件规定的路径下的库文件加载到缓存文件(字典)

1
ldconfig

缓存文件(字典)

1
2
3
4
5
lujinkai@Z510:~$ ll /etc/ld.so.cache
-rw-r--r-- 1 root root 81473 Aug 14 08:47 /etc/ld.so.cache
lujinkai@Z510:~$ file !$
file /etc/ld.so.cache
/etc/ld.so.cache: data

查看缓存文件(字典)
显示所有可用库文件名及文件路径映射关系

1
ldconfig -p

程序执行时会去 ld.so.cache 文件查找动态库的信息,所以如果有新的库文件,需要先执行 ldconfig 将新的库文件加载到 ld.so.cache

软件包

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
[root@centos7 /]# yum install -y autofs
[root@centos7 /]# systemctl start autofs
[root@centos7 /]# cd /misc/cd
[root@centos7 cd]# ls
CentOS_BuildTag GPL LiveOS RPM-GPG-KEY-CentOS-7
EFI images Packages RPM-GPG-KEY-CentOS-Testing-7
EULA isolinux repodata TRANS.TBL
[root@centos7 cd]# cd Packages/
[root@centos7 Packages]# ls
acl-2.2.51-15.el7.x86_64.rpm
aic94xx-firmware-30-6.el7.noarch.rpm
aide-0.15.1-13.el7.x86_64.rpm
alsa-firmware-1.0.28-2.el7.noarch.rpm
alsa-lib-1.1.8-1.el7.x86_64.rpm
alsa-tools-firmware-1.1.0-1.el7.x86_64.rpm
...
...

# 预览包内文件
[root@centos7 Packages]# rpm2cpio zip-3.0-11.el7.x86_64.rpm | cpio -itv
-rwxr-xr-x 1 root root 215840 Nov 6 2016 ./usr/bin/zip
-rwxr-xr-x 1 root root 100456 Nov 6 2016 ./usr/bin/zipcloak
-rwxr-xr-x 1 root root 95984 Nov 6 2016 ./usr/bin/zipnote
-rwxr-xr-x 1 root root 100096 Nov 6 2016 ./usr/bin/zipsplit
drwxr-xr-x 2 root root 0 Nov 6 2016 ./usr/share/doc/zip-3.0
-rw-r--r-- 1 root root 210354 Jul 6 2008 ./usr/share/doc/zip-3.0/CHANGES
-rw-r--r-- 1 root root 3412 Mar 4 2007 ./usr/share/doc/zip-3.0/LICENSE
-rw-r--r-- 1 root root 12748 Jun 26 2008 ./usr/share/doc/zip-3.0/README
-rw-r--r-- 1 root root 6430 Mar 26 2008 ./usr/share/doc/zip-3.0/README.CR
-rw-r--r-- 1 root root 6675 Jun 13 2008 ./usr/share/doc/zip-3.0/TODO
-rw-r--r-- 1 root root 15731 Jul 2 2008 ./usr/share/doc/zip-3.0/WHATSNEW
-rw-r--r-- 1 root root 13167 Jun 13 2008 ./usr/share/doc/zip-3.0/WHERE
-rw-r--r-- 1 root root 3395 Dec 14 1996 ./usr/share/doc/zip-3.0/algorith.txt
-rw-r--r-- 1 root root 28496 Jun 17 2008 ./usr/share/man/man1/zip.1.gz
-rw-r--r-- 1 root root 951 May 8 2008 ./usr/share/man/man1/zipcloak.1.gz
-rw-r--r-- 1 root root 819 Nov 6 2016 ./usr/share/man/man1/zipnote.1.gz
-rw-r--r-- 1 root root 619 Nov 6 2016 ./usr/share/man/man1/zipsplit.1.gz
1598 blocks

范例:统计 rpm 的架构类型及相应的包管理器

1
2
3
4
5
6
7
8
9
[root@centos7 Packages]# ls *.rpm | sed -En "s/.*\.(.*)\.rpm/\1/p" | sort | uniq -c | sort -nr
381 x86_64
65 noarch
[root@centos7 Packages]# ls *.rpm | rev | cut -d. -f2 | rev | sort | uniq -c | sort -nr
381 x86_64
65 noarch
[root@centos7 Packages]# ls *.rpm | grep -Eo "[^.]+\.rpm$" | grep -Eo "^[^.]+" | sort | uniq -c | sort -nr
381 x86_64
65 noarch

包管理器

  • redhat:rpm 文件,rpm 包管理器
  • debian:deb 文件,dpkg 包管理器

/var/lib/rpm

当安装完一个包后,包的信息会存储到一个数据库中,这数据库中信息是所有包共享的,所以叫共享数据库:/var/lib/rpm,这里面记录了当前主机上安装好的各种 rpm 包,包括它们的名称、版本、依赖关系、功能说明、包安装后生成的各文件路径及校验码信息。

这个数据库很重要,一旦破坏,就无法安装卸载软件,当使用 rpm 安装软件的时候,会查询这个数据库,如果已经安装,就提示已安装。

获取包的途径

光盘:
http://mirrors.163.com/centos/
https://mirrors.aliyun.com/centos/
http://mirrors.163.com/ubuntu-releases/
https://mirrors.aliyun.com/ubuntu-releases/

包管理器 rpm

安装、卸载、升级、查询、校验、数据库维护

安装

1
2
3
4
5
rpm -ivh PACKAGE_FILE...

-i # install
-v # 显示详细的信息
-h # 显示进度

卸载

1
2
3
4
rpm -e PACKAGE_FILE... [--nodeps]

-e # 卸载
--nodeps # 忽略依赖关系

升级

1
2
3
4
5
6
rpm -Uvh PACKAGE_FILE...
rpm -Fvh PACKAGE_FILE...

-U # upgrade 例如要升级php,如果本机已安装php,则升级,如果本机没有php,则安装
-F # freshen 例如要升级php,如果本机已安装php,则升级,如果本机没有php,则不执行任何操作
--force # 强制安装

注意:

  1. 不要对内核做升级操作;Linux 支持多内核并存,因此直接安装新版本内核
  2. 如果原程序的配置文件被修改,升级时,新版本提供的同以配置文件不会直接覆盖,而是把新版本的配置文件重命名(filename.rpmnew)后保留

降级

在升级的命令上添加 –oldpackage 即可

1
2
rpm -Uvh --oldpackage PACKAGE_FILE...
rpm -Fvh --oldpackage PACKAGE_FILE...

查询

1
rpm {-q|--query} [select-options] [query-options]

常用查询语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-q PACKAGE
-qa | grep 'pattern'
-qi PACKAGE
-qc PACKAGE
-ql PACKAGE
-qd PACKAGE
-q --scripts PACKAGE
-qf file
-qpi PACKAGE_FILE
-qpl PACKAGE_FILE, ...

-a # 所有已安装的包
-f # 文件是由哪个包安装生成的
-p rpmfile # 尚未安装的包文件,以下-c、-d、-i、-l、--script默认只查询已安装的包,如果要查询未安装的包的相关信息,需要配合-p选项
-c # 程序的配置文件
-d # 程序的文档
-i # information
-l # 安装后生成的所有文件
--scripts # 包自带的脚本文件

校验

占位。。。

数据库维护

/var/lib/rpm

yum 和 dnf

rpm 不能解决包的依赖关系,yum 和 dnf 可以,yum 适用 centos7,dnf 适用 centos8,以下只介绍 yum,dnf 也都一样

yum:服务器存放 rpm 包和包相关的元数据库

yum 实现过程:

先在 yum 服务器上创建 yum repository(仓库),在仓库中事先存储了众多 rpm 包,以及包的相关的元数据文件(放置于特定目录 repodata 下),当 yum 客户端利用 yum/dnf 工具进行安装时包时,会自动下载 repodata 中的元数据,查询远数据是否存在相关的包及依赖关系,自动从仓库中找到相关包下载并安装

yum 客户端配置

配置文件

1
2
/etc/yum.conf
/etc/yum.repos.d/*.repo

/etc/yum.conf 这个文件不用动,如果要改,就改 /etc/yum.repos.d/

/etc/yum.repos.d/*.repo,每个 repo 文件都记录了不同的仓库

CentOS-Base.repo

访问 mirrorlist,返回的是多个 baseurl,所以可以不设置 mirrorlist,只设置 baseurl,可以设置多个 baseurl

1
2
3
4
5
6
7
8
9
10
11
12
failovermethod    # 随机访问baseurl还是顺序访问
roundrobin # 意为随机挑选,默认值
priority # 按顺序访问
enable # 开启或禁用,不写默认就是开启
gpgcheck # 是否需要校验,校验的key就是gpgkey
gpgkey # 校验的key

$releasever # 当前OS的发行版的主版本号,如:8,7,6
$arch # CPU架构,如:aarch64, i586, i686,x86_64等
$basearch # 系统基础平台;i386, x86_64
$contentdir # 表示目录,比如:centos-8,centos-7
$YUM0-$YUM9 # 自定义变量

什么是 epel?
extra packages for enterprise linux,是基于 Fedora 的一个项目,为“红帽系”的操作系统提供额外的软件包,适用于 RHEL、CentOS 和 Scientific Linux,可以理解为是一个第三方源

常见 baseurl

centos

1
2
3
4
http://mirrors.aliyun.com/centos/$releasever/os/$basearch/
http://mirrors.163.com/centos/$releasever/os/$basearch/
http://mirrors.huaweicloud.com/centos/$releasever/os/$basearch/
http://mirrors.tuna.tsinghua.edu.cn/centos/$releasever/os/$basearch/

epel centos7

1
2
3
http://mirrors.aliyun.com/epel/7/$basearch
http://mirrors.huaweicloud.com/epel/7/$basearch
http://mirrors.tuna.tsinghua.edu.cn/epel/7/$basearch

epel centos8

1
2
3
http://mirrors.aliyun.com/epel/8/Everything/$basearch
http://mirrors.huaweicloud.com/epel/8/Everything/$basearch
http://mirrors.tuna.tsinghua.edu.cn/epel/8/Everything/$basearch

注意:yum 仓库指向的路径一定必须是 repodata 目录所在目录

yum 命令

1
yum [options] [command] [package ...]

options

1
2
3
4
5
6
7
-y                     #自动回答为“yes”
-q #静默模式
--nogpgcheck #跳过gpg check
--enablerepo=<repoid> #临时启用指定repo,支持通配符,如:"*"
--disablerepo=<repoid> #临时禁用指定repo,支持通配符,如:"*",和--repo互斥
--noplugins #禁用所有插件
--showduplicates #显示仓库中所有的包,主要与list和search等子命令搭配使用

command

在 centos8 中(centos7/6 不行),yum 然后 tab 可以列出 yum 部分常用的子命令,注意不是全部

详细的命令解析参考man dnfman yum,但是 man 手册的内容太多了,我们只记 yum 的常用用法就可以了,毕竟 yum 只是用来解决编译安装的依赖问题。

以下是常用的子命令:

repolist

显示仓库列表

1
yum repolist [all|enabled|disabled]

list

显示程序包

1
2
3
yum list
yum list [all | glob_exp1] [glob_exp2] [...]
yum list {available|installed|updates} [glob_exp1] [...]

install and reinstall

安装和重新安装程序包

1
2
yum install package1 [package2] [...] # 安装
yum reinstall package1 [package2] [...] # 重新安装

–downloadonly:只下载相关的 rpm 包,默认下载到 var/cache/yum/x86_64/7 目录下,不执行安装动作
–downloaddir=,–destdir=:配合–downloadonly,指定下载的路径,如果不存在则自动创建

remove or erase

卸载程序包

1
yum remove | erase package1 [package2] [...]

update and downgrade

检查可用升级

1
yum check-update

升级和降级

1
2
3
yum upgrade|update [package1] [package2] [...]
yum upgrade-minimal # 最小化升级
yum downgrade package1 [package2] [...] # 降级

查看包的详细信息

info、provides|whatprovides、search、deplist、repoquery

1
yum info [...]

查看指定的特性(可以是某文件)是由哪个程序包提供的

1
yum provides | whatprovides feature1 [feature2] [...]

主义文件要写全路径,而不是只是文件名,可以使用通配符,例如:yum provides */vsftpd.conf

以指定的关键字搜索程序包及 summary 信息

1
yum search string1 [string2] [...]

查看指定包所依赖的 capabilities:

1
yum deplist package1 [package2] [...]

扩展: centos8 查看未安装包的文件列表

1
2
dnf repoquery package
dnf repoquery -l package

扩展: centos7 查看未安装包的文件列表

1
2
3
yum -y install yum-utils
repoquery -q package
repoquery -ql package

clean and makecache

清除/var/cache/yum 缓存

1
yum clean [ packages | metadata | expire-cache | rpmdb | plugins | all ]

构建缓存

1
yum makecache

范例:

1
[root@centos7 ~]# yum clean all; yum makecache

history

事务历史

centos7 及以前的版本日志

1
/var/log/yum.log

centos8 版本日志

1
2
/var/log/dnf.rpm.log
/var/log/dnf.log
1
yum history [info|list|packages-list|packages-info|summary|addon-info|redo|undo|rollback|new|sync|stats]

范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@centos8 ~]# dnf history
ID | Command line | Date and time | Action(s) | Altered
-------------------------------------------------------------------------------
4 | install lrzsz | 2020-08-15 09:58 | Install | 1
3 | install -y autofs | 2020-08-15 09:05 | Install | 1
2 | install vim wget -y | 2020-08-15 08:03 | Install | 5
1 | | 2020-07-29 05:12 | Install | 455 EE
[root@centos8 ~]# dnf history info 4
Transaction ID : 4
Begin time : Sat 15 Aug 2020 09:58:11 AM EDT
Begin rpmdb : 455:2fec96053059825d1e35cd07ef7f27130b90a171
End time : Sat 15 Aug 2020 09:58:11 AM EDT (0 seconds)
End rpmdb : 456:d9d289d9345ad932d7563415cf622772184a6e92
User : root <root>
Return-Code : Success
Releasever : 8
Command Line : install lrzsz
Packages Altered:
Install lrzsz-0.12.20-43.el8.x86_64 @BaseOS

localinstall or install

安装及升级本地程序包

1
2
yum localinstall|install rpmfile1 [rpmfile2] [...]
yum localupdate|update rpmfile1 [rpmfile2] [...]

查看包的安全警告

1
yum updateinfo --summary|--list|--info

包组管理相关

groupinstall、groupupdate、grouplist、groupremove、groupinfo

什么是软件包组?
简单说就是功能相对集中的软件包集合,例如,在初始安装 Linux 时没有安装图形界面,但后来发现需要图形界面的支持,这时可以手工安装图形界面软件组(Server with GUI、GNOME),这样就可以支持图形界面了。

1
2
3
4
5
yum grouplist [hidden] [groupwildcard] [...]
yum groupinstall group1 [group2] [...]
yum groupupdate group1 [group2] [...]
yum groupremove group1 [group2] [...]
yum groupinfo group1 [...]

实现私有 yum 仓库

服务端:

  1. 将远程仓库的 rpm 包和 meta 同步到本地
  2. 安装 http 或 https 或 ftp 或 file 服务,使客户端可以访问

客户端:

  1. 修改配置文件,将远程仓库地址修改为私有的仓库地址

具体实现:

服务端

服务端环境为 CentOS7,配置 yum 和 apt 私有仓库

  1. 创建目录
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
[root@centos7 mirrors]# pwd
/data/mirrors
[root@centos7 mirrors]# tree
.
├── centos
│   ├── 6
│   │   └── x86_64
│   ├── 7
│   │   └── x86_64
│   └── 8
│   └── x86_64
├── epel
│   ├── 6
│   │   └── x86_64
│   ├── 7
│   │   └── x86_64
│   └── 8
│   └── x86_64
└── ubuntu
├── 18.04
│   └── x86_64
└── 20.04
└── x86_64

19 directories, 0 files
  1. 安装 ngnix,配置路径
1
2
3
4
5
6
location /mirrors {
root /data;
autoindex on;
autoindex_exact_size on;
autoindex_localtime on;
}

  1. 配置服务端的 yum 远程仓库国内的镜像源
    centos7
1
2
3
4
5
6
7
8
9
10
11
# /etc/yum.repos.d/tmp.repo
[base]
name=base repo
baseurl=http://mirrors.aliyun.com/centos/$releasever/os/$basearch/
http://mirrors.163.com/centos/$releasever/os/$basearch/
http://mirrors.huaweicloud.com/centos/$releasever/os/$basearch/
http://mirrors.tuna.tsinghua.edu.cn/centos/$releasever/os/$basearch/
failovermethod=priority
enabled=1
gpgcheck=0 # 方便下载,先禁止校验
# gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7

ubuntu18.04

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse

deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse

deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse

deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse

deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse

  1. 将远程仓库的 rpm 包和 meta 同步到本地
    centos 使用 reposync,centos8 的 dnf 集成,centos7 来自 yum-utils;ubuntu 使用 apt-mirror,yum 无法安装这个命令,所以可以在 ubuntu 的机器上同步完后,拷贝到 centos 的机器上。如果服务端的环境是 ubuntu,apt 则可以直接安装 apt-mirror 和 reposync 命令。
1
dnf reposync --repoid=epel --download-metadata -p /data/mirrors/epel/7/x86_64

以上命令是把整个 epel 仓库同步过来,大约有一万多个包。
如果想自定义仓库,控制仓库中有哪些包,以及每个包的版本,可以使用yum install --downloadonly下载
指定的 rpm 包,或者去官网等渠道下载想要的版本的包,将这些包放到指定目录下:/data/mirror/centos{6,7,8}/x86_64
然后执行createrepo /data/mirror/centos{6,7,8}/x86_64即可生产元数据 repodata,当仓库的包有变动,重新执行createrepo命令即可

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
41
42
43
44
45
46
47
48
[root@centos7 x86_64]#yum install --downloadonly --downloaddir=./ php mysql nginx httpd
[root@centos7 x86_64]#ll
total 17724
-rw-r--r--. 1 root root 105968 Aug 23 2019 apr-1.4.8-5.el7.x86_64.rpm
-rw-r--r--. 1 root root 94132 Jul 4 2014 apr-util-1.5.2-6.el7.x86_64.rpm
-rw-r--r--. 1 root root 2843664 Apr 4 04:53 httpd-2.4.6-93.el7.centos.x86_64.rpm
-rw-r--r--. 1 root root 94308 Apr 4 04:53 httpd-tools-2.4.6-93.el7.centos.x86_64.rpm
-rw-r--r--. 1 root root 49644 Jul 4 2014 libzip-0.10.1-8.el7.x86_64.rpm
-rw-r--r--. 1 root root 31264 Jul 4 2014 mailcap-2.1.41-2.el7.noarch.rpm
-rw-r--r--. 1 root root 9169616 Apr 4 05:01 mariadb-5.5.65-1.el7.x86_64.rpm
-rw-r--r--. 1 root root 575413 Oct 4 2019 nginx-1.16.1-1.el7.x86_64.rpm
-rw-r--r--. 1 root root 19857 Oct 4 2019 nginx-all-modules-1.16.1-1.el7.noarch.rpm
-rw-r--r--. 1 root root 30445 Oct 4 2019 nginx-mod-http-image-filter-1.16.1-1.el7.x86_64.rpm
-rw-r--r--. 1 root root 39849 Oct 4 2019 nginx-mod-http-perl-1.16.1-1.el7.x86_64.rpm
-rw-r--r--. 1 root root 29613 Oct 4 2019 nginx-mod-http-xslt-filter-1.16.1-1.el7.x86_64.rpm
-rw-r--r--. 1 root root 58301 Oct 4 2019 nginx-mod-mail-1.16.1-1.el7.x86_64.rpm
-rw-r--r--. 1 root root 86465 Oct 4 2019 nginx-mod-stream-1.16.1-1.el7.x86_64.rpm
-rw-r--r--. 1 root root 1421660 Apr 4 05:04 php-5.4.16-48.el7.x86_64.rpm
-rw-r--r--. 1 root root 2880032 Apr 4 05:04 php-cli-5.4.16-48.el7.x86_64.rpm
-rw-r--r--. 1 root root 578968 Apr 4 05:04 php-common-5.4.16-48.el7.x86_64.rpm
[root@centos7 x86_64]#createrepo ./
Spawning worker 0 with 17 pkgs
Workers Finished
Saving Primary metadata
Saving file lists metadata
Saving other metadata
Generating sqlite DBs
Sqlite DBs complete
[root@centos7 x86_64]#ll
total 17728
-rw-r--r--. 1 root root 105968 Aug 23 2019 apr-1.4.8-5.el7.x86_64.rpm
-rw-r--r--. 1 root root 94132 Jul 4 2014 apr-util-1.5.2-6.el7.x86_64.rpm
-rw-r--r--. 1 root root 2843664 Apr 4 04:53 httpd-2.4.6-93.el7.centos.x86_64.rpm
-rw-r--r--. 1 root root 94308 Apr 4 04:53 httpd-tools-2.4.6-93.el7.centos.x86_64.rpm
-rw-r--r--. 1 root root 49644 Jul 4 2014 libzip-0.10.1-8.el7.x86_64.rpm
-rw-r--r--. 1 root root 31264 Jul 4 2014 mailcap-2.1.41-2.el7.noarch.rpm
-rw-r--r--. 1 root root 9169616 Apr 4 05:01 mariadb-5.5.65-1.el7.x86_64.rpm
-rw-r--r--. 1 root root 575413 Oct 4 2019 nginx-1.16.1-1.el7.x86_64.rpm
-rw-r--r--. 1 root root 19857 Oct 4 2019 nginx-all-modules-1.16.1-1.el7.noarch.rpm
-rw-r--r--. 1 root root 30445 Oct 4 2019 nginx-mod-http-image-filter-1.16.1-1.el7.x86_64.rpm
-rw-r--r--. 1 root root 39849 Oct 4 2019 nginx-mod-http-perl-1.16.1-1.el7.x86_64.rpm
-rw-r--r--. 1 root root 29613 Oct 4 2019 nginx-mod-http-xslt-filter-1.16.1-1.el7.x86_64.rpm
-rw-r--r--. 1 root root 58301 Oct 4 2019 nginx-mod-mail-1.16.1-1.el7.x86_64.rpm
-rw-r--r--. 1 root root 86465 Oct 4 2019 nginx-mod-stream-1.16.1-1.el7.x86_64.rpm
-rw-r--r--. 1 root root 1421660 Apr 4 05:04 php-5.4.16-48.el7.x86_64.rpm
-rw-r--r--. 1 root root 2880032 Apr 4 05:04 php-cli-5.4.16-48.el7.x86_64.rpm
-rw-r--r--. 1 root root 578968 Apr 4 05:04 php-common-5.4.16-48.el7.x86_64.rpm
drwxr-xr-x. 2 root root 4096 Aug 18 15:33 repodata

客户端

  1. 修改配置文件 /etc/yum.repos.d/base.repo
1
2
3
4
5
6
7
[base]
name=base repo
baseurl=http://10.0.0.100/mirrors/centos/7/x86_64
failovermethod=priority
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
  1. 重新生产缓存
1
yum clean all; yum makecache

Ubuntu 软件管理

Ubuntu 下,dpkg 类似 rpm,apt 类似 yum

dpkg 常见用法

安装

1
dpkg -i package.deb

卸载,不建议,不自动卸载依赖于它的包

1
2
dpkg -r package
dpkg -P package # 同时删除配置文件

列出当前已安装的包

1
dpkg -l

显示该包的简要说明

1
dpkg -l package

列出该包的状态,包括详细信息,类似 rpm -qi

1
dpkg -s package

列出该包中所包含的文件,类似 rpm -ql

1
dpkg -L package

搜索包含 pattern 的包,类似 rpm –qf

1
dpkg -S <pattern>

列出 deb 包的内容,类似 rpm –qpl

1
dpkg -c package.deb

解开 deb 包的内容

1
dpkg --unpack package.deb

apt

apt 与 apt-get 有一些类似的命令选项,但它并不能完全向下兼容 apt-get 命令,也即可用 apt 替换部分 apt-get 系列命令,但不是全部

apt 命令 被取代的命令 命令的功能
apt install apt-get install 安装软件包
apt remove apt-get remove 移除软件包
apt purge apt-get purge 移除软件包及配置文件
apt update apt-get update 刷新存储库索引
apt upgrade apt-get upgrade 升级所有可升级的软件包
apt autoremove apt-get autoremove 自动删除不需要的包
apt full-upgrade apt-get dist-upgrade 在升级软件包时自动处理依赖关系
apt search apt-cache search 搜索应用程序
apt show apt-cache show 显示安装细节

卸载软件包:

1
2
3
4
sudo apt purge package
sudo apt autoremove
sudo apt autoclean
# dpkg -l |grep ^rc|awk '{print $2}' |sudo xargs dpkg -P

删除缓存

1
2
3
sudo apt-get autoclean
sudo apt-get autoremove
sudo apt-get clean

locate

find 去硬盘搜索,locate 是去数据库搜索,系统预建的文件索引数据库:/var/lib/mlocate/mlocate.db
执行updatedb更新数据库,这个命令构建索引的过程需要遍历整个根文件系统,很消耗 io 资源,所以在服务器比较繁忙的时候不要执行

1
2
3
4
5
locate [OPTION]... [PATTERN]...

-i # 不区分大小写的搜索
-n N # 只列举前N个匹配项目
-r # 使用基本正则表达式

支持模糊搜索:

1
2
3
4
5
6
lujinkai@Z510:~/data/test$ locate `pwd`/*sh
/home/lujinkai/data/test/a.sh
/home/lujinkai/data/test/b.sh
/home/lujinkai/data/test/c_r.sh
/home/lujinkai/data/test/test.sh
/home/lujinkai/data/test/test2.sh

find

locate 查询的不是实时数据,find 查询的是实时的,因为 find 是去遍历指定路径完成文件查找

1
find [OPTION]... [path...] [expression]

OPTION

1
2
3
-P   # 找到符号链接的时候,所有的属性都来自符号链接,从来不follow symbolic links。这是默认选项
-L # 找到符号链接的时候,所有的属性来自文件本身,总是follow symbolic links。而不是符号链接
-H # 仅仅当作为命令行参数时,也就是这里的[path...],才会follow symbolic links

expression

描述了如何匹配文件以及如何处理匹配的文件。一个 expression 是由以下多个选项组成:

1
2
3
4
5
- Tests
- Actions
- Global options
- Positional options
- Operators

如果整个 expression 没有包含任何选项,默认 -print

-delete 也类似一个选项,因为它隐含 -depth 选项

Positional options 位置选项

位置选项总是返回 true。位置选项会影响所有指定的 Tests,哪怕 Tests 写在前面(除了-daystart,-daystart 只影响写在其后面的 Tests),为了易读性,最好把位置选项放在 expression 的开头部分

  • -daystart
    指定每天的开始是 0 点而不是 24 小时之前,例如:当前时间是 8 月 13 日 8 点,要求搜索 2 天内修改过的文件,默认搜索文件的起点是 8 月 11 日 8 点,如果使用-daystart 选项,则搜索文件的起点是 8 月 11 日 0 点

    影响的 Tests:-amin,-atime,-cmin,-ctime,-mmin、-mtime

  • -follow
    这个选项已经被弃用,使用-L 代替

  • -regextype type
    不常用,先略过。。。

    影响的 Tests:-regex、-iregex

  • -warn,-nowarn
    打开或关闭警告信息,具体看 man 帮助

Global options 全局选项

Global options 也总是返回 true,对所有的

  • -d
    已弃用,等同于-depth 的,与 FreeBSD、NetBSD、MacOSX 和 OpenBSD 兼容.

  • -depth
    搜索到目录时,先处理目录中的文件,再处理目录本身。对于-delete 这个 action,它隐含-depth 选项

  • -ignore_readdir_race
    在 find 统计文件的时候,如果有其他操作把这文件删了,会报错,加上这个选项就不报错了

  • -noignore_readdir_race
    关闭-ignore_readdir_race.

  • -maxdepth levels
    最大搜索目录深度,指定目录下的文件为第 1 级

  • -mindepth levels
    最小搜索目录深度

  • -mount
    不搜索其他文件系统,-xdev 的替代名称,用于与 find 的其他版本兼容

  • -xdev
    不搜索其他文件系统

  • -noleaf
    禁止在非 UNUX 文件系统,MS-DOS 系统,CD-ROM 文件系统中进行最优化查找,具体什么意思没太看懂

  • -help,–help
    打印帮助信息,并且退出

  • -version
    打印 find 版本号然后退出

Tests 测试

关于选项的参数是数字的,n 表示等于 n,+n 表示大于 n,-n 表示小于 n,例如:-uid 0表示要求 uid 等于 0,-uid +0要求 uid 大于 0

时间戳

n:n 个时间单位之内
+n:n 个时间单位之前
atime:access time,访问时间,文件中的数据库最后被访问的时间
mtime:modify time,修改时间,文件内容被修改的最后时间
ctime:change time,变化时间,文件的原数据,发生改变的时间

  • -amin n
    a:atime、min:minutes
    n:搜索 atime 在 n 分钟内有改变的文件
    +n:搜索 atime 在 n 分钟之前有改变的文件

  • -mmin n
    m:mtime

  • -cmin n
    c:ctime

  • -atime n、-ctime n、-mtime n
    和 amin、mmin、cmin 的区别在于时间单位为天,也就是 24 小时,从现在往前数 24×n 小时

  • -used n
    查找 atime 在 ctime 后 n 天的文件

  • -anewer file
    搜索 atime 比给定 file 的 mtime 还要晚的文件。换言之,在 file 的内容被修改后,又访问了哪些文件?

  • -cnewer file
    搜索 ctime 比给定 file 的 mtime 还要晚的文件。

  • -new file
    搜索 mtime 比给定文件的 mtime 还要晚的文件。

-newerXY reference
X 和 Y 是占位符,主要是查找 X 时间戳比 reference 的 Y 时间戳更新的文件。
reference 可以是时间字符串(参考 date -d 格式,推荐使用 yyyy-MM-dd hh:mm:ss),也可以是
X 可以是 a、B、c、m,Y 可以是 a、B、c、m、t。
a:atime;
B:birth time 文件的出生日期,不是所有系统都支持这个参数;
c:ctime;
t:代表 reference 本身,此时 reference 必须是时间,不能是文件,显然只有 Y 可以设为 t,X 设为 t 是没有意义的

范例:获取 mtime 为昨天的文件

1
2
3
4
5
6
lujinkai@Z510:~/data/test$ find . -newermt $(date -d '-1 day' +%F) ! -newermt $(date +%F)
./issue
./re.txt
lujinkai@Z510:~/data/test$ find . -newermt '2020-08-12 00:00:00' ! -newermt '2020-08-13 00:00:00'
./issue
./re.txt
文件名和 inode
  • -name pattern
    文件的 basename 与 pattern 匹配,支持使用通配符,注意,这里的 pattern 的 *、?、[] 是可以匹配.开头的文件,也就是隐藏文件(在 bash 中,是无法匹配的)

  • -iname pattern
    不区分大小写的 name

  • -lname pattern
    找找符合指定匹配模式的符号链接文件,该选项与-L 冲突

  • -ilname pattern
    不区分大小写的 lname

  • -inum n
    按照 inode 号查找

  • -samefile name
    找出指定文件同 inode 号的文件,即其硬链接

  • -links n
    File has n hard links.

  • -regex pattern
    -name 只能匹配 basename,-regex 可以匹配整个文件路径,匹配规则是正则表达式

  • -iregex pattern
    相比 regex,iregex 对匹配的内容不区分大小写

  • -path pattern
    和-regex pattern 相比,-path pattern 的匹配规则是通配符

  • -ipath pattern
    不区分大小写的 path

  • -wholename pattern
    不推荐使用,可以使用 -path 代替

  • -iwholename pattern
    不区分大小写的 wholename

属主、属组
  • -user uname
    uname 可以是用户名,也可以是 uid
  • -group gname
    gname 可以是组名,也可以是 gid
  • -uid n
    文件的属主 id 是 n
  • -gid n
    文件的属组 id 是 n
  • -nouser
    查找没有属主的文件
  • -nogroup
    查找没有属组的文件
文件类型
  • -type c
    c 可以是以下类型:
    b(block)、c(character)、d(directory)、p(FIFO)、f(regular file)、l(symbolic link)、s(socket)、D(门 (Solaris 特有))
    当搜索多个文件类型时,可以用逗号分割
空文件或目录
  • -empty
    查找空文件和空目录
文件大小
  • -size n[cwbkMG]
    c:1 字节、w:2 字节、b:512 字节(默认)、k:1k、M:1M、G:1G
    n 的前缀+和-表示大于和小于,对比目标文件时,对文件的 size 进行四舍五入,精度就是指定的 cwbkMG,例如:-size -1M并不等同于-size -1048576c。前者只匹配空文件,后者匹配 0 到 1048575 字节的文件。
权限
  • -perm mode
    精确匹配文件权限,必须提供全部的权限,不提供的默认此权限为 0,mode 推荐使用八进制模式,如果使用符号模式,可能会比较复杂,例如:-perm g=w只能匹配到模式为 0020 的文件

  • -perm /mode
    可以提供 1 个或多个权限,只要满足其中一个权限,就能匹配,例如:-perm /u=rwx,u 满足 rwx 中一项即可;-perm /222,ugo 中只要有一个具有写权限即可

  • -perm -mode
    可以提供 1 个或多个权限,无论提供多少权限,必须全部满足,才能匹配,例如:-perm /u=rwx,u 必须全部满足 rwx;-perm /222,ugo 都要具有写权限

  • -perm +mode
    已淘汰,使用 -perm /mode 代替

  • -writable
    匹配当前用户可写的文件

  • -readable
    匹配当前用户可读的文件。-perm 测试文件的 ugo 的权限,而-writable 和-readable 测试当前用户对文件的权限

其他
  • -executable
    查找可以被执行的文件(sh、bin 文件),和可以被搜索的目录

  • -true
    总是返回 true

  • -false
    总是返回 false

  • -fstype type
    指定文件系统的类型,本机的文件系统类型在/etc/fstab 中可以查看

  • -xtype c
    和“-type”相同,除非文件是符号链接。对于符号链接,-xtype 检查-type 不检查的文件的类型。
    具体什么意思,还不太清楚

  • -context pattern
    只限 SELinux,因为 SELinux 我们都会关掉,所以这个选项没什么用

ACTIONS 动作

  • -print
    默认的处理动作,输出内容到终端

  • -delete
    删除文件,删除成功返回 true,删除失败报错

  • -ls
    对处理后的内容执行类似ls -l的操作

  • -ok command {} ;
    对查找到的每个文件执行 command,对于每个文件执行命令之前,都会交互式要求用户确认,{}用于引用查找到的文件名称自身

  • -exec command {} ;
    对查找到的每个文件执行由 COMMAND 指定的命令,{}用于引用查找到的文件名称自身

  • -fls file
    查找到的所有文件的长格式信息保存至指定文件中

  • -prune
    如果该文件是一个目录,则不要下降到它,该选项和-depth 冲突,如果设置了-depth 选项,该选项失效

  • -quit

OPERATORS 操作

  • ( expr )
    强制优先

  • !expr
    取反

  • expr1 expr2
    隐含的“与“操作

  • expr1 -a expr2
    和“expr1 expr2“一样

  • expr1 -o expr2
    “或“操作

  • expr1 , expr2

替换参数 xargs

xargs 读取标准输入的内容,使用空格或回车(-n)将数据分割,可以直接输出,也可以作为参数传递给后面的命令

find 常常搭配 xargs 一起使用

1
find | xargs command

范例:

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
41
#显示10个数字
seq 10 | xargs
#删除当前目录下的大量文件
ls | xargs rm
#
find -name "*.sh" | xargs ls -Sl
[root@centos8 data]#echo {1..10} |xargs
1 2 3 4 5 6 7 8 9 10
[root@centos8 data]#echo {1..10} |xargs -n1
1
2
3
4
5
6
7
8
9
10
[root@centos8 data]#echo {1..10} |xargs -n2
1 2
3 4
5 6
7 8
9 10
#批量创建和删除用户
echo user{1..10} |xargs -n1 useradd
echo user{1..100} | xargs -n1 userdel -r
#这个命令是错误的
find /sbin/ -perm /700 | ls -l
#查找有特殊权限的文件,并排序
find /bin/ -perm /7000 | xargs ls -Sl
#此命令和上面有何区别?
find /bin/ -perm -7000 | xargs ls -Sl
#以字符nul分隔*
find -type f -name “*.txt” -print0 | xargs -0 rm
#并发执行多个进程
seq 100 |xargs -i -P10 wget -P /data
http://10.0.0.8/{}.html
#并行下载视频
seq 389 | xargs -i -P3 you-get https://www.bilibili.com/video/av36489007?p={}

练习

  1. 查找/var 目录下属主为 root,且属组为 mail 的所有文件
  2. 查找/var 目录下不属于 root、lp、gdm 的所有文件
  3. 查找/var 目录下最近一周内其内容修改过,同时属主不为 root,也不是 postfix 的文件
  4. 查找当前系统上没有属主或属组,且最近一个周内曾被访问过的文件
  5. 查找/etc 目录下大于 1M 且类型为普通文件的所有文件
  6. 查找/etc 目录下所有用户都没有写权限的文件
  7. 查找/etc 目录下至少有一类用户没有执行权限的文件
  8. 查找/etc/init.d 目录下,所有用户都有执行权限,且其它用户有写权限的文件

压缩和解压缩

除了 zip,所有的压缩都是压缩文件,不能打包目录。

.Z

压缩 compress

1
compress file

解压缩 uncompress

1
uncompress file.Z

.gz

压缩 gzip

1
gzip file

解压缩 gunzip

1
gunzip file.gz

.bz2

压缩 bzip2

1
bzip2 file

解压 bunzip2

1
bunzip2 file.bz2

.xz

压缩 xz

1
xz file

解压 unxz

1
unxz file.xz

.zip

压缩 zip

1
2
# 需要指定压缩后的名称file.zip
zip file.zip file

解压 unzip

1
2
# 如果解压的是文件,不是目录,需要指定解压后的名称file
unzip file.zip file

打包目录并压缩 zip -r

1
zip -r dir.zip dir

解压目录 unzip

1
2
3
4
# 解压目录可以指定解压后的名称,也可以不指定
unzip dir.zip
# 或
unzip dir.zip dir

打包和解包

tar

tar 即 Tape ARchive 磁带归档,可以对目录和多个文件打包一个文件,并且可以压缩,保留文件属性不丢失,常用于备份功能,推荐使用

对应格式是.tar

结合压缩工具,实现打包并压缩

  • -z gzip .tar.gz
  • -j bzip2 .tar.bz2
  • -J xz .tar.xz

打包

1
2
3
4
tar cvf dir.tar dir
tar zcvf dir.tar.gz dir
tar jcvf dir.tar.bz2 dir
tar Jcvf dir.tar.xz dir

解包

1
2
3
4
tar -xvf dir.tar
tar -zxvf dir.tar.gz
tar -jxvf dir.tar.bz2
tar -Jxvf dir.tar.xz

split

split 命令可以分割一个文件为多个文件

切割

1
2
3
4
5
6
#分割大的 tar 文件为多份小文件
split -b Size –d tar-file-name prefix-name

split -b 1M mybackup.tgz mybackup-parts
#切换成的多个小分文件使用数字后缀
split -b 1M –d mybackup.tgz mybackup-parts

合并

1
cat mybackup-parts* > mybackup.tar.gz

cpio

cpio 是历史悠久的打包和解包工具,不过目前很少使用了

判断某需求是否满足,一般做条件判断,搭配 if 和三元表达式使用。
若真,则状态码$?返回0
若假,则状态码$?返回 1

test 命令也可以简写为[]

1
2
3
test expression
# 或
[ expression ]

在 test 中使用变量建议使用双引号包裹

test 和[]都是命令,一个命令本质上对应一个程序或者一个函数。即使是一个程序,它也有入口函数,所以也可以将一个程序等效为一个函数,这样我们就不用在区分函数和程序了,直接讲一个命令和一个函数对应起来。

所以,使用一个命令实际上就是调用一个函数,命令后面附带的选项和参数最终都会作为实参传递给函数。参数”“和不传参是不一样的,所以,变量应该用双引号包裹起来,这样能避免变量为空时导致的很多奇葩问题。

与数值比较相关的 test 选项

  • -eq 等于
  • =和== 两者是一样的
  • -ne 不等于
  • != 等同与-ne
  • -gt 大于
  • -\> 判断 ascii
  • -lt 小于
  • -\< 判断 ascii
  • -ge 大于等于
  • -le 小于等于

变量测试

  • -v FILE 小写 v,判断变量是否定义
  • -R FILE 判断变量是否定义并且是名称引用,bash4.4 新特性,这个说实话看不懂

文件测试

存在性测试

  • -a FILE 同-e
  • -e FILE 文件是否存在,存在返回 0
  • -b FILE 文件是否存在且为块设备文件
  • -c FILE 文件是否存在且为字符设备文件
  • -d FILE 文件是否存在且为目录文件
  • -f FILE 文件是否存在且为普通文件
  • -h FILE 同-L
  • -L FILE 文件是否存在且为符号链接文件
  • -p FILE 文件是否存在且为命名管道文件
  • -S FILE 文件是否存在且为套接字文件

文件权限测试

  • -r FILE 文件是否存在且可读
  • -w FILE 文件是否存在且可写
  • -x FILE 文件是否存在且可执行
  • -u FILE 文件是否存在且拥有 suid 权限
    如果 bin 文件设置了 SUID,那么当前用户执行这个 bin 文件的时候,会自动继承这个 bin 文件的属主的身份。
  • -g FILE 文件是否存在且拥有 guid 权限
  • -k FILE 是否存在且拥有 sticky 权限
    当目录设置了 Sticky 权限,只有文件的所有者和 root 可以删除该文件。

文件属性测试

  • -s FILE 文件是否存在且非空
  • -t fd 文件描述符是否在某终端已经打开
    fd 是文件描述符,或者说句柄,open 打开一个文件,就返回一个文件描述符 fd,fd 就指向这个打开的文件,使用 close(fd)关闭。linux 默认最多打开 1024 个文件,显然不够用,可以通过设置 ulimit 来调整。
  • -N FILE 文件自从上一次被读取后是否被修改
  • -O FILE 当前有效用户是否为文件属主
1
2
3
4
5
6
7
8
9
10
11
lujinkai@Z510:/home/ljk$ ll ./ljk.log
-rw-rw-r-- 1 ljk ljk 8 Aug 9 17:57 ./ljk.log
lujinkai@Z510:/home/ljk$ [ -O ljk.log ]
lujinkai@Z510:/home/ljk$ echo $?
1
lujinkai@Z510:/home/ljk$ cd ~/data/test/
lujinkai@Z510:~/data/test$ ll test.sh
-rw-r--r-- 1 lujinkai lujinkai 36 Aug 8 22:38 test.sh
lujinkai@Z510:~/data/test$ [ -O test.sh ]
lujinkai@Z510:~/data/test$ echo $?
0
  • -G FILE 当前有效用户是否为文件数组
  • FILE1 -ef FILE2 FILE1 是否是 FILE2 的硬链接
  • FILE1 -nt FILE2 FILE1 是否新于 FILE2(mtime)
  • FILE1 -ot FILE2 FILE1 是否旧于 FILE2

字符串测试

  • -z 判断字符串是否为空,为空返回 0

  • -n 判断字符串是否为非空,非空返回 0,-n 可以省略,test string等同于test -n string

逻辑运算

  • -a 逻辑与
  • -o 逻辑或

for i in 遍历后 $i 还存在,需要手动删除 unset i

在 shell 脚本中,相比 cdcd -,使用 pushdpopd 更高端大气上档次

  • pushd : push (in) directory,将目录压栈,最后一个压入的目录位于栈顶
  • popd : pop (out) directory,将目录栈逐个弹出

目录栈的栈顶永远存放的是当前目录。如果当前目录发生变化,那么目录栈的栈顶元素肯定也变了;反过来,如果栈顶元素发生变化,那么当前目录肯定也变了

pushd 不带参数,就会在栈顶的两个目录之间切换.如果需要调整到其他目录,可以使用+n 参数.具体每个栈内目录编号,通过 dirs -v 查看

基本用法

脚本调试

脚本错误分为三种:

  • 语法错误,导致后续命令不继续执行,使用 bash -n 检查错误
  • 命令错误,默认后续命令继续执行,使用 bash -x 检查错误
  • 逻辑错误,只能使用 bash -x 检查

变量

shell 脚本中的所有变量值都是字符串,其中的数字也是字符串

shell 中变量命名规则

1
2
3
- 只能使用数字、字母及下划线,且不能以数字开头
- 区分大小写
- 变量、等号、值中间不能出现任何空格

注意: 不支持短横线 “-”,这点和主机名相反

定义变量

1
2
3
4
5
name='value'

name='root' # 直接字符串赋值
name="$user" # 变量引用,注意一定要用双引号
name=`COMMAND` 或者 name=$(COMMAND) # 命令引用

引用变量

1
2
$name
${name}

关于$:

1
2
3
- js中变量名前不加$,只有在上引号包裹的字符串中才使用${}引用变量
- php变量名前加$,无论什么情况,$和变量名不可分离
- shell变量名前不加$,只有在引用的时候加$,在容易起因歧义的时候,使用${}引用变量

拼接字符串:

1
2
3
- php使用点 .
- js使用加号 +
- shell中什么也不需要,直接写在一起就可以了,也可以用+=拼接自身

删除变量

1
unset name

declare ★★★

declare 是 bash 内置命令,用来声明变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
declare [-aAfFgilnrtux] [-p] [name[=value] ...]

f # 显示已定义的函数
F # 显示已定义的函数名
g # 声明一个全局变量,name="value" 这种方式就是省略参数g,完整的写法是declare -g name="value",所以在函数中请使用 declare name="value",不要使用 name="value",除非你想声明一个全局变量
-p # 显示所有变量及其属性和值,不包括函数

为变量设置属性,如果没有声明变量,则显示设置此属性的变量:
a # 声明索引数组
A # 声明关联数组
i # 声明整数,可以进行算数运算
l # 声明变量为小写字母
n # 为value设置引用属性
r # 声明只读变量,相当于 readonly,当前进程内,只能定义,不能修改,不能删除
t # 为变量设置trace属性,跟踪函数从调用shell继承DEBUG和RETURN类型的。trace属性对变量没有特殊意义
u # 声明变量为大写字母
x # 声明环境变量,相当于 export

Shell 内建命令之 trap:https://blog.csdn.net/asty9000/article/details/88393166

  • i 示例:

    1
    2
    3
    4
    5
    6
    let var=算术表达式
    ((var=算术表达式))
    var=$[算术表达式]
    var=$((算术表达式))
    declare -i var=算术表达式
    echo '算术表达式' | bc
  • n 示例:

    1
    2
    declare a=123
    declare -n b=a # b=123 等同于 declare b=$a

eval ★★★

先扫描一遍,进行置换,再执行命令

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
# 范例一
lujinkai@Z510:~$ echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z
lujinkai@Z510:~$ echo {a..z} | tr -d ' '
abcdefghijklmnopqrstuvwxyz
lujinkai@Z510:~$ words="echo {a..z} | tr -d ' '"
lujinkai@Z510:~$ echo $words
echo {a..z} | tr -d ' '
lujinkai@Z510:~$ echo `echo $words`
echo {a..z} | tr -d ' '
lujinkai@Z510:~$ eval $words
abcdefghijklmnopqrstuvwxyz

# 范例二
[root@centos8 ~]# CMD=whoami
[root@centos8 ~]# echo
$CMD
whoami
[root@centos8 ~]# eval $CMD
root

# 范例三
[root@centos8 ~]# n=10
[root@centos8 ~]# echo {0..$n}
{0..10}
[root@centos8 ~]# eval echo {0..$n}
0 1 2 3 4 5 6 7 8 9 10

练习

1、编写脚本 systeminfo.sh,显示当前主机系统信息,包括:主机名,IPv4 地址,操作系统版本,内核版本,CPU 型号,内存大小,硬盘大小

1
2
3
4
5
6
主机名:hostname
ipv4地址:hostname -I
操作系统版本:os-release NAME VERSION
内核版本:echo `uname -s;uname -r`
内存大小:`free -m | awk '/Mem:/{print $2}'`
硬盘大小:lsblk -m | awk '/sda/{print $2}'| head -n1

2、编写脚本 backup.sh,可实现每日将 /etc/ 目录备份到 /backup/etcYYYY-mm-dd 中

3、编写脚本 disk.sh,显示当前硬盘分区中空间利用率最大的值

4、编写脚本 links.sh,显示正连接本主机的每个远程主机的 IPv4 地址和连接数,并按连接数从大到小排序

环境变量

对所有用户生效的环境变量:/etc/profile
对特定用户生效的环境变量:~/.bashrc 或者 ~/.bash_profile
临时有效的环境变量:脚本或命令行使用 export 定义

常用环境变量:

1
PATH、HOME、LOGNAME、PWD、HISTFILE、HISTSIZE、HOSTNAME、SHELL、PS1、TMOUT、IFS、OFS
1
2
# 声明环境变量,除了使用 declare -x 还可以使用export
export name=VALUE

显示所有环境变量:

1
2
3
4
env
printenv
export
declare -x

位置变量

位置变量,在 bash shell 中内置的变量,在脚本代码中调用通过命令行传递给脚本的参数

1
2
3
4
5
1,2...   对应第一个,第二个等参数,当参数小于10个的时候,$n可以表示,当参数大于10个的时候,使用${n}表示
0 命令本身,包括路径在内
* 传递给脚本所有的参数,全部参数合为一个字符串,每个参数区别对待
@ 传递给脚本的所有参数,每个参数为独立字符串,所有参数视为一个整体
# 传递给脚本的参数个数

注意:$@$* 只有在被双引号包起来的时候才会有差异,在 for in 循环中,不加引号的时候,$*$@ 都可以正常遍历,加双引号的时候,$* 只有一个字符串变量

清空所有位置变量:

1
set --

范例:利用软链接实现同一个脚本不同功能

1

退出状态码

用于无条件终止当前脚本的执行 exit n

1
2
3
4
5
6
7
8
9
10
n == 0         #脚本执行成功
n == 1-125 #出错,这些对应的错误值由用户在脚本中定义
n == 1 #一般未知错误
n == 2 #不合适的shell命令
n == 126 #文件不可执行
n == 127 #不存在该命令
n == 128 #无效的退出参数
n == 128+x #与linux信号x相关的严重错误
n == 130 #通过ctrl+c终止的命令
n > 255 #正常范围之外的退出状态码

1-125 这些错误值可以由用户自定义
如果不给定 n 的值,而直接使用 exit,那么返回 exit 之前最后一条语句的状态,等于 exit $?

‘’ “” `` $()

单引号不解析变量,双引号解析变量,反引号中必须是有输出的命令,$() 和 ``是等价的, 但是 $() 可以嵌套

脚本安全和 set

set 命令:可以用来定制 shell 环境,set + 关闭设置,set - 打开设置

1
2
lujinkai@Z510:~/data/test$ echo $-
himBHs
1
2
3
4
5
h #hashall,外部命令使用过一次后就会被hash下来,后面再使用这个命令就优先从hash中获取,通过`set +h`将h选项关闭
i #interactive-comments,说明当前shell是交互式shell,在脚本中,i选项是被关闭的
m #monitor,打开监控
B #braceexpand,大括号扩展
H #history,H选项打开,可以展开历史列表中的命令,可以通过!来完成,例如!!返回上一个历史命令,!n返回第n个历史命令

set 命令实现脚本安全:

参考:http://www.ruanyifeng.com/blog/2017/11/bash-set.html?utm_source=tool.lu

1
2
3
4
5
6
7
-u 变量未定义就报错, 等同 `set -o nounset`
-e 遇到错误(exit不等于0)就退出,等同 `set -o errexit`
-o option 显示,打开或关闭选项
显示选项 `set -o`
打开选项 `set -o 选项`
关闭选项 `set -o 选项`
-x 当执行命令的时候,打印命令及其参数,类似 `bash -x`
1
2
3
4
5
6
7
8
9
10
11
lujinkai@Z510:~/data/test$ set -o
allexport off
braceexpand on
emacs on
errexit off
errtrace off
functrace off
hashall on
histexpand on
history on
...

格式化输出 printf

1
printf "指定的格式" "文本1" "文本2"...

1
2
3
4
5
6
7
8
9
10
11
%s       #字符串
%f #浮点数
%b #解析转义符
%c #ASCI字符串,即显示对应参数的第一个字符
%d,%i #十进制整数
%o #八进制值
%u #不带正负号的十进制值
%x #十六进制值(a-f)
%X #十六进制值(A-Z)
%% #表示%本身
%n #指定输出宽度为n,不足补空格,默认右对齐,例如:%-10s 表示10个字符宽,- 表示左对齐

常用转义字符:

1
2
3
4
5
6
7
\a    #警告字符
\b #后退
\f #换页
\n #换行
\r #回车
\t #水平制表符,就是tab键
\v #水平制表符
1
2
3
4
5
6
7
8
lujinkai@Z510:~/data/test$ printf "%.2f\n" 1 2 3
1.00
2.00
3.00
lujinkai@Z510:~/data/test$ printf "hello: %s\n" daming xiaohong tom
hello: daming
hello: xiaohong
hello: tom

算术运算

bash 只支持整数运算,不支持小数运算。复杂的运算请使用 bc

加减乘除、自加自减,取余,异或取反,三元运算等都和 php 一样。

1
2
3
4
5
6
let var=算术表达式
((var=算术表达式))
var=$[算术表达式]
var=$((算术表达式))
declare -i var=算术表达式
echo '算术表达式' | bc

$RANDOM:内建的随机数生成器变量

1
$RANDOM 取值范围: 0-32767 (2^15=32768)

示例:生成 0-49 随机数

1
echo $[$RANDOM%50] # 除以50取余

示例:鸡兔同笼

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash

head=$1
foot=$2

let tu=($foot-2*$head)/2
let ji=$head-$tu

echo 兔子:$tu
echo 鸡:$ji

逻辑运算 &&、||、!

1
2
3
与  &&
或 ||
非 !

短路运算

使用 read 命令来接受输入

read 是 shell 内置命令,从标准输入中读取数据并赋值给变量。如果没有重定向,默认是从键盘读取用户输入的数据,如果进行了重定向,那么可以从文件中都去数据。

1
2
# read [options] [name ...]
read: read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]

如果没有提供变量名,那么读取的数据默认存放到环境变量 REPLY 中。

1
2
3
4
5
6
7
8
9
-a array     #读取的数据赋值给数组
-d delimiter #用字符串delimiter指定读取结束的位置,而不是一个换行符(读取到的数据不包括delimiter)
-n num #指定输入字符串的长度,到达指定长度自动退出
-t seconds #设置超时时间,用户超过时间没有输入,自动退出,exit非零
-s #静默输入,一般用于密码
-p prompt #显示提示内容,提示内容不能换行
-e #在用户输入的时候,对功能键进行编码转换,更人性化
-r #不允许反斜杠转义,选项一般都要加上,pycharm中如果不加会警告提示
-u #从文件描述符fd中读取内容

read 就像表单提交,所有用户提交的信息都是不可信的,一定要做好校验

条件测试命令

bash shell 的配置文件

bash shell 配置文件有: /etc/profile~/.bash_profile~/.bash_login~/.profile~/.bashrc/etc/bashrc/etc/profile.d/\*.sh,不同的启动方式会加载不同的配置文件

生效范围

/etc 目录下的配置文件是全局生效,家目录下的配置文件是对当前用户生效

配置文件太多,为了避免混乱,默认:对所有用户添加配置,添加到 /etc/profile.d/ 目录下,当前用户添加配置,添加到 ~/.bashrc 中

登录方式

shell 登录有两种方式:交互式登录和非交互式登录

交互式

  1. 帐号密码登录;
    1. su - userName

配置文件生效和执行顺序:

Ubuntu18.4

1
2
3
4
5
1. /etc/profile
2. /etc/profile/*.sh
3. /etc/bash.bashrc
4. ~/.profile
5. ~/.etc/bashrc

CentOS7

1
2
3
4
5
1. /etc/profile
2. /etc/profile.d/\*.sh
3. ~/.bash_profile
4. ~/.bashrc
5. /etc/bashrc

非交互式

  1. su userName
  2. 图形化界面下打开的终端;
  3. 执行脚本;
  4. 任何其他的 bash 实例

非交互式登录,只加载 ~/.bashrc(centos 的~/.bashrc 中会加载/etc/bashrc 文件)

所以按照功能划分:profile 类只在交互式登录中加载,而 bashrc 类在交互式和非交互式登录中都有加载

bash 退出任务

退出 shell 时自动运行 ~/.bash_logout 文件,可以用来创建自动备份文件、清除临时文件

流程控制 - 条件选择

if

if/then/elif/else/fi

单分支

1
2
3
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi

双分支

1
2
3
4
5
if []; then
code...
else
code...
fi

多分支

1
2
3
4
5
6
7
8
9
10
if []; then
code...
elif []; then
code...
elif []; then
code...
...
else
code...
fi

case

类似 php 中的 switch

case/esac

1
2
3
4
5
6
7
8
9
10
11
12
13
#! /bin/sh
echo "Is it morning? Please answer yes or no."
read YES_OR_NO
case "$YES_OR_NO" in
yes|y|Yes|YES)
echo "Good Morning!";;
[nN]*)
echo "Good Afternoon!";;
*)
echo "Sorry, $YES_OR_NO not recognized. Enter yes or no."
exit 1;;
esac
exit 0

case 支持 glob 风格的通配符:*、?、[]、|

流程控制 - 循环

for in

for in/do/done

递归遍历当前目录下的所有文件

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
dir=$(dirname "`readlink -f $0`")
fn() {
for filename in `ls $1`; do
file=${1}/${filename}
echo -e "$file\n"
if [ -d "$file" ]; then
fn $file
fi
done
}
fn $dir

while

while/do/done

无限循环

1
2
3
while true; do
循环体
done

验证密码,限制尝试输入不超过 5 次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash
echo 'enter password:'
read -s try
num=1
while [[ $try != '123456' ]]; do
num=$(($num+1))
if [[ $num > 5 ]]; then
echo 'no change'
break
fi
echo 'try again'
read -s try
done
if [[ $try == '123456' ]]; then
echo 'yes'
fi

while read

while 循环的特殊用法,遍历文件或文本的每一行

1
2
3
4
# 依次读取file文件中的每一行,且将行赋值给变量X
while read X; do
循环体
done < file

练习,使用 while 实现:

  1. 编写脚本,求 100 以内所有正奇数之和
  2. 编写脚本,提示请输入网络地址,如 192.168.0.0,判断输入的网段中主机在线状态,并统计在线和离线主机各多少
  3. 编写脚本,打印九九乘法表
  4. 编写脚本,利用变量 RANDOM 生成 10 个随机数字,输出这个 10 数字,并显示其中的最大值和最小值
  5. 编写脚本,实现打印国际象棋棋盘
  6. 后续六个字符串: efbaf275cd、4be9c40b8b、44b2395c46、f8c8873ce0、b902c16c8b、ad865d2f63 是通过对随机数变量 RANDOM 随机执行命令: echo $RANDOM | md5sum | cut -c1-10 后的结果,请破解这些字符串对应的 RANDOM 值

until

while 判断条件为真则循环,until 判断条件为假则循环

无限循环

1
2
3
until false; do
循环体
Done

循环控制 continue 和 break

continue 跳过本次循环,break 跳出所有循环

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
set -u

i=10
while ((i--)); do
if [ $i == 5 ]; then
echo 'i is 5'
# continue
break
fi
echo $i
done
unset i

循环控制 shift

将参数列表最左边的参数删除,while 循环遍历位置参量列表时,常用到 shift

1
2
3
4
5
#!/bin/bash
while (($# > 0)); do
echo "$@"
shift
done

select

select 用于生成菜单,并显示 PS3 提示符,然后等待用户输入,无论输入什么都执行一遍循环,常搭配 case 来处理用户输入,select 通过 break 退出循环

1
2
3
select variable in list ;do
循环体命令
done

示例:

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
#!/bin/bash
sum=0
PS3="请点菜(1-6): "
select MENU in 北京烤鸭 佛跳墙 小龙虾 羊蝎子 火锅 点菜结束; do
case $REPLY in
1)
echo $MENU 价格是 100
((sum += 100))
;;
2)
echo $MENU 价格是 88
((sum += 88))
;;
3)
echo $MENU价格是 66
((sum += 66))
;;
4)
echo $MENU 价格是 166
((sum += 166))
;;
5)
echo $MENU 价格是 200
((sum += 200))
;;
6)
echo "点菜结束,退出"
break
;;
*)
echo "点菜错误,重新选择"
;;
esac
done
echo "总价格是: $sum"

函数

函数运行在当前 shell 进程

定义函数

1
2
3
4
5
6
7
8
9
10
11
12
#语法一:
func_name (){
...函数体...
}
#语法二:
function func_name {
...函数体...
}
#语法三:
function func_name () {
...函数体...
}
1
2
3
# 声明环境函数
declare -xf function_name
export -f function_name

调用函数

1
2
3
func_name  # 注意不要加()
# 参数用空格分割,在函数内部使用位置变量接收传入的参数
func_name arg1 arg2 arg3 ...

删除函数

1
unset func_name

函数返回值

函数返回值:

  • 使用 echo 等命令进行输出
  • 函数体中调用命令的输出结果

函数的退出状态码:

  • 默认取决于函数中执行的最后一条命令的退出状态码
  • 自定义退出状态码,其格式为:
    return 从函数中返回,用最后状态命令决定返回值
    return 0 无错误返回
    return 1-255 有错误返回

函数变量

1
2
3
local NAME=VALUE
# 等于
declare NAME=VALUE

脚本相关工具

trap

信号捕捉,捕捉一个或多个信号,替换为自定义操作

trap 捕捉的信号包括系统信号和 shell 信号,系统信号通过trap -lkill -l查看,shell 信号有四个:EXIT(或信号代码 0)、ERR、DEBUG 和 RETURN。

1
2
3
4
5
trap command signal_list # 捕捉信号列表,替换为自定义command
trap '' signal_list # 捕捉信号列表,不做任何操作
trap '-' signal_list # 捕捉信号列表,恢复这些信号
trap -p # 列出自定义捕捉信号操作
trap -l # 列出所有系统信

信号名是 SIG 后面的部分,捕捉的时候可以写信号的编号,也可以写信号名称,不区分大小写

由于不少信号在不同架构的计算机上数值不同,所以在不确定编号是否唯一的时候,最好写信号名称

1
trap "echo 'Press ctrl+c or ctrl+\ '" int quit

DEBUG 和 RETURN 这两种信号陷阱无需关注,EXIT 是退出状态码为 0 时发出的信号,退出状态码非 0 时则发出 ERR 信号

系统信号的编号从 1 到 64,EXIT 的编号是 0

1
2
3
4
finish(){
echo finish| tee -a /root/finish.log
}
trap finish exit

mktemp

创建临时文件,可以避免文件名冲突

1
2
3
4
5
6
# X至少3个,大写X
lujinkai@Z510:~/data/test$ mktemp fileXXXXX
fileS9vLK
# -d 创建临时目录
lujinkai@Z510:~/data/test$ mktemp -d dirXXX
diresA

expect

expect 基于 Tcl( Tool Command Language )语言开发,主要应用于自动化交互式操作的场景,借助 expect 处理交互的命令,可以将交互过程如:ssh 登录,ftp 登录等写在一个脚本上,使之自动化完成。尤其适用于需要对多台服务器执行相同操作的环境中,可以大大提高系统管理人员的工作效率

1
expect [ -dDinN ] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ]

数组

定义

shell 和 php 一样,支持索引数组和关联数组。

1
2
3
4
# 索引数组
arr=(val1 val2 val3 val4 ...)
# 关联数组
declare -A brr=([name]='lujinkai' [age]='18' [gender]='男')

注意:声明索引数组可以省略 declare,声明关联数组不能省略 declare,如果省略不报错,但是数据会乱。

调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
lujinkai@Z510:~/data/test$ array=("Allen" "Mike" "Messi" "Jerry" "Hanmeimei" "Wang")
# 打印数组长度
lujinkai@Z510:~/data/test$ echo ${#array[@]}
6
# 打印元素长度
lujinkai@Z510:~/data/test$ echo ${#array[1]} # 打印第1个元素 Mike的长度
4
# 分片访问
lujinkai@Z510:~/data/test$ echo ${array[@]:1:2}
Mike Messi
# 元素内容替换
lujinkai@Z510:~/data/test$ echo ${array[@]/e/E}
AllEn MikE MEssi JErry HanmEimei Wang
lujinkai@Z510:~/data/test$ echo ${array[@]//e/E}
AllEn MikE MEssi JErry HanmEimEi Wang
# 数组的遍历
for in ${array[@]}
do
echo $val
done

字符串

1
lujinkai@Z510:~$ str='abcdefghijklmnopqrstuvwxyz'

切片

假设有一个指针 pointer,始终指向字符串的“间隙”,从前往后,“间隙”号依次为 0、1、2、3…,而且显然“间隙”数量比字符数量多 1 个,如果字符串有 2 个字符,那就有 3 个“间隙”。指针默认指向 0,指针的位置就是切片位置。

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
# 字符串长度
lujinkai@Z510:~$ echo ${#str}
26

# 从切片位置往后截取length个字符:${str:offset:length}
lujinkai@Z510:~$ echo ${str:0:6}
abcdef
lujinkai@Z510:~$ echo ${str:4:2}
ef

# 截取后length个字符:${str:0-length} 0可以用空格代替
lujinkai@Z510:~$ echo ${str: -3}
xyz
lujinkai@Z510:~$ echo ${str:0-3}
xyz

# 截取后length个字符,然后再处理:${str:0-length:offset}、${str:0-length:-offset}
lujinkai@Z510:~$ echo ${str:0-3:1}
x
lujinkai@Z510:~$ echo ${str:0-3:-1}
xy


# 掐头去尾:${str:offset:-length}
lujinkai@Z510:~$ echo ${str:1:-1}
bcdefghijklmnopqrstuvwxy

查找替换

${str#*word} 查找 word 第一次出现的位置,删除其以及之前的部分

${str##*word} 贪婪模式,查找 word 最后一次出现的位置,删除其及其之前的部分

${str%word*} 查找 word 最后出现的位置,删除其及其之后的部分

${str%%word*} 查找 word 第一次出现的位置,删除其及其之后的部分

${str/pattern} 删除第一个匹配

${str//pattern} 删除所有匹配

大小写转换

${str^^} 转大写

${str,,} 转小写

1
2
3
4
lujinkai@Z510:~$ echo ${str^^}
ABCDEFGHIJKLMNOPQRSTUVWXYZ
lujinkai@Z510:~$ echo ${str,,}
abcdefghijklmnopqrstuvwxyz

变量测试

变量测试点比较多,而且不怎么用,主要用 if 来判断测试,所以不用深究

expect 基于 Tcl( Tool Command Language )语言开发,主要应用于自动化交互式操作的场景,借助 expect 处理交互的命令,可以将交互过程如:ssh 登录,ftp 登录等写在一个脚本上,使之自动化完成。尤其适用于需要对多台服务器执行相同操作的环境中,可以大大提高系统管理人员的工作效率。

expect 可以认为是一种脚本语言,下面介绍的是 expect 环境下的变量和命令:

变量

定义变量

1
set name value

调用变量

1
$name

命令

close [-slave] [-onexec 0|1] [-i spawn_id]

debug [[-now] 0|1]

disconnect

exit [-opts] [status]

exp_continue [-continue_timer] ★

不返回结果,继续执行。

exp_internal [-f file] value

exp_open [args] [-i spawn_id]

exp_pid [-i spawn_id]

exp_send

exp_send_error

exp_send_log

exp_send_tty

exp_send_user

exp_version [[-exit] version]

expect [[-opts] pat1 body1] … [-opts] patn [bodyn] ★

模式匹配,匹配成功后执行指定操作,如果没有指定操作,则忽略。
eof 和 timeout 是关键字
如果匹配的是关键字”eof”,则在文件结束时候执行指定操作,如果匹配的是关键字”timeout”,则在超时时执行指定的操作,默认的超时时间是 10 秒,可以设置为 30 秒set timeout 30,或者设置不超时set timeout -

1
2
3
4
5
6
7
8
9
10
11
set timeout 30
# 匹配一次
expect 'hi' {send "You said hi\n"}
# 匹配多次:每次匹配成功后执行指定操作,返回的结果用来进行下一次模式匹配。
expect {
busy {puts busy\n ; exp_continue}
failed abort
"invalid password" abort
timeout abort
connected
}

expect_after [expect_args]

expect_background [expect_args]

expect_before [expect_args]

expect_tty [expect_args]

expect_user [expect_args]

fork

interact [string1 body1] … [stringn [bodyn]] ★

interact 是交互的意思。将当前进程的控制权交给用户,就是让用户手动处理的意思。

interpreter [args]

log_file [args] [[-a] file]

log_user -info|0|1

match_max [-d] [-i spawn_id] [size]

overlay [-# spawn_id] [-# spawn_id] […] program [args]

parity [-d] [-i spawn_id] [value]

remove_nulls [-d] [-i spawn_id] [value]

send [-flags] string ★

将字符串发送给当前进程。

  • -i
  • -null
  • -break
  • -s
  • -h

send_error [-flags] string

send_log [–] string

send_tty [-flags] string

send_user [-flags] string

sleep seconds

spawn [args] program [args] ★

spawn 是繁衍的意思。新建一个进程,运行指定命令,标准输入,标准输出、标准错误都在 expect 环境内,方便其他 expect 命令进行后续处理。
spawn 启动进程时,会产生一个变量 spawn_id, 存储了进程的描述符。用 close 关闭当前进程及其相关文件。

1
2
3
4
5
6
7
#!/usr/bin/expect
spawn ssh 10.0.0.7
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "123456" }
}
interact

strace level

stty args

system args

timestamp [args]

trap [[command] signals]

wait [args]

范例

写成 expect 脚本,可以接收参数,[lindex $argv 0]表示调用第一个参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/expect
set ip [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
set timeout 10
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$password\n" }
}
expect "]#" { send "useradd haha\n" }
expect "]#" { send "echo 123456 |passwd --stdin haha\n" }
send "exit\n"
expect eof

#./ssh4.exp 10.0.0.7 root 123456
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash
ip=$1
user=$2
password=$3
expect <<EOF
set timeout 20
spawn ssh $user@$ip
expect {
"yes/no" { send "yes\n";exp_continue }
"password" { send "$password\n" }
}
expect "]#" { send "useradd hehe\n" }
expect "]#" { send "echo 123456 |passwd --stdin hehe\n" }
expect "]#" { send "exit\n" }
expect eof
EOF

#./ssh5.sh 192.168.8.10 root 123456

1
2
# 安装邮件服务
yum -y install mailx
1
2
3
4
5
# 配置/etc/mail.rc
set from=ljkk3014@foxmail.com
set smtp=smtp.qq.com
set smtp-auth-user=ljkk3014@foxmail.com
set smtp-auth-password=jwopcmnpmawddbbg # 随便写的
1
2
# 发送邮件
[root@centos8 ~]# echo -e "Hello, I am `whoami`,The system version is here,please help me to check it ,thanks! \n`cat /etc/os-release`" | mail -s hello 441757636@qq.com

效果:

参考:https://www.cnblogs.com/maxgongzuo/p/6372898.html

信号配置 Signal dispositions

每个信号都有一个配置,决定信号传递到进程时的行为。每个信号都有一个默认的配置,指定信号的默认操作,至于最终具体的操作,肯定是基于它的默认操作的。

Term

默认操作是终止进程

Ign

默认操作是忽略进程

Core

默认操作是终止进程,并 core dump

core dump

当程序运行的过程中异常终止或崩溃,操作系统会将程序当时的内存状态记录下来,保存在一个文件中,这种行为就叫做 Core Dump(中文有的翻译成“核心转储”)。除了内存信息之外,还有些关键的程序运行状态也会同时 dump 下来,例如寄存器信息(包括程序指针、栈指针等)、内存管理信息、其他处理器和操作系统状态和信息。core dump 对于编程人员诊断和调试程序是非常有帮助的,因为对于有些程序错误是很难重现的,例如指针异常,而 core dump 文件可以再现程序出错时的情景。

如果没有进行 core dump 的相关设置,默认是不开启的。可以通过ulimit -c查看是否开启。如果输出为0,则没有开启,需要执行ulimit -c unlimited开启 core dump 功能。

Stop

默认操作是停止进程,后台休眠

Term 和 Stop 的区别:Term 终止进程,进程不存在;Stop 停止进程,进程还存在,可以通过 Cont 唤醒

Cont

默认操作是继续被停止的进程

标准信号 Standard signals

kill -ltrap -l可以查看所有的系统信号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
lujinkai@Z510:~$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX

编号 1-31 是标准信号,我们只研究这些,后面的不研究,常用信号说明:

信号名是 SIG 后面的部分,大部分信号都能被捕获,SIGKILL 和 SIGSTOP 不能被捕获。

1) HUP

默认操作 Term,可捕获

本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一 session 内的各个作业, 这时它们与控制终端不再关联。

登录 Linux 时,系统会分配给登录用户一个终端(Session)。在这个终端运行的所有程序,包括前台进程组和后台进程组,一般都属于这个 Session。当用户退出 Linux 登录时,前台进程组和后台有对终端输出的进程将会收到 SIGHUP 信号。这个信号的默认操作为终止进程,因此前台进 程组和后台有终端输出的进程就会中止。不过可以捕获这个信号,比如 wget 能捕获 SIGHUP 信号,并忽略它,这样就算退出了 Linux 登录,wget 也 能继续下载。

此外,对于与终端脱离关系的守护进程,这个信号用于通知它重新读取配置文件。

2) INT

默认操作 Term,可捕获

程序终止(interrupt)信号, 在用户键入 INTR 字符(通常是 Ctrl-C)时发出,用于通知前台进程组终止进程

3) QUIT

默认操作 Core,可捕获

和 SIGINT 类似, 但由 QUIT 字符(通常是 Ctrl-)来控制. 进程在因收到 SIGQUIT 退出时会 core dump, 在这个意义上类似于一个程序错误信号。

9) KILL

默认操作 Term,不可捕获

杀掉进程,KILL 和 TERM 的区别在于此信号不能被捕获、阻塞或忽略,所以一旦接收到此信号的进程只能终止,而 TERM 可以被捕获,进程在捕获到 TERM 信号后,可以执行自定义操作,例如通知子进程关闭,清理资源等操作。

15) TERM

默认操作 Term,可捕获

终止进程,是 kill 默认传递的信号。

单小括号 ()

():等同`` ,命令组。括号中的命令将会新开一个子 shell 顺序执行,所以括号中的变量不能够被脚本余下的部分使用。括号中多个命令之间用分号隔开,最后一个命令可以没有分号,各命令和括号之间不必有空格。

可以使用$获取()中的标准输出,$()

array=(a b c d) 用于初始化数组

{}

{}和()一样可以将多个命令组合在一起,使用分号隔开,批量执行,区别在于:
1.()是新开进程,{}在当前进程
2.()和命令之间不需要空格,{}左边和命令之间必须要有空格
3.()中最后一个命令可以不加分号,{}中所有命令都要加分号 4.$搭配()可以获取()的返回值;$搭配{}是用来解析变量,这点和 php 一样

扩展

bash 支持{a,b,c}这样的扩展,比如 a{d,c,b}e 会被扩展成 ade ace abe

类似还有:cp filename{,bak}

等同于 :cp filename filename.bak

简化重复字符串

{} 可以实现打印重复字符串的简化形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@4710419222 test]# touch {0..9}.log
[root@4710419222 test]# ls
0.log 1.log 2.log 3.log 4.log 5.log 6.log 7.log 8.log 9.log
[root@4710419222 test]# ls {4..7}.log
4.log 5.log 6.log 7.log
[root@4710419222 test]# ls {0..9..2}*
0.log 2.log 4.log 6.log 8.log
[root@4710419222 test]# ls {1,4,6}.log
1.log 4.log 6.log
# 关闭和开启{}的扩展功能
[root@4710419222 test]# echo $-
himBH
[root@4710419222 test]# set +B
[root@4710419222 test]# echo $-
himH
[root@4710419222 test]# ls {1,4,6}.log
ls: cannot access {1,4,6}.log: No such file or directory
[root@4710419222 test]# set -B
[root@4710419222 test]# ls {1,4,6}.log
1.log 4.log 6.log

补充:批量创建文件是用{},批量显示文件使用[]

变量替换

变量测试

双小括号 (())

将数学运算表达式放在(())中,表达式可以有一个,也可以由多个,多个表达式用逗号分割,(())的执行结果取决于最后一个表达式的值

可以使用$获取(())命令的执行结果

单中括号 []

[]有两种用法:一是等同 test; 二是算数运算$[],等同于$(())
关于 test 和[],推荐使用[]; 关于$[]和$(()),推荐使用$(())

1. 等同 test

等同于 test 命令,通常和 if 语句一起使用,在”[]”的内部和数据之间必须使用空格;否则判断式会报错。

test 和[]中可用的比较运算符只有==和!=,两者都是用来比较字符串的,不可用于整数比较,整数比较只能使用-eq、-gt 这种形式。

具体参数参考test.md

双中括号 [[]]

[[]]是shell内置关键字,它和test类似,也用来检测某个条件是否成立。
test能做到的,[[]]也能做到,而且做的更好,test 做不到的,[[]]还能做到。可以认为[[]]是 test 的升级版,对细节进行了优化,并扩展了一些功能。

[[]]的用法

1
[[ expression ]]

当[[]]判断expression成立时,退出状态0,否则为非0值,注意[[]]和 expression 之间的空格,这两个空格是必须的,否则会导致语法错误。

[[]]不需要注意某些细枝末节

[[]]是shell内置关键字,不是命令,在使用时没有给函数传递参数的过程,所以test命令的某些注意事项在[[]]中不存在了,具体包括:

  • 不需要把变量名用””包裹起来,即使变量是空值,也不会出错
  • 不需要、也不能对>、<进行转义,转义后会出错

[[]]支持逻辑运算符

[[]]剔除了 test 命令的-o 和-a 选项,只能使用||和&&

[[]]支持正则表达式

在[[]]中,可以使用=~来检测字符串是否符合某个正则表达式,他的用法为:

1
[[ str =~ regex ]]

总结

[[]]完全可以替换[],但是[[]]对数字的比较仍然不友好,所有,当 if 判断条件时,用(())来处理整型数字,当使用正则表达式或通配符使用[[]],其它情况一般使用 [ ]

参考:https://zh.m.wikipedia.org/zh-hans/ANSI%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97

ANSI 转义序列(ANSI escape sequences)是一种带内信号(英语:In-band signaling)的转义序列标准,用于控制视频文本终端上的光标位置、颜色和其他选项。在文本中嵌入确定的字节序列,大部分以ESC转义字符和”[“字符开始,终端会把这些字节序列解释为相应的指令,而不是普通的字符编码

ANSI 序列是在二十世纪七十年代引入的标准,用以取代特定于终端供应商的序列,并在二十世纪八十年代早期开始在计算机设备市场上广泛使用。与早期缺少光标移动功能的系统相比,新生的电子公告板系统使用 ANSI 序列改进其显示。正是因为这个原因,ANSI 序列变成了所有制造商共同采用的标准。

在 21 世纪,尽管硬件文本终端已经越来越少了,但 ANSI 标准依然存在,因为大多数终端模拟器会对部分 ANSI 转义序列进行解释。一个值得注意的例外是,在微软Windows 10更新 TH2 之前,Windows 操作系统Win32 控制台是不支持 ANSI 转义序列的。

转义序列

序列具有不同的长度。所有序列都以 ASCII 字符**[ESC](https://zh.m.wikipedia.org/wiki/退出键)**(27 / 十六进制 0x1B)开头,第二个字节则是 0x40–0x5F(ASCII @A–Z[\]^_)范围内的字符。[12]:5.3.a

一些 ANSI 转义序列(不完整列表)

序列 **C1 名称 作用
ESC [ 0x9b CSI - 控制序列导入器(Control Sequence Introducer) 大部分有用的序列,请参阅下一节。结束于 ASCII 64 到 126 (@~/十六进制 0x40 到 0x7E).[12]

CSI

CSI 序列由ESC [、若干个(包括 0 个)“参数字节”、若干个“中间字节”,以及一个“最终字节”组成。

所有常见的序列都只是把参数用作一系列分号分隔的数字,如1;2;3。缺少的数字视为 0(如1;;3相当于中间的数字是 0,ESC[m这样没有参数的情况相当于参数为 0)。某些序列(如 CUU)把 0 视为 1,以使缺少参数的情况下有意义。

一些 ANSI 控制序列(不完整列表)

代码 名称 作用
CSI n m SGR – 选择图形再现(Select Graphic Rendition) 设置SGR 参数,包括文字颜色。CSI 后可以是 0 或者更多参数,用分号分隔。如果没有参数,则视为CSI 0 m(重置/常规)。

选择图形再现(SGR)参数

代码 作用 备注
0 重置/正常 关闭所有属性。
1 粗体或增加强度
4 下划线
5 缓慢闪烁 低于每分钟 150 次。
22 正常颜色或强度 不强不弱。
30–37 设置前景色 参见下面的颜色表。
38 设置前景色 下一个参数是5;n2;r;g;b,见下。
39 默认前景色 由具体实现定义(按照标准)。
40–47 设置背景色 参见下面的颜色表。
48 设置背景色 下一个参数是5;n2;r;g;b,见下。
49 默认背景色 由具体实现定义(按照标准)。

颜色

初始的规格只有 8 种颜色,只给了它们的名字。SGR 参数 30-37 选择前景色,40-47 选择背景色。相当多的终端将“粗体”(SGR 代码 1)实现为更明亮的颜色而不是不同的字体,从而提供了 8 种额外的前景色,但通常情况下并不能用于背景色,虽然有时候反显(SGR 代码 7)可以允许这样。例如:在白色背景上显示黑色文字使用ESC[30;47m,显示红色文字用ESC[31m,显示明亮的红色文字用ESC[1;31m。重置为默认颜色用ESC[39;49m(某些终端不支持),重置所有属性用ESC[0m。后来的终端新增了功能,可以直接用 90-97 和 100-107 指定“明亮”的颜色。

字颜色:30———–37

1
2
3
4
5
6
7
8
30    黑
31 红
32 绿
33 黄
34 蓝色
35 紫色
36 深绿
37 白色

字背景颜色范围:40—-47

1
2
3
4
5
6
7
8
40    黑
41 深红
42 绿
43 黄色
44 蓝色
45 紫色
46 深绿
47 白色

字体加亮颜色:90————97

1
2
3
4
5
6
7
8
90    黑
91 红
92 绿
93 黄
94 蓝色
95 紫色
96 深绿
97 白色

背景加亮颜色范围:100——————–107

1
2
3
4
5
6
7
8
100   黑
101 深红
102 绿
103 黄色
104 蓝色
105 紫色
106 深绿
107 白色

ANSI 控制码的说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
\033[0m                 # 关闭所有属性
\033[1m # 设置高亮度
\033[4m # 下划线
\033[5m # 闪烁
\033[7m # 反显
\033[8m # 消隐
\033[30m -- \033[37m # 设置前景色
\033[40m -- \033[47m # 设置背景色
\033[nA # 光标上移n行
\033[nB # 光标下移n行
\033[nC # 光标右移n行
\033[nD # 光标左移n行
\033[y;xH # 设置光标位置
\033[2J # 清屏
\033[K # 清除从光标到行尾的内容
\033[s # 保存光标位置
\033[u # 恢复光标位置
\033[?25l # 隐藏光标
\033[?25h # 显示光标

在 vim 中应用

输入逃逸符^[ : ctrl + v + [

vim 中输入 ^[[1;31m123^[[0m

使用 cat 打印就是红色的 123

正则表达式

字符匹配

1
2
3
.   匹配任意单个字符
[] 匹配指定范围内的任意单个字符
[^] 匹配指定范围外的任意单个字符

匹配次数

1
2
3
4
5
6
7
* 匹配前面的字符0次到无数次
? 匹配前面的字符0次或1次
+ 匹配前面的字符至少一次
{n} 匹配前面的字符n次
{m,n} 匹配前面的字符m次到n次
{,n} 匹配前面的字符最多n次
{n,} 匹配前面的字符至少n次

位置锚定

1
2
3
4
5
6
7
8
9
10
^ 行首
$ 行尾
\> 或 \b 词首
\< 或 \b 词尾

^$ 空行
^[[:space:]]*$ 空白行
\<PATTERN\> 匹配整个单词

ps:单词是由字母、数字、下划线组成

分组及其他

1
2
3
() 分组
向后引用:\1,\2...
| 或

正则表达式练习 1

1、显示/proc/meminfo 文件中以大小 s 开头的行(要求:使用两种方法)

1
2
grep -i '^s' /proc/meminfo
grep -E '^(s|S)' /proc/meminfo

2、显示/etc/passwd 文件中不以/bin/bash 结尾的行

1
grep -E '^/bin/bash$' passwd

3、显示用户 rpc 默认的 shell 程序

1
getent passwd ljk | cut -d: -f8

4、找出/etc/passwd 中的两位或三位数

1
grep -Eo '\<[0-9]{2,3}\>' passwd

5、显示 CentOS7 的/etc/grub2.cfg 文件中,至少以一个空白字符开头的且后面有非空白字符的行

1
grep -E '^[[:space:]]+[^[:space:]]+' /etc/grub2.cfg

6、找出“netstat -tan”命令结果中以 LISTEN 后跟任意多个空白字符结尾的行

1
netstat -tan | grep -E 'LISTEN[[:space:]]+$'

7、显示 CentOS7 上所有 UID 小于 1000 以内的用户名和 UID

1
2
cat /etc/passwd | cut -d: -f1,3 | grep -E '\<[0-9]{1,3}\>'
cat /etc/passwd | cut -d: -f1,3 | grep -Ev '\<[0-9]{4,}\>'

8、添加用户 bash、testbash、basher、sh、nologin(其 shell 为/sbin/nologin),找出/etc/passwd 用户名和 shell 同名的行

1
grep -E '^(\<[0-9a-zA-Z_]+\>).*\1$' /etc/passwd

9、利用 df 和 grep,取出磁盘各分区利用率,并从大到小排序

1
2
df | grep '^/dev/sd' | grep -Eo '[0-9]{,3}%' | tr -d % | sort -nr
df | grep '^/dev/sd' | tr -s " " % | cut -d% -f5 | sort -nr

练习 2

1、显示三个用户 root、mage、wang 的 UID 和默认 shell
2、找出/etc/rc.d/init.d/functions 文件中行首为某单词(包括下划线)后面跟一个小括号的行

1
grep -E '^[a-zA-Z0-9_]+\(\)' ./functions

3、使用 egrep 取出/etc/rc.d/init.d/functions 中其基名

1
2
grep -E '^\<[0-9a-zA-Z_]+\>\(\)' ./functions | grep -Eo '^\<[0-9a-zA-Z_]+\>'
grep -E '^\<[0-9a-zA-Z_]+\>\(\)' ./functions | cut -d ' ' -f 1 | tr -d '()'

4、使用 egrep 取出上面路径的目录名
5、统计 last 命令中以 root 登录的每个主机 IP 地址登录次数

1
last | tr -s ' ' : | cut -d: -f3 | sort | uniq -c | sort -nr

6、利用扩展正则表达式分别表示 0-9、10-99、100-199、200-249、250-255

1
2
3
4
5
grep -E '\<[0-9]{1}\>' a.log
grep -E '\<[0-9]{2}\>' a.log
grep -E '\<1[0-9]{2}\>' a.log
grep -E '\<2[0-4][0-9]\>' a.log
grep -E '\<25[0-5]\>' a.log

7、显示 ifconfig 命令结果中所有 IPv4 地址

1
ifconfig | grep -Eo '[0-9]{,3}\.[0-9]{,3}\.[0-9]{,3}\.[0-9]{,3}'

8、将此字符串:welcome to magedu linux 中的每个字符去重并排序,重复次数多的排到前面

1
echo 'welcome to magedu linux' | grep -o '.' | sort | uniq -c | sort -nr

9、精确匹配 ip

1

sed:stream editor 流编辑器,对标准输出或文件逐行进行处理

sed 是从文件或管道中读取一行,处理一行,输出一行;再读取一行,再处理一行,再输出一行,直到最后一行。每当处理一行时,把当前处理的行存储在临时缓冲区中,称为**模式空间(PatternSpace)**,接着用 sed 命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。一次处理一行的设计模式使得 sed 性能很高,sed 在读取大文件时不会出现卡顿的现象。如果使用 vi 命令打开几十 M 上百 M 的文件,明显会出现有卡顿的现象,这是因为 vi 命令打开文件是一次性将文件加载到内存,然后再打开。Sed 就避免了这种情况,一行一行的处理,打开速度非常快,执行速度也很快

1
sed [OPTION]... 'script;script;...' [input-file]...

基本用法

OPTION

1
2
3
4
5
6
-n         # 不输出模式空间内容到屏幕,即不自动打印,如果不加-n,会自动打印每一行
-e # 当script有多个时,使用-e,多个script之间是或关系,$(-e 'script1' -e 'script2') 等同于 $('script1;script2')
-f file # 从指定文件中读取script
-r,-E # 支持扩展正则表达式
-i # 对文件直接进行修改,而不是输出到终端
-i.bak # 先备份原文件再修改文件

script

script 是 地址命令 的组合,地址用来定位,命令用来对定位的内容进行操作

地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
不给地址:对全文进行处理

单地址:
n:n是阿拉伯数数字,指定行
$:最后一行
/pattern/:匹配到的每一行

地址范围:
n,n:第n行到第n行 3,6 从第3行到第6行
n,+n:第n行开始,往后再匹配n行 3,+4 从第3行到第7行
/pattern1/,/pattern2/:从pattern1匹配到行,到pattern2匹配到的行,再往下继续,从pattern1匹配到行,到pattern2匹配到的行...

步进:~

first\~step:匹配first行,然后步进为step向后继续匹配,例如 1\~2 就是匹配奇数行、2\~2就是匹配偶数行

命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
p         # print 打印查询的行
Ip # 忽略大小写输出
d # 删除匹配到的行
a # append,追加内容到下一行,支持使用\n实现多行追加; 如果要解析空格,需要用反斜杠\
i text # insert,在行前面插入内容
c text # change,替换行
w file # 保存模式匹配的行到指定文件
r file # 从file中读取内容,插入到匹配到的行的下一行
= # 打印匹配到行的行号
! 命令 # 对没有匹配到的行进行处理,例如 !p 是打印未匹配到的行、!atext 是在未匹配到的行的下一行添加text

s/pattern/string/修饰符 # 查找替换
修饰符:
g # 一行里如果由多个匹配到的内容,全部替换。默认只替换第一个。
p # 打印替换成功的行
w file # 将成功替换的行保存至文件中
I,i # 忽略大小写

练习

1.将 php.ini-production 中的无关信息去掉

1
sed -E '/^; /d;/^;+;$/d' /usr/local/src/php-7.4.8/php.ini-production | uniq > php.ini

2.获取分区利用率

1
lujinkai@Z510:~$ df | sed -En '/^\/dev\/sd/s/.* ([0-9]+)%.*/\1/p'

3.取基名和目录名

1
2
3
# 使用/作为分隔符,如果搜索条件中出现/需要转义,所以为了易读性,不建议使用搜索条件中出现的字符作为分割符
pwd |sed -r 's/(^\/.*\/)([^\/]+\/?)/\2/' # 取基名
pwd |sed -r 's@(^/.*/)([^/]+/?)@\2@' # 取目录

4.将非#开头的行加#

1
2
3
4
5
lujinkai@Z510:~/data/test$ sed -Ei 's/^[^#].*/# &/' a.log
# 或
lujinkai@Z510:~/data/test$ sed -Ei 's/(^[^#].*)/# \1/' a.log
# 或
lujinkai@Z510:~/data/test$ sed -ri '/^#/!s/^/# /' a.log

5.将#开头的行删除#

1
lujinkai@Z510:~/data/test$ sed -ri 's/^#(.*)/\1/' a.log

高级用法

略。。。

文本搜索工具,对目标文本逐行进行匹配检查,打印匹配的行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
grep [OPTION]... PATTERN [FILE]...

-m n # 匹配n次后停止
-v # 对匹配的内容取反
-i # 忽略大小写
-n # 显示匹配内容的行号
-c # 统计匹配的行号
-o # 仅显示匹配的内容,而不是整行
-q # 静默模式,不输出任何信息
-A n # after,匹配到的每行,再向下多匹配n行
-B n # before,匹配到的每行,再向上多匹配n行
-C n # context,匹配到的行,再向上和向下各多匹配n行
-e # 当搜索条件有多个时,使用-e,多个搜索条件之间是或关系
-w # 匹配整个单词,例如我想匹配root,不加-w会匹配到rooter,加-w就不会出现这种情况
- -E # 相当于egrep
- -F # 支持正则表达式,相当于fgrep
-f file # 搜索条件可以写在文件中,每行是一个搜索条件,多个搜索条件之间是或的关系
- -r # 递归目录,但不处理软链接
- -R # 递归目录,处理软件链接

-e 示例:

1
2
3
lujinkai@Z510:~/data/test$ grep -e 'root' -e 'ljk' passwd
root:x:0:0:root:/root:/bin/bash
ljk:x:1001:1001::/home/ljk:/bin/bash

-w 示例:

-f file 示例:

1
2
3
4
5
lujinkai@Z510:~/data/test$ echo '^root' > re.txt
lujinkai@Z510:~/data/test$ cat re.txt
^root
lujinkai@Z510:~/data/test$ grep -f re.txt passwd
root:x:0:0:root:/root:/bin/bash

练习

1.取两个文件的相同行

1
2
# 思路:使用-f参数,将第一个文件作为搜索条件
grep -f f1.txt f2.txt

2.分区利用率最大的值

1
df -h | grep '^/dev/sd' | grep -Eo '[0-9]{,3}%' | tr -d % | sort -nr | head -n1

2.哪个 IP 和当前主机连接数最多的前三位

1

3.连接状态的统计

1

4.算出所有人的年龄总和

1
2
3
4
5
6
lujinkai@Z510:~/data/test$ cat age.txt
xiaoming=20
xiaohong=18
xiaoqiang=22

lujinkai@Z510:~/data/test$ grep -Eo '[0-9]{,2}' age.txt | paste -s -d+ | bc

awk 是一个文本处理工具,通常用于处理数据并生成结果报告。

awk 的工作模式和 sed 类似,也是逐行读取,逐行处理。

1
2
3
4
5
awk [-W option] [-F value] [-v var=value] [--] 'program text' [file ...]
awk [-W option] [-F value] [-v var=value] [-f program-file] [--] [file ...]

-F # 指定分隔符
-v # 变量赋值

语法格式

1
2
3
awk 'BEGIN{commands}pattern{commands}END{commands}' file
# 或
standard output | awk 'BEGIN{commands}pattern{commands}END{commands}'
  • BEGIN{commands}:正式处理文本之前执行,可以省略
  • END 后{commands}:处理完所有文本后执行,可以省略
  • pattern{commands}:逐行执行,pattern 判断此行是否符合匹配模式,符合则执行{commands}
    • pattern:匹配模式,可以省略
    • {commands}:对每行执行的命令,这里面可以写判断、循环等语句,功能强大,所以 awk 又被称为 awk 编程。不可省略

内置变量

1
2
3
4
5
6
7
8
9
10
11
12
$0          # 整行内容
$1 - $n # 当前行的第1-n个字段
NF # 当前行的字段个数,也就是有多少列
NR # 当前行的行号,从1开始计数
FNR # 多文件处理时,每个文件行号单独计数,从0开始
FS # 输入字段分割符。不指定默认就是空格或tab
RS # 输入行分割符号。默认回车换行
OFS # 输出字段分隔符。默认空格
ORS # 输出行分割符。默认回车换行
FILENAME # 处理文件的文件名
ARGC # 命令行参数个数
ARGV # 命令行参数数组

格式化输出 printf

格式符

1
2
3
4
5
6
7
%s    # 打印字符串
%d # 打印十进制数
%f # 打印一个浮点数
%x # 打印十六进制数
%o # 打印八进制数
%e # 打印数字的科学计数法形式
%c # 打印单个字符的ASCII码

修饰符

1
2
3
-    # 左对齐
+ # 右对齐
# # 显示八进制在前面加0,显示十六进制在前面加0x

模式匹配

第一种模式匹配:正则表达式 RegExp

第二种模式匹配:关系运算符号匹配

<、>、<、>=、!=
:匹配正则表达式
!
:不匹配正则表达式
||:或
&&:与
!:非

表达式

+、-、、/、%、^或*、++x、x++、–x、x–

-x:转换为负数
+x:将字符串转换为数值

条件语句

注:awk 中语句块没有作用域,都是全局变量。

1
2
3
4
5
6
if(条件表达式)
动作1
else if(条件表达式)
动作2
else
动作3
1
2
3
4
5
switch (expression) {
case value|regex : statement
...
[ default: statement ]
}

循环语句

1
2
while(条件表达式)
动作
1
2
3
do
动作
while(条件表达式)
1
2
for(i=0; i<100, i++)
动作

continue 和 break

continue 跳过本次循环,break 跳出整个循环

next

next 可以提前结束对本行处理而直接进入下一行处理(awk 自身循环)

1
[root@centos8 ~]#awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd

awk 内置函数

算数函数

  • rand:返回 0-1 之间一个随机数,需要配合 srand()配合生成随机数种子

    1
    [root@centos8 ~]#awk 'BEGIN{srand();print rand()}'
  • int:返回整数

字符串函数

  • length

  • index

  • tolower

  • toupper

  • substr

  • split

  • match

  • sub

  • gsub

时间函数

  • mktime:生成时间戳

  • strftime:将时间戳转化格式化时间输出

    1
    strftime("%Y-%m-%dT%H:%M",systime()) ;
  • systime:获取当前时间戳

其他函数

  • next:提前结束本行处理,进入下一行处理

  • system:system(commands),在 awk 中调用 shell 命令

  • exit

终止脚本执行

常用选项

1
2
3
4
-v    # 参数传递
-f # 指定脚本文件
-F # 指定分割符
-V # 查看awk的版本号

数组

shell 中数组的下标从 0 开始,awk 中数组下标从 1 开始,而且也有关联数组。但每次只能定义元素。如果想一次性定义多个元素,可以通过 split 间接实现。

1
2
3
4
str="a b c d e"
split(str,array," ")
for(i in array)
print array[i]

++

awk 中,数组[键]++ 表示给数组的元素赋值,赋值+1。灵活运用,可以实现去重、统计的功能

1
2
3
4
arr[a]=1
arr[b]=2

print arr[a]++ # 2

范例:显示主机的连接状态出现的次数

1
2
awk 'NR!=1{print $1}' ss.log |sort |uniq -c
ss -ant | awk 'NR>1 {++s[$1]} END {for(k in s) print k,s[k]}'

awk

脚本

将 awk 写成脚本,调用或直接执行

范例:调用

1
2
3
4
5
6
7
[root@centos8 ~]#cat passwd.awk
{if($3>=1000)print $1,$3}
[root@centos8 ~]
[root@centos8 ~]#awk -F: -f passwd.awk /etc/passwd
nobody 65534
wang 1000
mage 1001

范例:直接执行

1
2
3
4
5
6
7
8
9
10
[root@centos8 ~]#cat test.awk
#!/bin/awk -f
#this is a awk script
{if($3>=1000)print $1,$3}
[root@centos8 ~]
[root@centos8 ~]#chmod +x test.awk
[root@centos8 ~]#./test.awk -F: /etc/passwd
nobody 65534
wang 1000
mage 1001

练习:

检查出最近一小时内访问 nginx 服务次数超过 3 次的客户端 IP

1

1、文件 host_list.log 如下格式,请提取”.magedu.com”前面的主机名部分并写入到回到该文件中

1
2
3
4
5
6
7
1 www.magedu.com
2 blog.magedu.com
3 study.magedu.com
4 linux.magedu.com
5 python.magedu.com
......
999 study.magedu.com

2、统计/etc/fstab 文件中每个文件系统类型出现的次数

3、统计/etc/fstab 文件中每个单词出现的次数

4、提取出字符串 Yd$C@M05MB%9&Bdh7dq+YVixp3vpw 中的所有数字

5、有一文件记录了 1-100000 之间随机的整数共 5000 个,存储的格式 100,50,35,89…请取出其中最大和最小的整数

6、解决 Dos 攻击生产案例:根据 web 日志或者或者网络连接数,监控当某个 IP 并发连接数或者短时内 PV 达到 100,即调用防火墙命令封掉对应的 IP,监控频率每隔 5 分钟。防火墙命令为:iptables -AINPUT -s IP -j REJECT

7、将以下文件内容中 FQDN 取出并根据其进行计数从高到低排序

1
2
3
4
5
6
7
http://mail.magedu.com/index.html
http://www.magedu.com/test.html
http://study.magedu.com/index.html
http://blog.magedu.com/index.html
http://www.magedu.com/images/logo.jpg
http://blog.magedu.com/20080102.html
http://www.magedu.com/images/magedu.jpg

终端

1
2
3
4
- 控制台 /dev/console
- 串行终端 /dev/ttyS#
- 虚拟终端: tty: /dev/tty#
- 伪终端: pty: /dev/pts/#

终端、Shell、tty 和控制台(console)有什么区别? - 知乎
Linux 终端(tty)
Linux 伪终端(pty)

控制台 /dev/console

计算机启动的时候,所有的信息都会显示到控制台上,而不会显示到终端上

串行终端 /dev/ttyS#

计算机串行端口连接的终端设备, 这些串行端口所对应的设备名称是/dev/tty0、/dev/tty1 等,分别对应于系统下的 COM1、COM2 等

虚拟终端: tty: /dev/tty#

每打开一个 terminal,系统会启动一个 shell 与这个 terminal 关联起来, shell=命令行解释器, tty 和 terminal 是同义词

tty 是虚拟终端, 与之对应的是 console 是物理终端

伪终端: pty: /dev/pts/#

使用 tty1-6 的情况一般为 Linux 系统直接连了键盘和显示器,或者是使用了 vSphere console 等虚拟化方案,其它情况下使用的都是伪终端, 通过 SSH 等方式建立的连接中使用的都是伪终端

系统信息

1
2
3
4
5
6
7
8
lscpu
free
lsblk 和 df
uname
cat /etc/redhat-release
cat /etc/os-release
cat /etc/centos-release
lsb_release
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# 查看cpu
[lujinkai@centos8 ~]$ lscpu
[lujinkai@centos8 ~]$ cat /proc/cpuinfo
# 查看内存
[lujinkai@centos8 ~]$ free -h
[lujinkai@centos8 ~]$ cat /proc/meminfo
# 查看硬盘和分区情况
[root@centos8 ~]# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 200G 0 disk
├─sda1 8:1 0 1G 0 part /boot
├─sda2 8:2 0 100G 0 part /
├─sda3 8:3 0 50G 0 part /data
├─sda4 8:4 0 1K 0 part
└─sda5 8:5 0 2G 0 part [SWAP]
sr0 11:0 1 1.6G 0 rom
# -f 显示文件系统的信息
[root@centos8 /]# lsblk -f
NAME FSTYPE LABEL UUID MOUNTPOINT
sda
├─sda1 ext4 a489cd2b-daf2-4d94-90ab-696a21284f6a /boot
├─sda2 xfs 30e97194-a6ff-42cb-b6e0-3374d3e614e7 /
├─sda3 xfs 721abfdb-0d93-4559-9ac1-a487ecabf339 /data
├─sda4
└─sda5 swap 1b064969-1539-4d0a-ab1f-6f304f99ac9e [SWAP]
sr0 iso9660 CentOS-8-2-2004-x86_64-dvd 2020-06-08-22-08-12-00
[lujinkai@centos8 ~]$ cat /proc/partitions
major minor #blocks name

8 0 209715200 sda
8 1 1048576 sda1
8 2 104857600 sda2
8 3 52428800 sda3
8 4 1 sda4
8 5 2097152 sda5
11 0 1678336 sr0

# 查看系统版本信息
# 查看内核版本
[lujinkai@centos8 ~]$ uname
Linux
[lujinkai@centos8 ~]$ uname -r
4.18.0-80.el8.x86_64
[lujinkai@centos8 ~]$ uname -a
Linux centos8.mageedu.org 4.18.0-80.el8.x86_64 #1 SMP Tue Jun 4 09:19:46 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
# 查看操作系统发行版本
[lujinkai@centos8 ~]$ cat /etc/redhat-release
CentOS Linux release 8.0.1905 (Core)
[lujinkai@centos8 ~]$ cat /etc/os-release
NAME="CentOS Linux"
VERSION="8 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="8"
PLATFORM_ID="platform:el8"
PRETTY_NAME="CentOS Linux 8 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:8"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"

CENTOS_MANTISBT_PROJECT="CentOS-8"
CENTOS_MANTISBT_PROJECT_VERSION="8"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="8"
[lujinkai@centos8 ~]$ lsb_release -a
LSB Version: :core-4.1-amd64:core-4.1-noarch
Distributor ID: CentOS
Description: CentOS Linux release 8.0.1905 (Core)
Release: 8.0.1905
Codename: Core

用户登录信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 我是谁
[lujinkai@centos8 ~]$ whoami
lujinkai
# 我是谁 详细
[lujinkai@centos8 ~]$ who am i
lujinkai pts/1 2020-07-27 22:12 (10.0.0.1)
# 当前所有已登录用户信息
[lujinkai@centos8 ~]$ who
root tty1 2020-07-27 22:21
root pts/0 2020-07-27 22:11 (10.0.0.1)
lujinkai pts/1 2020-07-27 22:12 (10.0.0.1)
# 当前所有已登录用户信息 显示正在执行的操作
[lujinkai@centos8 ~]$ w
22:21:22 up 10 min, 3 users, load average: 0.05, 0.08, 0.09
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root tty1 - 22:21 18.00s 0.01s 0.01s -bash
root pts/0 10.0.0.1 22:11 10:12 0.02s 0.02s -bash
lujinkai pts/1 10.0.0.1 22:12 2.00s 0.08s 0.02s w

主机名

1
2
3
hostname NAME                 # 临时生效
hostnamectl set-hostname NAME # 永久生效
# 永久生效还可以修改文件 /etc/hostname
  1. 主机名不支持使用下划线_ , 可以使用字母、横线或数字组合
  2. 主机名不要使用数字结尾,因为有些软件对主机名有要求

命令提示符

1
2
3
4
5
6
7
8
9
10
11
[root@centos8 ~]$echo $PS1
\[\e[37;40m\][\[\e[32;40m\]\u\[\e[37;40m\]@\h \[\e[35;40m\]\W\[\e[0m\]]$

\e # 控制符\033
\u # 当前用户
\h # 主机名简称
\H # 主机名
\w # 当前工作目录
\W # 当前工作目录基名
\t # 24小时时间格式
\T # 12小时时间格式

命令

命令格式

三种风格:短选项(UNIX)、长选项(GNU)、BSD 风格选项

ctrl + c : 终止命令执行;ctrl + d : 退出终端

内部命令和外部命令

内部命令是 shell 内置的,外部命令是自己安装的

1
2
3
4
5
6
7
8
9
10
type -a COMMAND  # 判断一个命令是内部命令还是外部命令

# 内部命令:
enable COMMAND # 启用内部命令
enable -n COMMAND # 禁用内部命令
enable -n # 查看所有禁用的内部命令

# 外部命令:
which COMMAND # 查看外部命令的执行文件路径
whereis COMMAND # 除了查看外部命令执行文件路径,还能查看man文件的路径等信息

命令执行顺序

1
别名 > 内部命令 > hash > $PATH > command not found

echo

打印变量的时候,变量加引号的规则和 php 一样,单引号当成字符串输出,双引号则解析变量

-e:启用 \ 字符的解释功能 (escapes 转义符)

1
2
3
4
5
6
7
8
9
10
11
12
13
\a             # 发出警告声
\b # 退格键
\c # 最后不加上换行符号
\e # escape,相当于\033
\n # 换行且光标移至行首
\r # 回车,即光标移至行首,但不换行
\t # 插入tab
\ # 插入\字符
\0nnn # 插入nnn(八进制)所代表的ASCII字符
\xHH插入HH(十六进制) # 所代表的ASCII数字(man 7 ascii)

# 示例:
$echo -e "\a"

字符集和编码

Unicode 是字符集, Unicode 的编码方案有 UTF-8、UTF-16、UTF-32

bash shell

1
2
3
4
# 显示当前使用的shell
echo $SHELL
# 显示当前系统使用的所有shell
cat /etc/shells

bash 快捷键

Ctrl + a 光标移到命令行首,相当于 Home
Ctrl + e 光标移到命令行尾,相当于 End

hash 缓存表

shell 内部命令本来就在内存中, hash 缓存的是外部命令的路径

1
2
3
4
5
6
# 显示hash缓存
hash
# 清除某条命令缓存
hash -d COMMAND
# 清除所有命令的内存
hash -r

日期和时间

1
2
3
4
5
6
date
-d # display time described by STRING, not 'now'
-s # set time described by STRING
clock
timedatectl
cal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 显示时间戳
[lujinkai@centos8 ~]$ date +%s
1595857012
# 将时间戳格式化
[lujinkai@centos8 ~]$ date -d @`date +%s`
Mon Jul 27 21:40:31 CST 2020
[lujinkai@centos8 ~]$ date -d @`date +%s` +%F
2020-07-27
[lujinkai@centos8 ~]$ date -d @1595857012 +%F_%T
2020-07-27_21:40:34
[lujinkai@centos8 ~]$date "+%F %T"
2021-03-16 01:12:53
# 昨天
[lujinkai@centos8 ~]$ date -d '-1 day' +%F
2020-07-27
# 明天
[lujinkai@centos8 ~]$ date -d '1 day' +%F
2020-07-29

# %F 完整日期格式,等价于 %Y-%m-%d
# %T 时间,等于%H:%M:%S
1
2
3
4
# 以硬件时间为准, 校正系统时间
[root@centos8 ~]# clock -s
# 以系统时间为准, 校正硬件时间
[root@centos8 ~]# clock -w
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 显示系统全部的时区
[root@centos8 ~]# timedatectl list-timezones
# 查找上海
[root@centos8 ~]# timedatectl list-timezones | grep 'Shanghai'
Asia/Shanghai
# 设置时区为上海
[root@centos8 ~]# timedatectl set-timezone Asia/Shanghai
# 查看
[root@centos8 ~]# timedatectl status
Local time: Mon 2020-07-27 21:56:19 CST
Universal time: Mon 2020-07-27 13:56:19 UTC
RTC time: Mon 2020-07-27 13:56:19
Time zone: Asia/Shanghai (CST, +0800)
System clock synchronized: no
NTP service: inactive
RTC in local TZ: no
# 查看当前所在的时区
[root@centos8 ~]# ll /etc/localtime
lrwxrwxrwx. 1 root root 35 Jul 23 16:33 /etc/localtime -> ../usr/share/zoneinfo/Asia/Shanghai
1
2
3
4
5
6
# 日历
# 显示全年日历
[root@centos8 ~]# cal -y
[root@centos8 ~]# cal 2020
# 显示一个月的日历
[root@centos8 ~]# cal 7 2020

关机和重启

1
2
3
4
5
6
7
halt     # 关机 相当于 shutdown -h
poweroff # 关机 相当于shutdown -p
reboot # 重启 相当于shutdown -r

shutdown -h +3 # 定时关机 3分钟后自动关机
shutdown -h 00:00 "class is over" # 定时关机 00:00关机 并提示'class is over'
shutdown -c # 取消关机

但是在 VMware 中, CentOS7、8 和 Ubuntu18.04 关机的时候, 使用halt会报错, 使用shutdown -h now就能顺利关机, 不知道为什么?

计算器 bc

-q 表示不显示欢迎信息

bc 有四个内置变量,我们在计算时会经常用到,如下表所示:

变量名 作 用
scale 指定精度,也即小数点后的位数;默认为 0,也即不使用小数部分。
ibase 指定输入的数字的进制,默认为十进制。
obase 指定输出的数字的进制,默认为十进制。
last 或者 . 表示最近打印的数字

注意:obase 要尽量放在 ibase 前面,因为 ibase 设置后,后面的数字都是以 ibase 的进制来换算的

命令历史 history

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 显示最近5条历史命令
[root@4710419222 test]# history 5
7618 2020-07-28 19:19:03 root set -B
7619 2020-07-28 19:19:05 root ls {1,4,6}.log
7620 2020-07-28 19:21:46 root history
7621 2020-07-28 19:22:06 root history 10
7622 2020-07-28 19:22:16 root history 5
# 清除此次登录记录的命令
[root@4710419222 ~]# history -c
[root@4710419222 ~]# history
1 2020-07-28 19:23:15 root history
# !n 执行第n条历史命令
# !$ 调用前一条命令的最后一个参数
# !* 调用前一条命令的所有参数
# ctrl + r 搜索历史命令快捷键
# !string 执行上一条以string开头的命令

相关环境变量:

1
2
3
4
5
6
7
8
9
10
HISTSIZE  # 命令历史记录的条数
HISTFILE # 指定历史文件,默认为~/.bash_history
HISTFILESIZE # 命令历史文件记录历史的条数
HISTTIMEFORMAT="%F %T \`whoami\` " # 显示时间和用户
HISTIGNORE="str1:str2*:…" # 忽略str1命令,str2开头的历史
HISTCONTROL # 控制命令历史的记录方式
ignoredups # 是默认值,可忽略重复的命令,连续且相同为“重复”
ignorespace # 忽略所有以空白开头的命令
ignoreboth # 相当于ignoredups, ignorespace的组合
erasedups # 删除重复命令
1
2
[root@centos8 profile.d]# echo 'export HISTTIMEFORMAT="%F %T `whoami` "' >> ~/.bash_profile
[root@centos8 profile.d]# echo 'export HISTCONTROL=ignoreboth' >> ~/.bash_profile

获得帮助 (重点)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 第一步: 判断命令为内部命令还是外部命令, 如果二者都是, 则查询内部命令
[root@4710419222 ~]# type -a echo
echo is a shell builtin
echo is /usr/bin/ech
# 第二步: 使用whatis查询man的分组
[root@4710419222 ~]# whatis echo
echo (1) - display a line of text
echo (3x) - curses input options
# 第三步
[root@4710419222 ~]# man 1 echo
# 如果是外部命令, 简单的查询可以省略第二步, 直接 --help
[root@4710419222 ~]# type hostname
hostname is /usr/bin/hostname
[root@4710419222 ~]# hostname --help

范例二:

1
2
3
4
5
6
7
8
[root@centos8 ~]# type -a history
history is a shell builtin
[root@centos8 ~]# whatis history
history (1) - bash built-in commands, see bash(1)
[root@centos8 ~]# man 1 history
# 此时发现找不到history
# 可以使用 /keyword 进行搜索, 使用 n 和 N 进行上下查找
# 然后很容易就找到了history的man手册

man 页面分组

不同类型的帮助称为不同的“章节”,统称为 Linux 手册

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
       1   Executable programs or shell commands
2 System calls (functions provided by the kernel)
3 Library calls (functions within program libraries)
4 Special files (usually found in /dev)
5 File formats and conventions eg /etc/passwd
6 Games
7 Miscellaneous (including macro packages and conventions), e.g. man(7), groff(7)
8 System administration commands (usually only for root)
9 Kernel routines [Non standard]
1:用户命令
2:系统调用
3:C库调用
4:设备文件及特殊文件
5:配置文件格式
6:游戏
7:杂项
8:管理类的命令
9:Linux 内核API

man 帮助段落说明

  • NAME 名称及简要说明
  • SYNOPSIS 用法格式说明
  • [] 可选内容
  • <> 必选内容
  • a|b 二选一
  • { } 分组
  • … 同一内容可出现多次
  • DESCRIPTION 详细说明
  • OPTIONS 选项说明
  • EXAMPLES 示例
  • FILES 相关文件
  • AUTHOR 作者
  • COPYRIGHT 版本信息
  • REPORTING BUGS bug 信息
  • 从 SEE ALSO 其它帮助参考

man 命令的操作方法

  • space 下一页
  • b 上一页
  • 1G 回到首部
  • G 翻至文件尾部
  • /keyword 搜索关键字, 使用 n 和 N 上下查找