Centos7-Linux

Centos7-Linux-RH

Linux操作系统(百度百科):全称GNU/Linux,是一套免费使用和自由传播的类UNIX操作系统,其内核由林纳斯·本纳第克特·托瓦兹于1991年第一次释出,它主要受到Minix和Unix思想的启发,是一个基于POSIX和Unix的多用户、多任务、支持多线程和多CPU的操作系统。它能运行主要的Unix工具软件、应用程序和网络协议。它支持32位和64位硬件。Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。Linux有上百种不同的发行版,如基于社区开发的debian、archlinux,和基于商业开发的[Red Hat Enterprise Linux](https://baike.baidu.com/item/Red Hat Enterprise Linux/10770503)、SUSE、[oracle linux](https://baike.baidu.com/item/oracle linux/6876458)等。

一、简述

1.1 什么是CentOS

CentOS(Community Enterprise Operating System,中文意思是社区企业操作系统)是Linux发行版之一,它是来自于Red Hat Enterprise Linux依照开放源代码规定释出的源代码所编译而成。由于出自同样的源代码,因此有些要求高度稳定性的服务器以CentOS替代商业版的[Red Hat](https://baike.baidu.com/item/Red Hat) Enterprise Linux使用。两者的不同,在于CentOS完全开源。

【扩展】CentOS和Ubuntu区别

1.centos中普通用户没有sudo权限,需在sudoers文件中添加用户的权限。
2.ubuntu中普通用户,直接使用sudo+命令行的方式即可。
3.ubuntu(apt/apt-get)与centos (yum) 安装软件包命令的格式不一样。centos是支持rpm包的,ubuntu不支持rpm包。

【Vi编辑器的简单使用】

移动光标, 按i字母, 进入Insert模式,可以按方向键左右上下移动,按Del键删除(向左删除-Mac),Window是backspace 回退键。编完之后,按ESC 取消插入模式, 再按Shift+: 进入命令行模式,输入wq 或 x,最后回车确认退出并保存。

1.2 主流的CentOS版本

1.2.1 CentOS-7

CentOS 7于2014年7月7号正式发布,这是一个企业级的Linux发行版本,基于Red Hat红帽免费公开的源代码。相对于之前版本,升级内容如下:

  • 支持新的处理器(Broadwell)及图像卡(AMD hawaii)
  • 全面支持 LVM 缓存
  • 可挂载 ceph 块设备
  • 更新 Hyper-V 网络驱动程序
  • 新的 libguestfs 功能
  • 全面支持 openJDK-1.8.0
  • 改善时钟(PTP 及 NTP)的稳定性
  • 更新 Networkmanager 组件至 1.0 版本
  • 更新 docker 至 1.4.1
  • 更新 openSSh 至 6.6.1
  • 新组件:Mozilla Thunderbird
  • 更新多个存储、网络及图像卡驱动程序
  • 科技预览:支持 Btrfs 文件系统、OverlayFS 及 Cisco VIC 内核驱动程序

1.2.2 CentOS-8

首个8.0版本于2019.09.25发布,最新版本为 CentOS 8.1.1911

相对于 RHEL 7主要改进:

内核更新至3.10.0;支持Linux容器(Docker),Open VMware Tools及3D图像能即装即用,Open JDK7作为缺省JDK,ext4及XFS的LVM快照,转用systemd、firewalld及GRUB2,XFS作为缺省文件系统,内核空间内的iSCSI及FCoE,支持PTPv2,支持40G网卡等。

1.3 Linux的目录结构

[root@localserver ~]# ls / -ll
总用量 16
lrwxrwxrwx.   1 root root    7 6月   8 10:22 bin -> usr/bin
dr-xr-xr-x.   5 root root 4096 6月   8 10:30 boot
drwxr-xr-x.  21 root root 3280 6月   8 10:29 dev
drwxr-xr-x.  74 root root 8192 6月   8 10:32 etc
drwxr-xr-x.   2 root root    6 4月  11 2018 home
lrwxrwxrwx.   1 root root    7 6月   8 10:22 lib -> usr/lib
lrwxrwxrwx.   1 root root    9 6月   8 10:22 lib64 -> usr/lib64
drwxr-xr-x.   2 root root    6 4月  11 2018 media
drwxr-xr-x.   2 root root    6 4月  11 2018 mnt
drwxr-xr-x.   2 root root    6 4月  11 2018 opt
dr-xr-xr-x. 120 root root    0 6月   8 10:29 proc
dr-xr-x---.   2 root root  135 6月   8 10:36 root
drwxr-xr-x.  24 root root  720 6月   8 10:36 run
lrwxrwxrwx.   1 root root    8 6月   8 10:22 sbin -> usr/sbin
drwxr-xr-x.   2 root root    6 4月  11 2018 srv
dr-xr-xr-x.  13 root root    0 6月   8 10:29 sys
drwxrwxrwt.   7 root root  132 6月   8 10:32 tmp
drwxr-xr-x.  13 root root  155 6月   8 10:22 usr
drwxr-xr-x.  19 root root  267 6月   8 10:29 var
  • / 根目录
  • /bin 基础系统所需要的命令位于此目录,是最小系统所需要的命令,如:ls, cp, cd等等。这个目录中的文件都是可执行的,一般的用户都可以使用。
  • /dev 设备文件,比如声卡、磁盘、鼠标、键盘等。
  • /etc 系统管理和配置文件
    • /etc/init.d 启动配置文件和脚本,可在这里添加启动脚本。
    • /etc/rc.local 用户添加启动项
  • /home 用户主目录,比如用户user的主目录就是/home/user,可以用~user表示
  • /lib 标准程序设计库存放路径,又叫动态链接共享库(.so 文件),作用类似windows里的.dll文件
  • /sbin 超级管理命令,这里存放的是系统管理员使用的管理程序。
  • /tmp 临时文件目录,有时用户运行程序的时候,会产生临时文件。 /tmp就用来存放临时文件的。
  • /root 系统管理员的主目录
  • /mnt 用来临时挂载其他的文件系统
  • /lost+found 这个目录平时是空的,系统意外崩溃或机器意外关机,而产生一些文件碎片放在这里。当系统启动的过程中fsck工具会检查这里,并修复已经损坏的文件系统。
  • /media 即插即用型存储设备的挂载点自动在这个目录下创建,比如USB盘系统自动挂载后,会在这个目录下产生一个目录 。
  • /proc 虚拟文件目录,可直接访问这个目录来获取系统信息,如version、zoneinfo、meminfo、cpuinfo等,示例命令: cat /proc/version
  • /var 所有服务的登录文件或错误日志档案(log files)都在 /var/log 里面
  • /boot 包含Linux内核及系统引导程序所需要的文件,比如 vmlinuz initrd.img 文件都位于这个目录中。在一 般情况下,GRUB或LILO系统引导管理器也位于这个目录;
  • /usr 最庞大的目录,要用到的应用程序和文件几乎都在这个目。

1.4 Linux系统核心组成

  • 系统内核
    • 进程调度
    • 内存管理
    • 虚拟文件系统
    • 网络接口
    • 进程间通信
  1. Shell : 集成Linux指令和业务逻辑的脚本文件。
  2. 文件系统: 包含各种媒体(光盘、U盘、硬盘、内存)中文件的管理。
  3. 应用程序集: 可以在Linux下执行的应用程序,包含Linux内部开发的应用或命令。

【目标】学习Linux时,除了认知它的文件体系之外,应该掌握常用的指令、Vim编缉器的使用、Shell脚本的写法和各类服务应用的安装与管理等。

1.5 查看帮助

学习Linux的命令,可以参考https://www.runoob.com/linux/linux-tutorial.html文档

在学习Linux丰富的指令之前,可以先学习以下几个实用的命令:

  • man 查看命令帮助信息

    用法: man 命令

    进入命令之后,有以下四个按键功能:

    • w 上一页
    • d 或 空格space 下一页
    • 下一行
    • q 退出

    示例1: man ls

  • -h 命令的帮助信息

    用法: 命令 -h 或 --help

    示例1: ls --help

    示例2: ip -h

  • more 可以将一个命令的多页内容进行分页

    用法: 命令 | more

    示例1: man cd | more

  • tail 显示多页内容的末尾信息,可以通过-n 10指定显示末尾10行

    用法: 命令 | tail -n 10

    示例: man ls | tail -n 30

  • which 查看命令是否存在

    用法: which 命令

二、文件命令

2.1 查看文件

2.1.1 ls命令

命令格式: ls [options] [file]

常用参数:

-a 显示所有文件及目录 (ls内定将文件名或目录名称开头为"."的视为隐藏档,不会列出)
-l 除文件名称外,亦将文件型态、权限、拥有者、文件大小等资讯详细列出
-r 将文件以相反次序显示(原定依英文字母次序)
-t 将文件依建立时间之先后次序列出
-A 同 -a ,但不列出 "." (目前目录) 及 ".." (父目录)
-F 在列出的文件名称后加一符号;例如可执行档则加 "*", 目录则加 "/"
-R 若目录下有文件,则以下之文件亦皆依序列出
-i 显示文件的iNode的信息(文件索引号)
--full-time  显示完整的文件修改时间(默认显示 MM dd hh:mm)

示例:

ls -a 
ls -ltrF

2.1.2 ll命令

是ls命令的别名,可以通过which 查看:

# which ll
alias ll='ls -l --color=auto'

【扩展】 alias 命令别名,可以将复杂的命令指定简化的名称。如:

alias la='ls -lA --color=auto'

取消一个命令的别名,可以使用unalias 命令的别名,如:

unalias la

2.1.3 文件描述

[root@localhost /]# ls -la
总用量 16
dr-xr-xr-x.  17 root root  224 6月  17 16:20 .
dr-xr-xr-x.  17 root root  224 6月  17 16:20 ..
lrwxrwxrwx.   1 root root    7 6月  17 16:13 bin -> usr/bin
dr-xr-xr-x.   5 root root 4096 6月  17 16:23 boot
drwxr-xr-x.  20 root root 3080 7月   5 21:24 dev
drwxr-xr-x.  74 root root 8192 7月   5 21:24 etc
drwxr-xr-x.   2 root root    6 4月  11 2018 home
lrwxrwxrwx.   1 root root    7 6月  17 16:13 lib -> usr/lib
lrwxrwxrwx.   1 root root    9 6月  17 16:13 lib64 -> usr/lib64
drwxr-xr-x.   2 root root    6 4月  11 2018 media
drwxr-xr-x.   2 root root    6 4月  11 2018 mnt
drwxr-xr-x.   2 root root    6 4月  11 2018 opt
dr-xr-xr-x. 105 root root    0 7月   5 21:24 proc
dr-xr-x---.   3 root root  178 7月   5 21:59 root
drwxr-xr-x.  23 root root  700 7月   5 21:24 run
lrwxrwxrwx.   1 root root    8 6月  17 16:13 sbin -> usr/sbin
drwxr-xr-x.   2 root root    6 4月  11 2018 srv
dr-xr-xr-x.  13 root root    0 7月   5 21:24 sys
drwxrwxrwt.   7 root root   93 7月   5 21:25 tmp
drwxr-xr-x.  13 root root  155 6月  17 16:13 usr
drwxr-xr-x.  19 root root  267 6月  17 16:22 var

第一行是 总文件及目录的个数

第二行开始,第一列的内容表示的含义如下:

第一位: 文件类型, - 文件, d  目录  l 软连接 s 表示该文件为sock文件 
第二位-第四位: 表示文件所属用户的权限, 三位长度,分别表示 r 读, w 写, x可执行  - 表示 无
第五位-第七位: 表示文件所属用户组的权限
第八位-第10位:  表示其它组的用户权限

【注意】第一位字符,还可能是c(表示该文件是一个字符设备文件(character),一般置于/dev目录下,一次传输一个字节的设备被称为字符设备,如键盘、字符终端等,传输数据的最小单位为一个字节)、b(表示块设备文件(block),一般置于/dev目录下,设备文件是普通文件和程序访问硬件设备的入口,是 很特殊的文件。没有文件大小,只有一个主设备号和一个辅设备号。一次传输数据为一整块的被称为块设备,如硬盘、光盘等。最小数据传输单位为一个数据块(通常一个数据块的大小为512字节)、p(表示该文件为命令管道文件。与shell编程有关的文件)。

第二列:表示文件的硬连接的数量。

第三列: 表示所属用户

第四列: 表示所属用户组

第五列: 表示文件的大小,目录的大小为0

第六列和第七列: 表示月份和几日

第八列: 表示文件修改的时间

最后列: 表示文件或目录的名称

2.1.4 cd命令

格式: cd 目录名

目录名的特殊符号:

  • ~ 代表当前用户的家(home)目录
  • / 代表根目录 , Linux的文件最起始的位置。
  • 在Window的gitbash 中 每个盘符都以/开始,如c盘,/c

2.1.5 pwd命令

功能:显示当前的目录

【扩展】在python中,获取当前目录位置:

os.getcwd()
os.path.dirname(os.path.abspath(__file__))

2.2 创建与删除

2.2.1 mkdir 创建目录

格式: mkdir [options] 目录名

参数:

-p 确保目录名称存在,不存在的就建一个【级联创建】
-m  指定目录的权限 mode( 如 chmod),  建议使用权限值;如果使用rwx字符时,建议使用a=rwx, 但不能使用-。
-v 打印创建的目录信息

示例:

mkdir -m 666 abc
mkdir -m a=rx sql
mkdir -m u=rw,g=rx,o=rx sql2

【注意】 mkdir 命令可以同时创建多个目录,如:

mkdir a1 a2 a3
[root@localhost ~]# mkdir -pv apiserver/spider1/code
mkdir: 已创建目录 "apiserver"
mkdir: 已创建目录 "apiserver/spider1"
mkdir: 已创建目录 "apiserver/spider1/code"

2.2.2 touch 创建文件

格式: touch [参数] 文件名或目录名

touch命令可以创建一个空白内容的新文件,也可以不修改文件内容的情况下更新文件最后修改时间。

参数:

-c 当文件不存在时,不创建任何文件
-d, --date=String 指定更新的时间来替换当前时间, 如 touch -d 12:10 a.txt
-m 仅修改时间, 默认行为
-t 使用 [[CC]YY]MMDDhhmm[.ss] 替换当前时间

示例:

touch -t 202010201510 a5.txt 
touch -c a1.txt a.txt a2.txt abc sql  

【注意】可以更新多个文及目录的最近修改时间

2.2.3 rmdir 删除目录

命令格式同mkdir,如下示例:

rmdir -pv 目录/子目录

【注意】删除空的目录

2.2.4 rm 删除

格式: rm [options] 文件或目录名

功能: 可以删除文件和目录。

参数:

-f 强制删除
-r 删除所有子目录及文件
-d 删除空目录
-v 显示删除操作

示例:

rm -rf apiserver
 rm -rf ./*

【注意】./* 表示当前目录下的所有文件及子目录。

2.3 复制与移动

2.3.1 cp命令

格式: cp [options] src dst

功能: 将 src 复制到 dst位置

参数:

-a:此选项通常在复制目录时使用,它保留链接、文件属性,并复制目录下的所有内容。其作用等于dpR参数组合。
-d:复制时保留链接。这里所说的链接相当于Windows系统中的快捷方式。
-f:覆盖已经存在的目标文件而不给出提示。【注意】在CentOS中,好像不起作用。
-i:与-f选项相反,在覆盖目标文件之前给出提示,要求用户确认是否覆盖,回答"y"时目标文件将被覆盖。
-p:除复制文件的内容外,还把修改时间和访问权限也复制到新文件中。
-r:若给出的源文件是一个目录文件,此时将复制该目录下所有的子目录和文件。
-l:不复制文件,只是生成链接文件(硬连接)。

【注意】 文件名或目录名可以使用 *通配符表示任意多个任意字符

示例

cp -r abc bcd/
cp abc/a.txt bcd/b.txt
cp sql/*.sql bcd/abc/
cp -l sql/1.sql 1.sql  创建sql/1.sql 文件的硬连接

2.3.2 mv命令

功能: 将源文件或目录移动目标位置上, 可以实现文件重命名的功能。

格式:

mv [OPTION] [-T] SOURCE DEST
mv [OPTION] SOURCE  DIRECTORY
mv [OPTION]  -t DIRECTORY SOURCE 

参数:

-f 强制
-t 指定目标位置

示例:

mv sql/2.sql .
rm -f sql/2.sql  # 报错, sql/2.sql 文件不存在。因为上一个命令将它移动了。
mv 2.sql  test2.sql  # 修改2.sql 的名称为 test2.sql

【提示】. 当前目录, ..父级目录

2.4 文件查找

命令格式: find [path] [表达式语句]

表达式语句的条件:

-name pattern:  匹配指定模式的文件名,可以使用通配符 *,? 
-iname pattern : 不区分大小写的文件名匹配
-readable  文件可读
-writable  文件可写
-executable 文件可执行
-maxdepth n 查找文件最大的深度, 【注意】应该在-name或-iname参数前面。
-depth n 从当前层次开始,查看第n子层次的文件
-delete 查出的文件,将会被删除

示例:

find / -name "*.txt" 
find abc  -maxdepth 2 -name "*.txt"
find . -name '*.txt' > txt_files.txt   # 将查找到的所有文本文件结果写入到 txt_files.txt文件中

面试题: 统计python的代码行数

find code -name "*.py" |xargs cat|grep -E ^$| wc -l

2.5 内容查看

2.5.1 cat 命令

命令: cat [OPTION] FILE

参数:

 -A, --show-all 显示所有内容
 -n 显示行号

示例:

cat 1.sql  # 显示文件的内容
cat -nb 1.sql  # 显示文件内容,并显示行号, 但空行不显示行号
cat -nA 1.sql  # 显示文件内容,包含行结束符 $

2.5.2 grep命令

可以和cat或find组合使用,为显示的数据增加显示或查找条件。

命令格式:

grep [OPTIONS] PATTERN [FILE...]
grep [OPTIONS] [-e PATTERN | -f FILE] [FILE...]

参数:

-E  pattern 指定正则表达式,使用单引号,且减少转义字符的使用。如同行`^$`, 数字`[0-9]+`
-m  指定匹配的数量,默认是全部
-n  显示行号
-v  pattern 不选择或显示匹配的规则

示例:

cat test2.sql|grep disen  # 过滤文件中是否存在disen
grep -E ^[0-9]+$ test2.sql  # 显示文件中的每一行的数值
grep -E ^[0-9]+$ -m 1 test2.sql  # 显示每一行的匹配的数值
grep -E ^.*$ -n test2.sql   # 显示每一行的内容,并显示行号
cat test2.sql |grep -E ^.+$ | wc -l
grep -v ^$ test2.sql # 不显示 test2.sql 文件中的空行的其它行内容

【扩展】正则的量词的写法(7种):

* 最少0次
?最多1次
+ 至少1次
{n} 必须是n次
{n, m} 在[n, m]区间范围的次数
{n,} 至少n 次
{,m} 最多m次

【注意】在Linux中正则不包含转义字符。

2.5.3 xargs命令

格式: 命令 | xargs [参数] [command ] , 不带command ,默认的使用echo 输出

作用:

- 数据以空格进行分隔
- 可以根据参数进行一次或多次处理,默认的处理命令是/bin/echo
- 空行不进行处理,会被忽略
- 遇到命令状态为255时,xargs会立刻停止,譬如发生错误时.

参数:

xargs  [-0prtx] [-E  eof-str] [-e[eof-str]] [--eof[=eof-str]] [--null]
       [-d delimiter] [--delimiter delimiter]  [-I  replace-str]  [-i[replace-
       str]] [--replace[=replace-str]]   [-l[max-lines]]   [-L   max-lines]
       [--max-lines[=max-lines]] [-n max-args] [--max-args=max-args] [-s  max-
       chars]  [--max-chars=max-chars] [-P max-procs] [--max-procs=max-procs]
       [--interactive]     [--verbose]     [--exit]   [--no-run-if-empty]
       [--arg-file=file]   [--show-limits]   [--version]   [--help]   [command
       [initial-arguments]]

常用示例:

xargs -a hello.txt  # 读取hello.txt文件内容,将以空格分隔每一行的数据
xargs -a hello.txt -E 'next'  # 读取数据时,截止到第一次出现next位置(不包含)
cat my.dat | xargs -L 3  #  一次性读取3行,将换行符转成空格,再使用echo 显示
find . -name '*.py' | xargs
find . -name "hello.txt" | xargs cat
find . -name '*.py' | xargs -E './2.py'  cat
	查找所有当前目录的.py文件, 并将查询的结果 交给 xargs
	xargs 读取 './2.py'之前的所有文件
	并显示每个py文件的内容
find . -iname 'S1.Sh'|xargs bash

【提示】s1.sh内容如下:

#!/bin/bash

echo 'hi, disen'
 xargs -a t3.txt -s 20 echo

【提示】-s 表示 后面执行的命令行总长度的最大限制,如 echo disen$ create$总长度 19个字符

2.6 文件连接

2.6.1 软硬连接

在Linux中,一个文件存储分为三块: 文件名、INode(文件的索引编号, ls -i可以查看)和 文件。

硬连接: 硬连接就是一个文件有两个名字。 【注意】文件目录不能创建硬连接

软连接: 软连接是一个完整的文件,软连接最终指向另外一个文件。

【提示】软连接指向的文件被删除后,软连接依然存在,另外重新在连接位置添加文件,还可以继续指向。

2.6.2 ln命令

格式: ln [参数] 源文件或目录 目标文件或目录

必要参数:

-b 删除,覆盖以前建立的链接
-d 允许超级用户尝试制作目录的硬链接
-f 强制执行
-i 交互模式,文件存在则提示用户是否覆盖
-n 把符号链接视为一般目录
-s 软链接(符号链接)
-v 显示详细的处理过程

选择参数:

-S "-S<字尾备份字符串> "或 "--suffix=<字尾备份字符串>"
-V "-V<备份方式>"或"--version-control=<备份方式>"
--help 显示帮助信息
--version 显示版本信息

示例:

ln -s my _m  给my目录创建 _m软连接
ln -s a.txt a 给a.txt 文件创建 a软连接
ln a.txt ab 给a.txt 文件创建硬连接
rm -f a.txt  # 删除 a 指向的文件连接
ln -sf ab a  # 强制将 a 指向ab文件

2.7 压缩与解压

命令格式: tar [option] 压缩文件 [文件或目录]

参数:

-c 打包, 不能和 -x 同时使用。
-x 解包, 不能和-c同时使用。
-z 压缩,解压 gz 格式
-v 显示执行过程信息
-f 文件, 打包成什么文件名或解什么压缩包
-C 指定解压路径

示例:

tar -zxvf  my.tar.gz  .   解压gz压缩文件
tar -cvf  new_my.tar.gz  my mysql  压缩gz文件
tar -xvf  my.tar  解压tar文件
tar -cvf  my.tar  创建tar文件
tar -zxvf txts.tar.gz -C _txt_

【提示】_txt_ 目录必须存在, -C 必须提供, 否则认为_txt_是压缩文件

作业: Centos7中安装Python环境

# https://blog.csdn.net/ahhqdyh/article/details/104923416  第3.4小节
#!/bin/bash
yum update
yum groupinstall -y "Development tools"
yum install -y flex.x86_64 zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel libffi-devel
# wget https://www.python.org/ftp/python/3.7.0/Python-3.7.0.tgz 
# wget从官网下载特别慢,网盘链接:https://pan.baidu.com/s/1f30S25Xny6gOzqD3uu8yUw 密码:jupe
tar xvf Python-3.7.5.tgz
cd Python-3.7.5
./configure --prefix=/usr/local/python3
make  
make install
ln -s /usr/local/python3/bin/python3 /usr/bin/python3
ln -s /usr/local/python3/bin/pip3 /usr/bin/pip3
cd ~
pip3 -V
python3 -V

2.8 文件权限

2.8.1 权限结构

文件权限分三个部分(u用、g组、o其它),每一部分包括三个权限描述(r、w、x),表示的内容如下所示:

用户: u  user
用户组:g group
其他用户:o other

只读: r 4, read 
只写:	w ,2, write 
可执行: x, 1
使用三个数完成了8个数值, 常用组合:
	777  -> rwxrwxrwx
	666  -> rw-rw-rw-
	555  -> r-xr-xr-x
	444  -> r--r--r--
	333  -> -wx-wx-wx
	222  -> -w--w--w-
	111  -> --x--x--x
	755  -> rwxr-xr-x
	644  -> rw-r--r--

2.8.2 权限修改

格式:chmod [ugoa][+-=][rwx] filename
说明:

a: ugo  表示全部

权限操作:

+  增加权限
-  去除权限
=  赋予权限

示例:

chmod 777 filename  
chmod +x aa.sh

三、用户命令

3.1 who与whoami命令

3.1.1 who命令

格式: who [-abdHlmpqrsTtu] [file]

参数说明:

-a    同 -bdlprTtu.
-b    最后启动的时间
-d    显示已关闭的进程客户端
-H    显示标题头
-m    只显示当前客户端(类似于 am i)
-q    快速模式,只显示用户名和用户数量, 当选择会让其它选择无效(除a 之外)
-s    默认选择,显示用户名、Line命令(连接线路-通道)和时间字段及备注。
-u    显示每个用户的空闲时间和关键进程的PID
am I  显示当前进程的用户的名

示例:

who -aH
who -m
who -uH  

3.1.2 whoami命令

类似于 who am i命令,显示当前用户的ID(名称)。

3.2 用户管理命令

3.2.1 增加用户

命令格式

useradd [-d home] [-s shell] [-c comment] 
[-m [-k template]] [-f inactive] [-e expire ] [-p passwd] [-r] name

参数说明:

-c:加上备注文字,备注文字保存在passwd的备注栏中。
-d:指定用户登入时的主目录,替换系统默认值/home/<用户名>
-D:变更预设值。
-e:指定账号的失效日期,日期格式为MM/DD/YY,例如06/30/12。缺省表示永久有效。
-f:指定在密码过期后多少天即关闭该账号。如果为0账号立即被停用;如果为-1则账号一直可用。默认值为-1.
-g:指定用户所属的群组。
    值可以使组名也可以是GID。用户组必须已经存在的,期默认值为100,即users。
    如果没有指定,默认情况下根据 /etc/login.defs中配置USERGROUPS_ENAB的结果(yes或no),确认是否自动创建组(组名同用户名)。
-G:指定用户所属的附加群组。
-m:自动建立用户的登入目录(home目录)。
-M:不要自动建立用户的登入目录。
-n:取消建立以用户名称为名的群组。
-r:建立系统账号。
-s:指定用户登入后所使用的shell。默认值为/bin/bash。
-u:指定用户ID号。在系统中必须是唯一的,0~499默认是保留给系统用户账号使用的。
-p: 创建用户时,可以指定口令或输入

涉及到相关的配置文件:

/etc/default/useradd  # 指定用户的过期时间(EXPIRE)和过期时间到期后的禁用策略(INACTIVE)
/etc/login.defs  # 指定创建用户时是否创建用户组的策略(USERGROUPS_ENAB)
/etc/passwd  # 用户的相关信息,结构是 `用户名:口令:用户标识号:组标识号:注释性描述:主目录:登录Shell`
/etc/shadow  # passwd中口令信息使用x或*表示,加密之后存在shadow文件中, 内容结构是`用户名:加密密码:最后一次修改时间:最小修改时间间隔:密码有效期:密码需要变更前的警告天数:密码过期后的宽限时间:账号失效时间:保留字段`
/etc/group   # 所有组相关的信息,内容结构是`组名:密码:GID:该用户组中的用户列表`

示例:

useradd -d /home/judy -mr  judy  # 创建judy用户并指定了用户根目录
useradd -u 544 -d /home/tom -g tom -m  tom -p tom
useradd -mr disen  # 创建disen用户和disen组,同时建议系统账号和创建home目录
passwd disen  # 为disen用户设置口令, 口令要求至少8位,如disen12345678

创建成功之后, 可以通过ssh命令尝试远程连接。

练习:

# 使用创建的用户登录之后, 可以尝试创建文件及目录,查看文件及目录的所属用户和组及权限。

【扩展】/etc/shadow结构说明

  • 加密密码: 目前 Linux 的密码采用的是 SHA512 散列加密算法(之前是MD5或DES)。所有伪用户或新建用户的口令是!!或*,这种用户是不能登录。

  • 最后修改时间: 以1970-1-1开始计算到目前时间的天数,如18451。

    ​ 可以使用date -d "1970-01-01 18451 days" 命令查看具体的日期。

    date -d "1970-01-01 18451 days"
    
    date -d "1970-01-01 18451 days" +"%Y-%m-%d %B %A"
    
  • 最小修改时间间隔(天):从最后修改时间开始,多少天之内不能修改,如果是0,表示随时修改。

  • 密码有效期(天): 默认为99999,也就是273年,可认为是永久。如是90,表示90天后需要修改密码。

  • 密码变更前的警告天数: 距离下次修改口令的n天之前,系统会提醒用户 “再过 n 天你的密码就要过期了,请尽快重新设置你的密码!”。

  • 密码过期后的宽限天数: 可以在宽限的时间内,允许用户登录。

  • 账号失效时间: 在规定的时间之外,不论你的密码是否过期,都将无法使用(或登录)。

3.2.2 修改用户

格式:usermod 选项 用户名
参数:

同useradd
-l 新用户名, 新的用户名不能是已存在的。

示例:

usermod -G lili,dage  davie  # 为davie用户添加两个附加组。
usermod -e 2020-07-10 davie # 修改davie用户的过期时间
usermod -l Davied davie  # 修改用户登录名

3.2.3 删除用户

格式:userdel 选项 用户名, 默认用户删除不会删除家的

【重要】使用 -r 可以在删除用户的时候删除相关信息,如示例:

userdel -r judy

userdel 命令使用如下值退出:

0  成功
1 无法更新密码文件
2 无效的命令语法
6 指定的用户不存在
8 用户已经登录
10 无法更新组文件
12 无法删除主目录

3.2.4 修改口令

命令格式: passwd 用户名
参数:

-l 锁定口令,即禁用账号。 (lock)
-u 口令解锁, (unlock)
-d 使账号无口令。(delete)
-f 强迫用户下次登录时修改口令。

【扩展】所有用户的口令信息:/etc/passwd,其文件内容格式:

用户名:口令:用户标识号:组标识号:注释性描述:主目录:登录Shell

示例:

useradd -rm davie
passwd davie 
passwd -l davie  # 锁定账号,不能登录。
passwd -u davie  # 解锁账号,恢复登录。
passwd -d davie  # 删除账号口令
cat /etc/shadow | grep davie  # 执行删除口令操作前后,可以观察用户口令的信息变化。

3.3 用户组操作

3.3.1 增加组

格式:groupadd [选项] 用户组
参数:

-g GID 指定新用户组的组标识号(GID),默认是按/etc/group中最大组号自增1,产生新组号。
-f 当新的组名已存在,则不报错(以成功的状态退出,且不创建新的组)

示例:

groupadd mysql
groupadd -g 1920 dev1  # 最小或最大的组号,可以在/etc/login.defs文件中。
cat /etc/login.defs|grep GID_MIN  # 1000
cat /etc/login.defs|grep GID_MAX  # 60000

创建成功的组信息,可以在/etc/group查看。

cat /etc/group | tail -n 10
cat /etc/group | grep dev1
usermod -G nice davie
usermod -G nice xc
cat /etc/group | grep nice  # 查看nice组信息,:分隔的最一后一部分是组中用户成员,多个成员用逗号分隔。

3.3.2 删除组

格式: groupdel 组ID(名称)

示例:groupdel mysql

groupdel mysql
groupdel nice  # 从/etc/group文件中删除组信息。

3.4 主组与附加组

主组 -g:

  • 某一个组作为任意用户的主组

  • 这个组就不能直接删除了,需要清空用户

[root@localhost ~]# useradd -rm davie
[root@localhost ~]# groupdel davie
groupdel:不能移除用户“davie”的主组
[root@localhost ~]# userdel -r davie
userdel: davie 邮件池 (/var/spool/mail/davie) 未找到
[root@localhost ~]# groupdel davie
groupdel:“davie”组不存在

附加组 -G:

  • 附加组可以随时删除

  • 和附加组相关联的用户会自动解除关系

3.5 组和用户关系

  • 一个用户可以同时属于多个组
  • 一个组可以同时包含多个用户
  • 一个用户只能有一个主组,可以有多个附加组

【注意】组的文件在配置中/etc/group, 内容格式如下:

组名: 口令:组号:组内用户列表

【提示】x 或 * 表示组没有口令。

3.6 chown命令

修改文件所属组及用户。

命令格式:

 chown [-fhnv] [-R [-H | -L | -P]] owner[:group] file 

参数:

-f 不显示任何错误信息
-R 递归修改文件目录下的所有文件和目录的归属(用户和组)
-v 显示修改的内容

示例:

chown mysql:mysql data
[root@localhost disen]# chown disen:dev1 *.sql
[root@localhost disen]# ls -la
总用量 20
drwx------.  2 disen disen 130 7月   8 15:37 .
drwxr-xr-x. 17 root  root  230 7月   8 15:32 ..
-rw-rw-r--.  1 disen dev1    0 7月   8 15:36 1.sql
-rw-rw-r--.  1 disen dev1    0 7月   8 15:37 2.sql
-rw-r--r--.  1 root  root    8 7月   8 15:33 a.txt
-rw-r--r--.  1 disen disen  18 10月 31 2018 .bash_logout
-rw-r--r--.  1 disen disen 193 10月 31 2018 .bash_profile
-rw-r--r--.  1 disen disen 231 10月 31 2018 .bashrc
-rw-r--r--.  1 disen dev1    0 7月   8 15:37 b.txt
-rw-------.  1 disen disen 689 7月   8 15:37 .viminfo
mv 1.sql 1.sh
mv 2.sql 2.sh

1.sh 文件内容:

#!/bin/sh

echo 'hi, 1.sh!'

修改1.sh文件的权限,将文件所属组用户的权限修改为rx:

chmod g=rx 1.sh
usermod -G dev1 davie
usermod -G dev1 xc

切换davie,尝试执行/home/disen/1.sh文件:

[davie@localhost disen] ./1.sh

【提示】确认/home/disen目录的权限(三部分ugo)都具有执行权限。

[disen@localhost disen] mkdir -p a/b/c/d
[disen@localhost disen] mkdir -p a/b2/c2/d2
[root@localhost disen] chown -R disen:dev1 a  # 将a及其子目录或文件的所属用户及组修改为disen和dev1

四、服务与进程命令

Linux的所有服务(service)都在 /etc/init.d目录下。

每一个服务都是一个进程,存在服务进程(后台进程或守护进程)。

4.1 systemctl命令

此命令同service功能相同。在Window机器中通过 services.msc 命令打开服务面板。

4.1.1 查看服务状态

格式: systemctl status 服务

示例:

systemctl status sshd
# service的命令
service sshd status

4.2 启动服务

格式: systemctl start 服务名

示例:

systemctl start sshd
# service命令
service sshd start

4.3 停止服务

格式: systemctl stop 服务名

示例:

systemctl stop sshd
# service命令
service sshd stop

4.4 重启服务

格式: systemctl restart 服务名

示例:

systemctl restart sshd
# service命令
service sshd restart

4.5 开机启动服务

格式: systemctl enable 服务名

示例:

systemctl enable sshd

4.6 取消开机启动服务

格式: systemctl disable 服务名

示例:

systemctl disable sshd

4.2 ps进程命令

ps命令就是最基本进程查看命令, 可以查看进程正在运行和运行的状态、进程是否结束、进程有没有僵尸、进程占用资源情况等。【注意】ps是显示瞬间进程的状态,并不动态连续;如果想对进程进行实时监控应该用top命令。

命令格式:

ps [-AaCcEefhjlMmrSTvwXx] [-O fmt | -o fmt] [-G gid[,gid...]]
  [-g grp[,grp...]] [-u [uid,uid...]]
  [-p pid[,pid...]] [-t tty[,tty...]] [-U user[,user...]]
  
ps [-L]

参数说明:

-A :所有的进程均显示出来,与 -e 具有同样的效用;
-a : 显示现行终端机下的所有进程,包括其他用户的进程;
-u :以用户为主的进程状态 ;
x :通常与 a 这个参数一起使用,可列出较完整信息。
l :较长、较详细的将该PID 的的信息列出;
j :工作的格式 (jobs format)
-f :做一个更为完整的输出。

4.2.1 显示当前用户下的进程

命令:

ps -l

相关输出信息意义:

F 代表这个程序的旗标 (flag), 4 代表使用者为 superuser;
S 代表这个程序的状态 (STAT);
UID 代表执行者身份
PID 进程的ID号!
PPID 父进程的ID;
C CPU使用的资源百分比
PRI指进程的执行优先权(Priority的简写),其值越小越早被执行;
NI 这个进程的nice值,其表示进程可被执行的优先级的修正数值。
ADDR 这个是内核函数,指出该程序在内存的那个部分。如果是个执行 的程序,一般就是『 - 』
SZ 使用掉的内存大小;
WCHAN 目前这个程序是否正在运作当中,若为 - 表示正在运作;
TTY 登入者的终端机位置;
TIME 使用掉的 CPU 时间。
CMD 所下达的指令名称

4.2.2 列出所有运行的进程

命令:

ps aux

结果说明:

USER:该进程属于那个使用者账号。
PID :该进程的进程ID号。
%CPU:该进程使用掉的 CPU 资源百分比;
%MEM:该进程所占用的物理内存百分比;
VSZ :该进程使用掉的虚拟内存量 (Kbytes)
RSS :该进程占用的固定的内存量 (Kbytes)
TTY :该进程是在那个终端机上面运作,若与终端机无关,则显示 ?。另外, tty1-tty6 是本机上面的登入者程序,若为 pts/0 等等的,则表示为由网络连接进主机的程序。
STAT:该程序目前的状态,主要的状态有:R、S、T、Z。
    R :该程序目前正在运作,或者是可被运作;
    S :该程序目前正在睡眠当中,但可被某些讯号(signal) 唤醒。
    T :该程序目前正在侦测或者是停止了;
    Z :该程序应该已经终止,但是其父程序却无法正常的终止他,造成 zombie (疆尸) 程序的状态
START:该进程被触发启动的时间;
TIME :该进程实际使用 CPU 运作的时间。
COMMAND:该程序的实际指令。

4.2.3 列出所有的sshd远程的进程

ps -ef|grep sshd | grep -v grep
root      3274     1  0 17:07 ?        00:00:00 /usr/sbin/sshd -D
root      3301  3274  0 17:07 ?        00:00:00 sshd: root@pts/5
root      3602  3274  0 17:07 ?        00:00:00 sshd: root@pts/0
root      3631  3274  0 17:07 ?        00:00:00 sshd: root@pts/1
root      3700  3274  0 17:07 ?        00:00:01 sshd: root@pts/3
root      3718  3274  0 17:07 ?        00:00:00 sshd: root@pts/4
root      3815  3274  0 17:07 ?        00:00:00 sshd: root@pts/2
root      3839  3274  0 17:07 ?        00:00:00 sshd: root@pts/6
root      3895  3274  0 17:07 ?        00:00:00 sshd: root@pts/7
root      3953  3274  0 17:08 ?        00:00:00 sshd: root@pts/10
root      3955  3274  1 17:08 ?        00:00:05 sshd: root@pts/8,pts/9
root      6816  3274  0 17:10 ?        00:00:00 sshd: root@pts/11
root      6834  3274  0 17:10 ?        00:00:00 sshd: root@notty
root      8695  3274  2 17:12 ?        00:00:00 sshd: root@pts/12

4.3 netstat命令

在Centos中需要通过yum命令安装 net-tools

yum install -y net-tools

netstat命令用于显示与IP、TCP、UDP和ICMP协议相关的统计数据,一般用于检验本机各端口的网络连接情况。

命令格式:

netstat [-veenNcCF] [] -r     
netstat {-V|--version|-h|--help}
netstat [-vnNcaeol] [ ...]
netstat { [-veenNac] -I[] | [-veenNac] -i | [-cnNe] -M | -s } [delay]

示例:

netstat -atulpn

查看 22端口的占用情况:

# netstat -nltp | grep 22
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      3274/sshd           
tcp6       0      0 :::22                   :::*                    LISTEN      3274/sshd   

4.4 kill命令

可以干掉或中断进程, 信号是9, 和ps 、who 或 netstat命令配合使用。

kill -9  进程ID
kill -l  列出所有信号

停止sshd服务进程及它的连接子进程:

ps -ef|grep sshd |grep -v grep |awk '{print $2}'|xargs kill 

【提示】awk 可以执行脚本,针每一行的数据,以空格分隔成多列, $2表示第2列。可以参考文档:https://www.runoob.com/linux/linux-comm-awk.html

只查看sshd的子进程信息:

ps -ef|grep sshd|grep -v grep| grep -v /usr/sbin/sshd
root     22124 22112  1 17:39 ?        00:00:00 sshd: root@pts/0
root     22131 22112  0 17:39 ?        00:00:00 sshd: root@pts/2
root     22155 22112  1 17:39 ?        00:00:00 sshd: root@pts/1
root     22177 22112  0 17:39 ?        00:00:00 sshd: root@pts/3
root     22214 22112  2 17:39 ?        00:00:01 sshd: root@pts/4,pts/5
root     22255 22112  0 17:39 ?        00:00:00 sshd: root@pts/6
root     22341 22112  1 17:40 ?        00:00:00 sshd: root@pts/7
root     22433 22112  0 17:40 ?        00:00:00 sshd: root@pts/8
root     22455 22112  1 17:40 ?        00:00:00 sshd: root@notty
root     22655 22112  0 17:40 ?        00:00:00 sshd: root@pts/9
root     22808 22112  1 17:40 ?        00:00:00 sshd: root@pts/12
root     22825 22112  3 17:40 ?        00:00:01 sshd: root@pts/10,pts/11
root     23021 22112  0 17:40 ?        00:00:00 sshd: root [priv]
sshd     23022 23021  0 17:40 ?        00:00:00 sshd: root [net]

4.5 自定义服务

参考: /etc/init.d/network脚本。

在 /etc/init.d/ 目录下,编写一个服务脚本,如idea文件,内容如下:

#!/bin/sh
# chkconfig: 2345 80 90
# description: idea register server

case "$1" in
        start)
                sh /usr/local/idea/start.sh
        ;;
        stop)
                ps -ef |grep idea|grep -v grep|awk '{print $2}'|xargs kill
        ;;
esac

在 start.sh脚本的内容如下:

#!/bin/sh
echo 'Staring Python Service'

python3

可以尝试启动服务和停止服务。

【扩展】Linux下socket进程间通信-Python语言实现

# multiprocessing.Process 进程类
# threading.Thread类
# socket 
# 1. 实现网络套接字的通信(C/S客户端和服务端之间有通信)
# 2. 在Linux中,实现进程间的通信
# socket核心函数
# 1. 主进程/server端: 
#   - socket = socket.socket(AF_INET|AF_UNIX)
#   - socket.bind((hostname|ip, port))  或 socket.bind(xxx.sock文件路径)
#   - socket.listen()  开始监听
#   - client, address = socket.accept()  等待客户端连接,对应是client客户端的socket.connect(bind方法的参数)方法

# 2. 客户端【进程】
#   - socket.socket(AF_INET 默认|AF_UNIX)
#   - socket.connect((server_ip, port)) 或 socket.connect(xxx.sock文件路径)

# 3. 通信的方法:  socket.send(b'')  , msg = socket.recv(8*1024)  # 8192
#   - socket.close()

实战: 实现缓存的服务器和客户端

# 设计的思路
服务器提供添加、删除、查询、设置过期时间功能,对数据进行缓存。 
服务器在接收客户端发送的指令时,需要解析,根据解析的结果来调用相关的功能。
客户端连接,编写服务器识别的指令:
	   # select 0  选择存储的空间(库)
	[0]# set name disen
	[0]# get name
	     disen
	[0]# del name
	[0]# expire name 60
	[0]# exit

/etc/init.d/cache/server.py

import socket
from threading import Thread
import os

# {(192.168.31.11, 45538): {0: {name:'disen'}, 1: {} }}

# 作业:尝试地从本地加载数据 【pickle和os两个模块】
caches = {}  # 缓存信息,以dict存储 。

class ClientThread(Thread):
  def __init__(self, client, addr):
    super().__init__()
    self.client = client
    self.addr = addr 
    self.current_cache = None
    
  def run(self):
    self.client.send(b'OK')
    # 判断之前是否连接过
    if self.addr not in caches:
      caches[self.addr] = {} 
      
    while True:
      # 接收客户端发送过来的命令
      msg = self.client.recv(8192)
      if b'exit' == msg:
        break
      
      cmd = msg.decode('utf-8')
      if cmd.startswith('set'):
        k,v = cmd.split()[1:]
        self.current_cache[k] = v
        self.client.send(b'OK')
        
      elif cmd.startswith('select'):
        index = cmd.split()[-1]
        # 判断index是否已存在
        if index not in caches[self.addr]:
          caches[self.addr][index] = {}
        
        self.current_cache = caches[self.addr][index]
        self.client.send(b'OK')
        
      elif cmd.startswith('get'):
        k = cmd.split()[-1]
        if k in self.current_cache:
          v = self.current_cache[k]
          self.client.send(v.encode('utf-8'))
        else:
          resp_text = f'Not exists {k}'
          self.client.send(resp_text.encode('utf-8'))
          
    self.client.close()

server = socket.socket(socket.AF_UNIX)
server.bind('/var/run/cache.sock')  # 自动创建
server.listen()  # 开启监听
print('Cache Server Running...')
try:
  while True:
    client, addr = server.accept()  # 阻塞方法
    ClientThread(client, addr).start() # 启动客户端线程
except:
  os.remove('/var/run/cache.sock')
  
# 作业:将字典数据持久化存储 【提示: pickle 库】

/etc/init.d/client.py

import socket

def show_msg(msg, prefix='#'):
  print(msg)

client = socket.socket(socket.AF_UNIX)
client.connect('/var/run/cache.sock')
msg = client.recv(1024)
show_msg(msg.decode('utf-8'))
prefix = '#'

while True:
  cmd = input(f'{prefix}')
  client.send(cmd.encode('utf-8'))
  if cmd.startswith('select'):  # select 1
    index = cmd.split()[-1]
    prefix = f'[{index}]#'
  elif cmd.startswith('exit'):
    break
  
  resp_txt = client.recv(8192)
  show_msg(resp_txt.decode('utf-8'),  prefix)

print('Bye Bye')

增加服务脚本 /etc/init.d/cached, 内容:

#!/bin/sh
# chkconfig: 2345 80 90
# description: Cache Server 

case "$1" in
        start)
                python3 cache/server.py
        ;;
        stop)
                ps -ef |grep cached|grep -v grep|awk '{print $2}'|xargs kill
        ;;
esac

设置文件的执行权限:

chmod +x /etc/init.d/cached

修改client.py文件,增加第一行内容:#!/usr/local/bin/python3, 另外,增加可执行的文件权限

chmod +x /etc/init.d/cache/client.py

创建client.py文件的软连接:

ln -s /etc/init.d/cache/client.py  /usr/bin/cache

在任何位置,通过cache命令,启动client.py客户端程序。

五、硬盘命令

5.1 free命令

参数 :

 -b, --bytes         show output in bytes
 -k, --kilo          show output in kilobytes
 -m, --mega          show output in megabytes
 -g, --giga          show output in gigabytes

查看内存使用情况:

free -mh

可以使用 top命令,查看CPU和内存使用情况。

5.2 df命令

命令格式: df [选项] [文件]…

参数:

-a, --all   所有文件信息: pseudo 虚假的, duplicate 重复的, inaccessible 不可达
-h, --human-readable 显示人性化的文件大小 (如, 1K 234M 2G)
-i, --inodes		显示inode 信息而非块使用量
-k			即--block-size=1K
-l, --local		只显示本机的文件系统
-T, --print-type      显示文件类型

5.2.1 显示存储空间大小

df -h  

5.2.2 显示各存储空间大小

df -ah

5.2.3 仅查看本地文件

df -ahlT 
df -hT

5.3 du命令

格式:

du [选项]... [文件]...
du [选项]... --files0-from=F

参数:

  -0, --null            end each output line with 0 byte rather than newline
  -a, --all             write counts for all files, not just directories
      --apparent-size   print apparent sizes, rather than disk usage; although
                          the apparent size is usually smaller, it may be
                          larger due to holes in ('sparse') files, internal
                          fragmentation, indirect blocks, and the like
  -B, --block-size=SIZE  scale sizes by SIZE before printing them; e.g.,
                           '-BM' prints sizes in units of 1,048,576 bytes;
                           see SIZE format below
  -b, --bytes           equivalent to '--apparent-size --block-size=1'
  -c, --total           显示所有文件的总和大小,包括最后的总量信息。
  -D, --dereference-args  dereference only symlinks that are listed on the
                          command line
  -d, --max-depth=N      显示每个目录的大小,但显示到指定子目录层次。
  -H                    equivalent to --dereference-args (-D)
  -h, --human-readable  显示人性化大小 (e.g., 1K 234M 2G)
  -k                    like --block-size=1K  k字节
  -L, --dereference     dereference all symbolic links
  -l, --count-links     count sizes many times if hard linked
  -m                    like --block-size=1M
  -P, --no-dereference  don't follow any symbolic links (this is the default)
  -S, --separate-dirs   显示每个子目录的大小
  -s, --summarize       仅显示一个总和
  -t, --threshold=SIZE  exclude entries smaller than SIZE if positive,
                          or entries greater than SIZE if negative
      --time            show time of the last modification of any file in the
                          directory, or any of its subdirectories
      --time=WORD       show time as WORD instead of modification time:
                          atime, access, use, ctime or status
      --time-style=STYLE  show times using STYLE, which can be:
                            full-iso, long-iso, iso, or +FORMAT;
                            FORMAT is interpreted like in 'date'
  -X, --exclude-from=FILE  exclude files that match any pattern in FILE
      --exclude=PATTERN    exclude files that match PATTERN
  -x, --one-file-system    skip directories on different file systems
      --help		显示此帮助信息并退出
      --version		显示版本信息并退出

5.3.1 当前目录的空间使用情况

du -sh

-s 统计求和,将所有目录及文件的大小的总和计算出来。结果只有一行。

5.3.2 查看指定目录的空间使用情况

du -h --max-depth=1 /home

统计/home下所有用户的目录的空间使用情况。

比较以下命令的结果:

du -c -d 1 /home

5.3.3 查看某个文件容量大小

du -h 1.txt
du -h 1.txt 2.txt 

查看当前目录及其子目录所有文本文件的大小:

find . -name '*.txt'|xargs du -h

【扩展】修改主机的时区

 cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

或者使用 tzselect 命令,根据提示选择Asia/Shanghai

5.4 fdisk 命令

格式:

fdisk [选项] <磁盘>    更改分区表
fdisk [选项] -l <磁盘> 列出分区表
fdisk -s <分区>        给出分区大小(块数)

参数:

 -b <大小>             扇区大小(512、1024、2048或4096)
 -c[=<模式>]           兼容模式:“dos”或“nondos”(默认)
 -h                    打印此帮助文本
 -u[=<单位>]           显示单位:“cylinders”(柱面)或“sectors”(扇区,默认)
 -v                    打印程序版本
 -C <数字>             指定柱面数
 -H <数字>             指定磁头数
 -S <数字>             指定每个磁道的扇区数

5.4.1 查看硬盘相关信息

fdisk -l 

5.4.2 新硬盘分区

如,新增了一个硬盘 /dev/sdb, 使用前需要进行分区

fdisk /dev/sdb

在提示中,输入m查看分区命令的帮助:

a   切换可启动标志
b   编辑 bsd 硬盘标签
c   切换dos兼容标识
d   删除分区
g   创建一个空的 GPT 分区表
G   创建一个IRIX(SGI)分区表
l   显示已知分区类型
m   显示菜单
n   添加一个分区
o   创建一个空的dos分区
p   显示分区表
q   退出,但不保存
s   创建一个空的Sun 硬盘标签
t   修改一个分区的 System ID
u   更改显示/输入单位
v   验证分区表
w   将表写入磁盘并退出
x   扩展功能 (仅专家)

创建分区时, 输入n

Command (m for help): n 

输入n之后,提示选择主分区还是扩展分区,默认选择 p :

Partition type:
   p   primary (0 primary, 0 extended, 4 free)
   e   extended
Select (default p): 

回车之后,提示输入分区号(1-4),输入1即可:

Partition number (1-4): 1 

回车之后,提示输入柱面号,默认是1:

起始 扇区 (2048-20971519,默认为 2048):

2048表示使用默认起始扇区号.如果要分多个区的话,先计算好要多大,再输入数字。

回车之后,提示输入结束柱面号:

Last 扇区, +扇区 or +size{K,M,G} (2048-20971519,默认为 20971519)

回车之后, 再输入w,表示保存并退出。

可以通过 fdisk -l查看/dev/sdb 下存在一个新的分区 /dev/sdb1

5.5 mkfs.ext3格式化硬盘

格式化: mkfs 默认使用ext2文件格式

mkfs.ext3使用ext3文件格式,如对/dev/sdb1 命令如下所示:

mkfs.ext3 /dev/sdb1

5.6 mount命令

格式:

mount [-lhV]
mount -a [选项]
mount [选项] [--source] <源> | [--target] <目录>
mount [选项] <源> <目录>
mount <操作> <挂载点> [<目标>]

参数:

 -a, --all               挂载 fstab 中的所有文件系统
 -c, --no-canonicalize   不对路径规范化
 -f, --fake              空运行;跳过 mount(2) 系统调用
 -F, --fork              对每个设备禁用 fork(和 -a 选项一起使用)
 -T, --fstab <路径>      /etc/fstab 的替代文件
 -h, --help              显示此帮助并退出
 -i, --internal-only     不调用 mount.<类型> 助手程序
 -l, --show-labels       列出所有带有指定标签的挂载
 -n, --no-mtab           不写 /etc/mtab
 -o, --options <列表>    挂载选项列表,以英文逗号分隔
 -O, --test-opts <列表>  限制文件系统集合(和 -a 选项一起使用)
 -r, --read-only         以只读方式挂载文件系统(同 -o ro)
 -t, --types <列表>      限制文件系统类型集合
     --source <源>       指明源(路径、标签、uuid)
     --target <目标>     指明挂载点
 -v, --verbose           打印当前进行的操作
 -V, --version           显示版本信息并退出
 -w, --rw, --read-write  以读写方式挂载文件系统(默认)

 -h, --help     显示此帮助并退出
 -V, --version  输出版本信息并退出

5.6.1 普通挂载

mount 命令可以将某一个分区挂载到某一个空的目录中(可创建新的目录):

mkdir /mnt/sdb1

一般挂载的文件目录都会在 /mnt下创建。

mount /dev/sdb1 /mnt/sdb1 

5.6.2 开机自动挂载

编辑 /etc/fstab中添加新硬盘的挂载信息。

如将 /dev/sdb1 自动挂载到 /mnt/sdb1目录下,在/etc/fstab文件中新增一行:

/dev/sdb1 /mnt/sdb1 ext3 defaults 1 2
  • ext3 表示挂载格式。

  • defautlts 表示文件系统参数(同时具有rw,suid,dev,exec,anto,nouser,async等参数)

    文件系统参数说明:

    async/sync(是否允许磁盘于内存中的数据异/同步写入) 
    auto/noauto(启动时自动/非自动载入该分区)
    rw/ro(该分区以读写/只读方式载入)
    exec/noexec(限制在此文件系统内是否可以进行“执行”操作)
    user/nouser(确定是否允许用户使用mount命令来载入)
    suid/nosuid(设置该文件系统是否允许SUID权限的存在)
    usrquota(启动文件系统支持磁盘配额模式)
    grpquota(启动文件系统对用户组磁盘配额模式的支持)
    
  • 每五位 表示:dump备份命令, 0表示不备份, 1表示备份

  • 第六位表示:在启动时是否以fsck检验分区, 0表示不检验,1表示根文件系统检验,2其它文件系统检验

如果挂载成功,可以通过如下命令查看:

df -lh 

5.6.3 取消挂载

umount /sdc1
umount /sdc2
# 查询取消挂载的情况
df -lh 
# 进入挂载点的目录, 原挂载后操作的文件不见了。
ls -la
[root@localhost /]# cd /sdc1
[root@localhost sdc1]# ls -la
总用量 28
drwxr-xr-x.  3 root root  4096 7月   9 16:09 .
dr-xr-xr-x. 19 root root   265 7月   9 16:06 ..
-rw-r--r--.  1 root root  7676 7月   9 16:09 1.sql
drwx------.  2 root root 16384 7月   9 16:00 lost+found
[root@localhost sdc1]# cd ..
[root@localhost /]# umount /sdc1
[root@localhost /]# cd /sdc1
[root@localhost sdc1]# ls -la
总用量 0
drwxr-xr-x.  2 root root   6 7月   9 16:06 .
dr-xr-xr-x. 19 root root 265 7月   9 16:06 ..
[root@localhost sdc1]#  

六、网络命令

6.1 ifconfig命令

格式:

 ifconfig [-a] [-v] [-s]  [[] 
] [add
[/]] [del
[/]] [[-]broadcast [
]] [[-]pointopoint [
]] [netmask
] [dstaddr
] [tunnel
] [outfill ] [keepalive ] [hw
] [mtu ] [[-]trailers] [[-]arp] [[-]allmulti] [multicast] [[-]promisc] [mem_start ] [io_addr ] [irq ] [media ] [txqueuelen ] [[-]dynamic] [up|down] ...

6.1.1 查看网络信息

ifconfig  # 所有网卡信息
ifconfig enp0s3  # 查看enp0s3网卡信息

6.1.2 配置网卡信息

格式: fconfig 网卡名称 ip地址 netmask 子网掩码

ifconfig enp0s3 192.168.31.90 netmask 255.255.255.0

修改之后,可以通过ifconfig查看,如果存在sshd远程连接服务,需要重启动network及sshd服务。

6.1.3 卸载网卡

格式: ifconfig 网卡名称 down

ifconfig enp0s3 down

6.1.4 启用网卡

格式: ifconfig 网卡名称 up

ifconfig enp0s3 up

作业:

# 基于Python,实现描述如下的功能。 提示: os.system(shell_cmd) , for line in open('path')
# 解释ifconfig命令执行后的内容
# 预期的结果:
# 网卡名 IP地址
enp0s3 10.36.172.114
lo 127.0.0.1
# 编写一个client.py脚本,可以通过命令行参数,获取-h和-p的参数值。提示: sys.argsv 和 argparse 模块
# 如,  python3  client.py  -h 192.168.10.11 -p 9000
def parse_cmdline():
    parser = argparse.ArgumentParser()
    parser.add_argument('--debug',
                        nargs='?', const=1, default=0, type=int,
                        choices=range(1, log.DEBUG_MAX+1),
                        help="""Enable logging of debug messages.
                                Additional argument in range 1..%s can be used
                                to specify log level.""" % log.DEBUG_MAX,
                        metavar="level")
    parser.add_argument('--debug-gc',
                        help="""Turn on garbage collector leak information.
                        The collector runs every 10 seconds and if there are
                        leaks, it prints information about the leaks.""",
                        action="store_true")
    parser.add_argument('--nofork',
                        help="""Turn off daemon forking,
                                run as a foreground process.""",
                        action="store_true")
    return parser.parse_args()

6.2 ip命令

格式:

ip [ OPTIONS ] OBJECT { COMMAND | help }
ip [ -force ] -batch filename
    where  OBJECT := { link | address | addrlabel | route | rule | neigh | ntable |
    tunnel | tuntap | maddress | mroute | mrule | monitor | xfrm |
    netns |  | fou | macsec | tcp_metrics | token | netconf | ila |
    vrf }
    
    OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |
    -h[uman-readable] | -iec |
    -f[amily] { inet | inet6 | ipx | dnet | mpls | bridge | link } |
    -4 | -6 | -I | -D | -B | -0 |
    -l[oops] { maximum-addr-flush-attempts } | -br[ief] |
    -o[neline] | -t[imestamp] | -ts[hort] | -b[atch] [filename] |
    -rc[vbuf] [size] | -n[etns] name | -a[ll] | -c[olor]}

6.2.1 查看当前网卡信息

ip a
ip address

6.2.2 查看路由信息

ip r
ip route

6.3 hostname命令

6.3.1 查看当前主机名

hostname

6.3.2 修改主机名

hostname server1

6.4 hostnamectl命令

格式: hostnamectl [OPTIONS…] COMMAND …

命令:

status                 显示当前hostname相关信息
set-hostname NAME      Set system hostname
set-icon-name NAME     Set icon name for host
set-chassis NAME       Set chassis type for host
set-deployment NAME    Set deployment environment for host
set-location NAME      Set location for host

查看当前主机状态信息(包含主机名):

hostnamectl status

6.5 firewall-cmd命令

通过 alias 为 firewall-cmd 定义名称为 fwc。

6.5.1 防火墙命令

6.5.1.1 查看状态
firewall-cmd --state

默认是未开启。

6.5.1.2 开启与关闭
service firewalld start|stop|restart
# systemctl start|stop|restart firewalld
6.5.1.3 启用与禁用
systemctl disable|enable firewalld

6.5.2 开放端口与查询

【注意】云服务开放端口还需要到安全组中,添加指定的端口。

6.5.2.1 查询端口是否开放
firewall-cmd  --list-port

如查询80端口是否开放:

firewall-cmd --query-port=80/tcp
6.5.2.2 开放端口
firewall-cmd --add-port=80/tcp --permanent

开放端口后,需要重载配置,命令如下:

firewall-cmd --reload
6.5.2.3 移除开放的端口
firewall-cmd --remove-port=80/tcp

6.5.3 白名单

指定只允许哪些ip访问某个端口,如下所示:

firewall-cmd --permanent --add-rich-rule 'rule family=ipv4 source address=10.36.172.51/2 port port=22 protocol=tcp accept'

表示: 只允许192.168.0.1的ip访问80端口

另外,可以修改/etc/hosts.allow文件,指定白名单,如下所示:

sshd:192.168.3.12:allow
sshd:192.168.3.13:allow

允许192.168.3.12或13的两个ip连接sshd服务。

6.5.4 黑名单

修改/etc/hosts.deny文件,将禁止访问的ip地址添加进来,如下所示:

sshd:10.36.172.51:deny

拒绝192.168.3.133的主机连接ssh。

如果对多次登录失败的用户(ip)添加到黑名单中,可以编写sh脚本。

1) 编写防爆破脚本 /usr/local/bin/secure_ssh.sh

#! /bin/bash
cat /var/log/secure|awk '/Failed/{print $(NF-3)}'|sort|uniq -c|awk '{print $2"="$1;}' > /usr/local/bin/black.txt
for i in `cat  /usr/local/bin/black.txt`
do
    IP=`echo $i |awk -F= '{print $1}'`
    NUM=`echo $i|awk -F= '{print $2}'`
    if [ $NUM -gt 5 ]
    then
        grep $IP /etc/hosts.deny > /dev/null
        if [ $? -gt 0 ]
        then 
            echo "sshd:$IP:deny" >> /etc/hosts.deny
        fi
    fi
done
  • grep $IP /etc/hosts.deny > /dev/null 判断IP地址是否已存在,存在时 $?则为0,反之为1
  • if [ $? -gt 0 ] 判断 检验结果是大否大于0,如果大于0表示黑名单中不存在这个IP
  1. crontab添加定时任务

缩写定时任务文件secure_ssh.cron 内容如下:

*/2 * * * *  sh /usr/local/bin/secure_ssh.sh

比示每2分钟检查一次。前5位表示时间格式: 分钟 小时 日 月 周。

通过crontab 命令添加这个任务:

crontab secure_ssh.cron
crontab -l 查询当前用户的定时任务

七、Vim编辑器

通过vi 命令来编辑一些文本或脚本文件。

7.1 三种模式

7.1.1 普通模式

可以移动方向键
shift+: 进入命令模式

7.1.2 插入模式

i键可以进入插入模式。

编程模式
ESC 进入普通模式

7.1.3 命令行模式(底行模式)

set number 显示行号
set nonumber 取消显示行号
n 移动第n行
/内容  搜索内容
%s/原内容/新内容/ig  全文或当前行替换内容
set ft=UNIX 设置文件格式为UNIX格式, 解决Window上开发的sh脚本不能在Linux中执行的问题。
w 写入
q 退出, q! 强制退出
x 写入并退出

7.1.4 模式之间的关系

普通模式可以转换为插入(按i、o、shift+o)和命令行模式
插入模式只能转换为普通模式(ESC)
命令行模式只能转换为普通模式(ESC, Del )
插入模式和命令行模式不能直接进行转换

【注意】普通模式是状态转换的桥梁

7.2 转变插入模式

i:光标当前位置 insert
Shift + i: 光标所在行首 insert
a:光标所在的后一个位置 insert
Shift + a: 光标所在行尾 insert
o: 光标所在行下插入空行
Shift + o: 光标所在行上插入空行
cc: 剪切当前行,进入当前。 和dd命令相似,但dd不会进入编辑模式
r:只替换一个,之后变成 普通模式
Shift + r (R):替换模式,可连续替换,直到模式切换为止。

7.3 普通模式下的命令

[n]yy: 复制, 当前光标位置向下复制n行,n默认为1行
[n]dd: 剪贴,当前光标位置向下剪贴n行,n默认为1行
d: 按左右方向键 删除光标左或右边的一个字符, 按上下方向键,删除当前行和上行或下行的两行内容。
nd: 向上或下删除 n+1 行, 向左或右删除 n个字符
p: 粘贴
x:光标所在位置删除一个字符
nx: 光标所在位置开始向右删除n个字符。如果删除最一个位置,光标自动向左移动。
dw:光标位置开始到行尾全部删除, 遇到空格或特殊字符结束。
gg: 第一行
G:光标移动到最后一行首位置
dG(Shift+g): 从当前行删除到最后一行
ggdG: 清空文件

v: 进入可视模式,移动光标选择字符
shift+v: 选择整行
. : 重复操作
u: 撤销操作
ctrl+r: 取消撤消
/ : 查找字符

八、Shell编程

8.1 Shell识知

8.1.1 什么是Shell

  • Shell 是一个用 C 语言编写的程序,是使用 Linux 的桥梁.
  • Shell 既是一种命令语言,又是一种程序设计语言
  • Linux Shell: Shell 脚本(shell script),是一种为 shell 编写的脚本程序

8.1.2 执行脚本的解释器

  • /bin/sh
  • /usr/bin/sh
  • /bin/bash
  • /usr/bin/csh

8.1.3 编写脚本要求

  • 脚本文件扩展名是.sh
  • 脚本第一行是执行脚本的Shell解释器, 如/bin/sh
  • 脚本需要 x执行权限, 如chmod +x bb.sh
  • 执行脚本
    • ./bb.sh 相对路径
    • /home/disen/bb.sh 绝对路径

8.2 基础IO操作

8.2.1 输入 In

8.2.1.1 read命令

格式:

read [-rs] [-a ARRAY] [-d delim] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [var_name1 var_name2 ...]

参数:

-a:将分裂后的字段依次存储到指定的数组中,存储的起始位置从数组的index=0开始。
-d:指定读取行的结束符号。默认结束符号为换行符。
-n:限制读取N个字符就自动结束读取,如果没有读满N个字符就按下回车或遇到换行符,则也会结束读取。
-N:严格要求读满N个字符才自动结束读取,即使中途按下了回车或遇到了换行符也不结束。其中换行符或回车算一个字符。
-p:给出提示符。默认不支持"\n"换行,要换行需要特殊处理,见下文示例。例如,"-p 请输入密码:"
-r:禁止反斜线的转义功能。这意味着"\"会变成文本的一部分。
-s:静默模式。输入的内容不会回显在屏幕上。
-t:给出超时时间,在达到超时时间时,read退出并返回错误。也就是说不会读取任何内容,即使已经输入了一部分。
-u:从给定文件描述符(fd=N)中读取数据。

示例:

read -p 输入你的姓名: name
echo $name
read -p 输入你的爱好,使用空格分隔: -a lovies
echo ${lovies[@]}  # 数据中所有元素
echo ${lovies[0]}  # 索引下标从0开始,显示第一个位置的信息
8.2.1.2 命令 < file

输入重定向到file中, 即将file作为输入的内容 传给命令。

示例:

wc -l < /etc/passwd

统计用户个数, /etc/passwd作为输入的内容传给wc 命令。

cat /etc/passwd|awk -F: '{print $1}'|uniq -c | wc -l
8.2.1.3 命令 << EOF

从键盘输入,输入到EOF结束, 可以输入多行.

示例:

wc -l << EOF
good
yes
no
EOF

8.2.2 输出 Out

8.2.2.1 命令 > file

将命令的结果重定向到 file中。

示例:

wc -l /etc/passwd > lines.txt

如果执行某一项目,不想输出也不想保留,可以重定向输出到 /dev/null:

service sshd start > /dev/null
8.2.2.2 命令 >> file

将命令的结果追加输出重定向到 file中。

示例:

wc -l /etc/passwd >> lines.txt
wc -l /etc/group >> lines.txt
8.2.2.3 echo命令

输出指定的信息

示例:

echo 'hello'
8.2.2.4 printf命令

printf 命令模仿 C 程序库(library)里的 printf() 程序

格式: printf format-string [arguments...]

参数说明

  • format-string: 为格式控制字符串,%s %c %d %f
  • arguments: 为参数列表, 多个参数用空格分隔

对齐、宽度及小数点说明:

  • %-ns

    • -左对齐, 没有-表示右对齐(默认)
    • n表示宽度, 长度不足使用空格来分隔
  • %-4.2f

    • 4 表示宽度(字符个数)

    • .2f 表示小数保留2位, 四舍五入。

  • %d 数值

  • %c 一个字符

支持转义字符

\a	警告字符,通常为ASCII的BEL字符
\b	后退
\f	换页(formfeed)
\n	换行
\r	回车(Carriage return)
\t	水平制表符
\v	垂直制表符
\	一个字面上的反斜杠字符
\ddd	表示1到3位数八进制值的字符。仅在格式字符串中有效
\0ddd	表示1到3位的八进制值字符

示例:

printf '%10s-> %-6.2f\r\n' disen  299.2566
printf '%-10s, %d, %10.2f \r\n' disen 90 11.1542

8.3 流程控制

8.3.1 if-else分支

8.3.1.1 if单行语句
if [ 2 == 2 ]; then echo good ; fi

【注意】中括号内部的两侧必须有空格, 关系运算符两侧建议有空格。

数值的关系运算时,除了 ==!= ,其它的关系建议使用关系字符表示,参考test命令中的数值测试。

; 分隔if结构中的关键字,如thenfi,参考多行if语句。

8.3.1.2 if多行语句
read -p 第一个数: a
read -p 第二个数: b
echo "预测第一个数 $a 大于第二个数 $b"
if [ $a -gt $b ]
then
      echo ok
fi

【注意】字符串包含变量时,必须使用双引号。

8.3.1.3 if-else语句
a=10
b=20
if [ $a -gt $b ]
then
      echo ok
else
      echo error
fi

关系运算符的字母表示:

-gt 大于
-ge 大于等于
-lt 小于
-le 小于等于
-eq 等于
8.3.1.4 if-elif-else语句
a=100
b=20
if [ $a -gt $b ]
then
      echo ok
elif [ $a -gt 20 ]
then 
       echo fail
else
      echo error
fi

【注意】赋值语句时,=符号前后不要加空格。

8.3.2 test 命令

test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件的测试。

8.3.2.1 数值测试
-eq	等于则为真
-ne	不等于则为真
-gt	大于则为真
-ge	大于等于则为真
-lt	小于则为真
-le	小于等于则为真

示例:

num1=200
num2=100
if test $[num1] -eq $[num2]
then
    echo '相等!'
else
    echo '不相等!'
fi
read -p "a=" a
read -p "b=" b
result=$[a+b]

echo "$[a+1] + $[b+10] = $[result+11]"

【注意】$[] 的中括号中,可以进行算术运算:

result=$[num1+num2]
echo "result=$result"
if test ! 100 -ge 90  ;then echo disen ;else echo nesid ;fi
8.3.2.2 字符串测试
=	等于则为真
!=	不相等则为真
-z 字符串	字符串的长度为零(zero)则为真
-n 字符串	字符串的长度不为零(nozero)则为真

示例:

num1="disen"
num2="Disen"
if test $num1 = $num2
then
    echo '两个字符串相等!'
else
    echo '两个字符串不相等!'
fi
read -p "a (default 11):" a
read -p "b (default 20):" b

if test $a -z ;then a=11 ;fi
if test $b -z ;then b=20 ;fi

echo "$a, $b"
8.3.2.3 文件测试
-e 文件名	如果文件存在则为真
-r 文件名	如果文件存在且可读则为真
-w 文件名	如果文件存在且可写则为真
-x 文件名	如果文件存在且可执行则为真
-s 文件名	如果文件存在且至少有一个字符则为真
-d 文件名	如果文件存在且为目录则为真
-f 文件名	如果文件存在且为普通文件则为真
-c 文件名	如果文件存在且为字符型特殊文件则为真
-b 文件名	如果文件存在且为块特殊文件则为真

示例:

cd /etc/init.d
if test -e ./cached
then
    echo 'cached 文件已存在!'
else
    echo 'cached 文件不存在!'
fi

也可将文件测试的条件在[]中使用,如下所示:

cd sql
if [ -e sql/1.sql ]; then
  echo '1.sql已存在'
else
  echo '1.sql不存在,可以创建'
fi

另外,Shell还提供了与( -a )、或( -o )、非( ! )三个逻辑操作符用于将测试条件连接起来,其优先级为:"!“最高,”-a"次之,"-o"最低。如下所示:

cd ~
if test -d ./sql -a -e ./sql/1.sql
then
    echo '1.sql脚本存在'
else
    echo 'sql目录不存在或1.sql脚本不存在'
fi

综合示例:

read -p "输入指定的文件:" path

# 判断输入的path是否为空字符串
if [ -z $path ] ; then path="." ;fi

if test -d $path ;then
  echo "$path 是一个目录"
elif test -e $path ;then
  echo "$path 是一个文件"
  if test -f $path ;then
     echo "$path 是一个普通的文件"

     if test -s $path ;then
        cat $path
     else
        echo "$path是空文件"
     fi

  else
     echo "$path 这个文件不普通"
  fi
else
  echo "$path 确定是一个文件路径?"
fi

8.3.3 case多选择

功能: case语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令
格式:

case 值 in
模式1)
    command1
    command2
    ...
    commandN
    ;;
模式2)
    command1
    command2
    ...
    commandN
    ;;
esac

多个模式可以合并:1|2|3)
模式为 * 时匹配除了之前的模式的任一种可能, 如*)

示例:

read -p "输入您的年龄:" age

case $age in 
  8|9|10|11|12|13|14|15) 
     echo "您适合学习Python入门技术"
     echo "C入门"
     echo "H5前端技术"
     ;;
  16)
     echo "MySQL数据库技术"
     echo "Linux基本用法"
     ;;

  17)
     echo "Numpy多维数组"
     echo "Pytho网络编程"
     ;;
  18|19)
     echo "Python AI深度学习算法"
     ;;
  *)
     echo "$age 只适合学习Python,学习其它的语言的最好时光已错!"
     ;;
esac

8.4 循环控制

8.4.1 for循环

多行格式:

for var in item1 item2 ... itemN
do
    command1
    command2
    ...
    commandN
done

单行格式:

for var in item1 item2 ... itemN; do command1; command2… done;

【注意】关键字的前面增加;分号,语句每个命令后也增加;分号, 另外done关键后,也需要增加;分号。

示例1:

ns = (20 30 40)  # 创建数组
for n in ${ns[@]}
do
   echo $n
done
ns=(1 2 3 4)
for n in ${ns[@]};do echo $n; done;

【注意】ns是数组类型,访问元素使用下标${变量名[下标]} ,获取数组所有元素${变量名[@]}, 获取数组的长度使用${#变量名[@]}${#变量名[*]}

示例2:

read -p "输入您的身高、体重和手机号:" -a info

echo ${info[@]}  # 数组中的元素,通过下标访问,也可以通过@来获取所有元素

for x in ${info[@]} 
do
   echo "--> $x"

done


scores=(90 89 100 99)
echo "|--Python--|-- MySQL--|--JScript--|-- Shell--|"

for score in ${scores[@]}
do
   printf '  %-8s  ' $score
done

printf '\r\n'

【扩展】for死循环的写法

for (( ; ; ))
ifconfig > ip.txt

# enp0s3: 10.12.188.11

# 输出内容的命令,必须使得"``"符号括起来,才能执行。
for line in `cat ip.txt|awk '{print $1}'`
do  
     echo $line 
   echo $line

done

【提示】for循环可以迭代命令执行结果。

8.4.2 while循环

格式:

while (( condition ))
do
    command
done

示例:

i=1
s=0
while (( $i<=100 ))
do
      s=$[i+s]
      echo "$i, $s"
      let "i++"  # 执行 字符串的shell表达式,等价于 i=$[i+1]
done

【提示】循环条件 可以改成 [ $i -le 100 ]test $i -le 100

i=1
s=0
# while (( $i <= 100 ))
while [ $i -le 100 ]
do
   s=$[s+i]
   # i=$[i+1]
   let "++i"

done

echo "100以内的和: $s"

【问题】在if的条件语句中是否可以使用(( 条件 ))

【扩展】while死循环

while :
do
    echo '加油!'
done

8.4.3 until循环

until 循环执行一系列命令直至条件为 true 时停止
格式:

until condition
do
    command
done

示例:

a=0
until [ ! a -lt 10 ]
do 
	  echo a
    a=$[a+1]
done

8.4.4 break和continu

  • break命令允许跳出所有循环(终止循环)
  • continue结束本次循环,执行下一次循环

8.5 其它

8.5.1 命令行参数

命令形式: ./bb.sh 1 2 3

读取:$0 $1

读取参数个数: $#

读取所有参数:$*

示例:

#!/bin/sh

echo "命令行参数长度: $#"
if [ $# -gt 0 ] 
then
  echo $*
  echo $1

else
  echo " $0 未输入参数"

fi

注意: $0 是脚本文件名,$*表示文件脚本后的所有参数(以空格分隔),$#脚本名后的所有参数的个数。

#!/bin/sh

if [ $# -gt 0 ] ;then
  path=$1
  if [ -e $path -a -f $path ]
  then
     read -p "是否删除 $path (Y|n)"  y
     if [ -z $y ] ;then y="y" ;fi
     if [ $y = "y" -o $y = "Y" ] ;then rm -rf $path ;fi
  fi
else
  echo "请输入要删除文件的参数或--help查看帮助"
fi

以上脚本的文件名rem.sh, 可以为此脚本在/usr/bin目录创建一个软连接。

练习任务: 创建vis命令脚本,在创建文件时,自动在首行增加#!/bin/sh内容。

#!/bin/sh

if [ $# -gt 0 ] ;then
   path=$1
   # 判断文件是否为新的文件
   if [ ! -e $path ]
   then
      echo "#!/bin/sh" > $path
      echo >> $path
      chmod +x $path
   fi
   
   # 判断当前的sh脚本是否创建软连接
   if [ $# -eq 2 ] ;then 
      link=$2
      curdir=`pwd`  # 获取当前的路径
      ln -s "$curdir/$path" $link
   fi
   
   vi + $path  # + 参数表示打开文件后,定位到最后一行
else
   echo "必须提供一个.sh脚本文件路径"
fi

如: vis a.sh /usr/bin/a

8.5.2 定时任务

8.5.2.1 crond进程
  • crond是linux下用来周期性的执行某种任务或等待处理某些事件的一个守护进程
  • 当安装完成操作系统后,默认会安装此服务工具,并且会自动启动crond进程
  • crond进程每分钟会定期检查是否有要执行的任务,如果有要执行的任务,则自动执行该任务
8.5.2.2 查看 cron进程状态
service cron status
8.5.2.3 crontab命令

功能: 主要负责定时任务的管理
格式: crontab [options] [配置文件]
选项说明:

-u user:用来设定某个用户的crontab服务;
file:file是命令文件的名字,表示将file做为crontab的任务列表文件并载入crontab。如果在命令行中没有指定这个文件,crontab命令将接受标准输入(键盘)上键入的命令,并将它们载入crontab。
-e:编辑某个用户的crontab文件内容。如果不指定用户,则表示编辑当前用户的crontab文件。
-l:显示某个用户的crontab文件内容,如果不指定用户,则表示显示当前用户的crontab文件内容。
-r:从/var/spool/cron目录中删除某个用户的crontab文件,如果不指定用户,则默认删除当前用户的crontab文件。
-i:在删除用户的crontab文件时给确认提示。

示例1,查看 当前用户下的定时任务:

crontab -l	

示例2,删除指定用户的定时任务:

crontab -r -u 用户名

【提示】如果不指定用户名,则表示当前登录的用户

8.5.2.4 cron文件格式
<分钟> <小时> <日> <月份> <星期> <命令>

特殊字符:

* 代表所有可能的值,如:分钟上表示每分钟
, 指定一个列表范围,如分钟上 2,7 表示第2分钟和第7分钟
- 表示一个整数范围,如小时上15-17 表示下午3点到5点
/ 正斜线,表示间隔频率,如分钟上*/2, 表示每2分钟

示例1, 每分钟向日志文件中追加 hello:

* * * * * echo 'hello' >> log_cron.txt

示例2,每天下午3点到7点的第1、第24分钟时执行一次

1,24 15-17 * * *  echo 'hello' >> log3_cron.txt

示例3, 每隔2小时,执行一次

* */2 * * * /etc/init.d/ssh restart

通过crontab xx.cron 添加当前用户的定时任务

8.5.2.5 所有用户下的cron信息
ls  /var/spool/cron/crontabs -l

重要说明:

1. cron是分用户的,即每个用户都有自己的cron文件(/var/spool/cron/crontabs/目录下,以用户名命名的文件)
2. 系统级别的命令(如/sbin/reboot等)应该用root身份来编辑和载入crontab(/etc/crontab 文件)
3. 普通用户的crontab会自动执行, 普通用户的crontab有系统级别命令,则不会执行

8.5.3 变量与类型

8.5.3.1 变量命名规范
命名只能使用英文字母,数字和下划线,首个字符不能以数字开头
中间不能有空格,可以使用下划线“_”
不能使用标点符号
不能使用bash里的关键字, help命令查看保留关键字
8.5.3.2 变量操作
定义变量名:name = 'disen'
使用变量名: echo $name
只读变量(作为常量): readonly  变量名
删除变量: unset 变量名
8.5.3.3 变量作用域
  • 局部变量: 在脚本或命令中定义,仅在当前shell实例中有效
  • 环境变量:所有的程序,包括shell启动的程序,都能访问环境变量
    • .bash_profile 当前用户的环境变量
    • ~/.bashrc 环境变量文件
    • /etc/profile 所有用户的环境变量, env 显示系统的环境变量
    • export 导出环境变量
    • source .bash_profile 更新.bash_profile文件中的环境变量
      • source xx.sh 脚本, 执行脚本并同时导出环境变量
8.5.3.4 字符串类型
  • 单引号
    单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的
    单引号字串中不能出现单引号(对单引号使用转义符后也不行)

  • 双引号
    双引号里可以有变量
    双引号里可以出现转义字符

  • 拼接

    "hi, "$name"!""hi, $name!"

  • 字符串长度, # 符号, 如${#name}

  • 提取, 如${name:1:5} 从name变量的第2个位置开始提取5个字符

九、Docker工具

Docker是镜像容器的管理工具, 实现虚拟化技术 替代类传于VMware或VirtualBox虑拟环境。Docker基于硬件虚拟化, 实现多个独立的容器(微型操作系统)在同等的硬件资源下和基础的操作系统(Ubuntu)上构建的虚拟环境。

Docker简化的开发到运维的过程, 要求开发人员具备运维能力。

9.1 Centos7安装Docker

yum update
yum install  -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum list docker-ce --showduplicates | sort -r
uname -a 

以两个命令搜索适当的docker版本。

yum install -y docker-ce

安装完成后,检查docker环境

docker version

只有客户端信息,并没有启动docker服务。

运行以下命令启动docker后台服务:

systemctl start docker

如果想开机启动docker服务,则运行以下命令:

systemctl enable docker
systemctl disable docker

9.2 Docker的加速配置

Docker中运行的镜像从Docker Hub远程仓库中下载的,因为在外,需要配置加速器。

编辑/etc/docker/daemon.json文件, 如果文件不存在,则直接创建:

vi /etc/docker/daemon.json
{
  "registry-mirrors": ["https://y4tay211.mirror.aliyuncs.com"]
}

修改完成后,执行以下两个命令:

systemctl daemon-reload
systemctl restart docker
systemctl status docker

9.3 镜像和容器的关系

类似于类和对象的关系, 镜像对应是类, 容器对应是对象, 一个镜像可以运行多次成为容器,同一个类可以创建多个实现的思想一样。当然,在容器中安装或修改相关的内容之后,可以将容器保存为镜像。

一般开发人员,通过容器配置项目开发环境,等开发完成后,将容器保存为镜像,并向本地仓库上传,其他相关人员即可以从本地仓库下载并运行。

重要的三个方面:

  • 容器是镜像启动后产生,且容器具备name和ID属性,容器的Name是唯一的(启动镜像是指定的,或Docker自动生成)
  • 一个镜像可以启动多次, 每一次产生一个容器
  • 容器可以保存为镜像。

9.4 镜像命令

9.4.1 搜索命令
docker search mysql

根据搜索的镜像列表,可以看到完整的镜像名称,以便下载。

9.4.2 下载镜像
docker pull 镜像名:版本号

:版本号如果不指定,默认为最新的,即为:latest

如果从某一个私有仓库下载镜像时,需要做以下任务:

  • 编辑 /etc/daemon.json文件,添加insecure-registries列表中安全仓库地址ip:5000

  • 重启当前系统的守护进程的信息, 运行systemctl daemon-reload

  • 重启docker服务, 运行systemctl restart docker

  • 通过docker pull命令从指定IP和Port的私有仓库下载,如:

    docker pull 10.36.172.83:5000/mysql

9.4.3 查看本地镜像
docker images
9.4.4 删除镜像

要求: 运行的容器没有一个属于待删除镜像的容器。

docker rmi [-f] 镜像全名[:tag]或ID

-f 强制删除镜像(镜像存在运行中的容器时)

9.4.5 运行镜像
docker run [-i -t -d] [--name name] [-v local_path:path] [-p local_port:port] [--network network_name] [-e envirment_name=value] 镜像全名[:tag]或ID

-i 表示input,可以进入容器中,

-t 表示打开新的terminal 终端, 一般和-i组合使用

-d 让容器处于后台运行, 同步返回容器的ID

–name 指定容器的名称

-v (Volumn) 将本地的文件路径和容器的文件绑定,同步文件数据。

-p 将本地的端口与容器中服务端口绑定。

-e 指定容器的环境变量,容器的程序在运行时需要的变量。

9.4.6 部署mysql示例
docker run -tid --name db2 -p 3307:3306 -e MYSQL_ROOT_PASSWORD=root -v /root/sql:/usr/src/sql mysql
docker run -d --name db0 -e MYSQL_ROOT_PASSWORD=root -p 3306:3306 -v /root/sql:/usr/src/sql mysql
9.6.7 Docker私有仓库

下载registry镜像(docker hub远程仓库):

docker search registry

查看镜像的信息:

docker images

启动镜像,产生一个容器:

docker run -d --name local_hub -p 5000:5000  registry

查看容器的运行状态:

docker ps [-a|l]

-a 所有的容器(运行的、停止的)

-l最后一个正在运行的容器

不能任何参数时,表示列出所有正在运行的容器。

如果出现错误操作,可以删除容器:

docker rm d07e62ec9480

设置当前地址为安全仓库地址, vi /etc/docker/daemon.json, 增加"insecure-registry"

{
  "registry-mirrors": ["https://y4tay211.mirror.aliyuncs.com"],
  "insecure-registries": ["10.36.172.83:5000"]
} 
systemctl daemon-reload
systemctl restart docker 

如果是私有仓库的服务器,则还需要启动registry容器。

为上传的镜像通过tag新建别名:

docker tag registry  10.36.172.83:5000/registry

向私有仓库推送镜像:

docker push 10.36.172.83:5000/registry

在其它的客户端(网络可达):

docker pull 10.36.172.83:5000/registry

【注意】客户端需要配置安全仓库地址insecure-registries

9.5 容器命令

9.5.1 查询容器状态

因为容器是Docker守护进程(后台进程)的子进程,执行以下命令可以查看正在运行的容器信息:

docker ps 

如果想查看最近一个运行的容器:

docker ps -l

如果想查看所有的容器,包含未运行的:

docker ps -a
9.5.2 启动与停止容器
docker stop 容器名或容器ID
docker start 容器名或容器ID
docker restart 容器名或容器ID
9.5.3 进入容器

当容器启动后,可以进入到容器中,执行Linux相关的命令,完成软件安装或项目部署。

docker exec -it 容器名或ID bash 
9.5.4 执行容器命令

如果只想单独执行某一项命令:

docker exec 容器名或ID Linux命令
# 查看db1容器中的/usr/src/sql目录的所有文件
Disen:~ apple$ docker exec db1 ls /usr/src/sql -la
total 32
drwxr-xr-x 9 root root  288 May 28 03:48 .
drwxr-xr-x 1 root root 4096 May 26 07:31 ..
-rw-r--r-- 1 root root 1026 May 26 07:29 1.sql
-rw-r--r-- 1 root root  968 May 26 08:31 2.sql
-rw-r--r-- 1 root root   87 May 27 03:07 3.sql
-rw-r--r-- 1 root root   30 May 27 03:40 4.sql
-rw-r--r-- 1 root root  175 May 27 07:24 5.sql
-rw-r--r-- 1 root root  622 May 27 02:07 w18.sql
-rw-r--r-- 1 root root  178 May 27 01:53 w8.sql
docker exec -it db0 mysql -uroot -proot

执行db0命令中的mysql命令,并进入到mysql打开的终端上(Terminal)。

9.5.5 复制文件

通过docker cp命令将本地文件复制到容器中,或者容器中文件复制到本地来。

docker cp 容器名:源文件路径  本地目标路径
docker cp 本地源路径 容器名:目标文件路径

相关示例:

docker cp /root/2.sql db0:/

将本地的2.sql文件复制到 db0容器的/目录下。

docker cp db0:/etc/mysql .

将容器db0中的/etc/mysql目录复制到本地的当前目录中。

9.5.6 容器日志

当容器启动后,可以查看容器中服务的运行日志:

docker logs 容器名或ID
9.5.7 删除容器
docker rm [-f] 容器名或ID

-f 如果容器运行的状态下,强制停止容器并删除。

十、MySQL服务

10.1 Yum安装MySQL安装

10.1.1 mysql的repo源

可以下载的源如下: https://dev.mysql.com/downloads/repo/yum/

wget http://repo.mysql.com/mysql57-community-release-el7-10.noarch.rpm

如果wget未安装,则运行yum install -y wget安装。

运行如下命令,安装mysql源包:

rpm -Uvh mysql57-community-release-el7-10.noarch.rpm

10.1.2 安装mysql

yum install -y mysql-community-server

安装成功后启动mysql服务:

systemctl start mysqld.service

可以查看mysql是否启动:

systemctl status mysqld.service

因为mysql在安装后,会生成一个临时口令,查看临时密码命令:

grep password /var/log/mysqld.log

进入mysql:

# mysql -uroot -p

输入临时口令,即可以进入mysql客户端。

10.1.3 修改口令规则

MySQL官网密码策略详细说明:http://dev.mysql.com/doc/refman/5.7/en/validate-password-options-variables.html#sysvar_validate_password_policy

另外,如果需要修改用户的口令,需要先修改口令的生成规则:

mysql> set global validate_password_policy=0;
mysql> set global validate_password_length=1;
  • validate_password_policy:密码策略,默认为MEDIUM策略

-validate_password_length:密码最少长度

如果想查看密码规则,可以运行如下命令:

mysql> show variables like '%password%';

validate_password_dictionary_file:密码策略文件,策略为STRONG才需要 validate_password_mixed_case_count:大小写字符长度,至少1个
validate_password_number_count :数字至少1个
validate_password_special_char_count:特殊字符至少1个

另外,不需要密码策略,还可以在/etc/my.cnf文件配置:

validate_password = off

10.1.4 修改root密码

mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY '密码';

10.1.5 远程登录

GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '口令' WITH GRANT OPTION;
FLUSH PRIVILEGES;

10.1.6 修改字符集

编辑 /etc/my.cnf (yum安装 MySQL 5.7)或 /etc/mysql/my.cnf (容器),增加的内容如下:

[client]
default-character-set=utf8
# ...

[mysqld]
character-set-server = utf8
# ...

/etc/mysql/conf.d/mysql.cnf (docker mysql8)客户端配置文件

[mysql]
default-character-set = utf8

【注意】修改服务相关的内容需要重启mysql服务。

可以执行如下命令,查看当前mysql服务的字符集设置情况:

mysql> show variables like '%character%';
+--------------------------+--------------------------------+
| Variable_name            | Value                          |
+--------------------------+--------------------------------+
| character_set_client     | latin1                         |
| character_set_connection | latin1                         |
| character_set_database   | utf8mb4                        |
| character_set_filesystem | binary                         |
| character_set_results    | latin1                         |
| character_set_server     | utf8mb4                        |
| character_set_system     | utf8                           |
| character_sets_dir       | /usr/share/mysql-8.0/charsets/ |
+--------------------------+--------------------------------+
8 rows in set (0.01 sec)

将mysql容器中的配置文件复制到主机上:

docker cp db0:/etc/mysql/my.cnf my.cnf
docker cp db0:/etc/mysql/conf.d/mysql.cnf mysql.cnf

修改完成后,将文件再复制到容器中:

docker cp  my.cnf  db0:/etc/mysql/my.cnf
docker cp  mysql.cnf db0:/etc/mysql/conf.d/mysql.cnf

启动mysql容器:

docker restart db0

再次进入mysql容器的mysql服务中:

docker exec -it db0 mysql -uroot -proot
mysql> show variables like '%character%';
+--------------------------+--------------------------------+
| Variable_name            | Value                          |
+--------------------------+--------------------------------+
| character_set_client     | utf8                           |
| character_set_connection | utf8                           |
| character_set_database   | utf8                           |
| character_set_filesystem | binary                         |
| character_set_results    | utf8                           |
| character_set_server     | utf8                           |
| character_set_system     | utf8                           |
| character_sets_dir       | /usr/share/mysql-8.0/charsets/ |
+--------------------------+--------------------------------+
8 rows in set (0.00 sec)

10.2 Docker安装MySQL

配置docker 的加速器:

10.2.1 下载镜像

默认镜像是从docker hub下载的,因为是国外的站点,所以下载的速度较慢。

配置docker的加速器, 可以在阿里云上免费申请。

vi /etc/docker/daemon.json , 增加如下内容:

{
  "registry-mirrors": ["https://y4tay211.mirror.aliyuncs.com"]
}

重启docker服务。

systemctl daemon-reload
systemctl restart docker
docker pull mysql	

c:/windows/system32/drivers/etc/hosts 记事本编辑(Win+R => notepad ):

127.0.0.1 localhost
10.12.153.219  lserver
ssh root@lserver

10.2.2 启动镜像

将镜像启动之后, docker daemon进程将会创建一个容器子进程。

镜像和容器的关系,类传于类和实例对象的关系。镜像可以运行多次,即创建多个容器。容器也可以保存为镜像。

docker run -itd --name db0 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -v /root/sql:/usr/src/sql mysql
  • -t 代表可以打开一个终端窗口
  • -i 代表可以进入到一个容器中
  • -d 代表容器在后台运行
  • -p 绑定本地的端口和容器的端口
  • -e 代表容器的Linux的环境变量

【注意】-v 是将本地的目录和容器的目录进行同步,这样方便调试sql脚本。

进入mysql容器中(db0):

root@tserver  # docker exec -it db0 bash
root@facakaai # mysql -uroot -proot
mysql>

10.2.3 解决password问题

由于容器下拉的是MySQL8, 而它的加密规则同Mysql5不同,因此在使用pymysql连接数据库时,会存在密码不正确问题,以下是解决办法:

> use mysql
> alter user 'root'@'%' identified by 'root' password expire never;
> alter user 'root'@'%' identified with mysql_native_password by 'root';

10.2.4 基于Python编写mysql连接测试

安装 pymysql 库

pip install pymysql -i https://mirrors.aliyun.com/pypi/simple

编写 test_conn.py 脚本,内容:

import pymysql

# localhost 可以修改为 IP地址,如 10.36.172.34
# 3306是当前主机的端口,不是容器的端口
conn = pymysql.Connect(host="localhost", port=3306, user="root", password="root", db="mysql", charset="utf8")

print('Connected OK')

检查 当前系统是否已安装pymysql 包:

pip freeze | grep PyMySQL

测试结果 : 发到 [email protected] 邮件中,截图放在正文中。

​ 标题: XA-PY-2001 第36天作业-李小明

​ 最晚时间: 11点半

​ 未完成: 必须发,且说明情况。

十一、数据库编程

每个数据库服务都具有自己的网络端口,如mysql 3306、redis 6379、sqlserver 1433、elasticsearch 9200。

11.1 数据库的认知

数据库: 永久性存储数据的仓库,按数据的媒体类型分关系型(结构化)和非关系型(二进制或字节码)。

关系型数据库: SQLServer(字符集: GBK/GB2312/GB16080)、MySQL(版本: 5.5, 5.6、5.7, 8.0)、Oracle(9i/10g/11g, 12c)、DB2、 MariDB(同MySQL是一个开发者)等。
非关系型数据库:  redis、mongodb(js)、elasticsearch 全文检索引擎(Lucene)
大数据库: hive、hbase、spark(Hadoop)
常用的字符集:
ASCII :  一个字符一个字节(8位)
各国家语言:  中国 GBK、GB2312  一个汉字占两个字节, 一个ascii占一个字节
万国编码:  
		unicode  每个字符占2个字节, 不支持ascii码, 如正则中表示中文的范围写法 \u4e00-\u9fa5
		utf-8    支持ascii码, 即英文字符占1个字节, 中文占三个字节

11.2 MySQL必备知识

官方文档: https://dev.mysql.com/doc/refman/8.0/en/

11.2.1 root用户

安装完mysql之后,进入mysql数据库进行修改root的权限(口令、远程连接)

#  mysql
mysql> use mysql
mysql> select user, host from user;
mysql> update user set host="%" where user="root";
mysql> alert user "root"@"%" with mysql_native_password identified by "root";
mysql> grant all privileges on *.* to "root"@"%" identified by "root";
mysql> flush privileges;

以上是mysql5.x版本执行的,在mysql8.x版本中,grant 语句中不能带有identified语句。

11.2.2 查看数据库的引擎

在MySQL中,存在不同的数据引擎,适应不同的开发环境(项目要求)。

mysql> show engines;

支持数据库引擎: MEMORY(内存)、InnoDB(默认的,支持事务、行级锁、外键)、MyISAM、CSV等

11.2.3 常见的数据操作

11.2.3.1 数据库管理

创建数据库:

create database db_name default charater set utf8;
create database db_name charset utf8;
create database <数据库名> [ default character set utf8 ];

【提示】数据库名是唯一的,若已存在,则会报错。每一条SQL语句以分号;结束。

示例:

create database stu default character set utf8;

查看已存在的数据库:

show databases;

修改数据库名:

rename database <原数据库名> to <新数据库名>;

【注意】MySQL 8.x 中无效。

在MySQL8.x中作法:

  • 备份数据: mysqldump -uroot -proot <数据库名> > xx.sql
  • 删除库: drop database <数据库名>
  • 创建新的库(修改的新名) : create database <新数据库>
  • 恢复数据: 打开新的数据库, source 执行 xx.sql。

删除数据库:

drop database ;
drop database [if exists] ;  -- 如果db_name存在,则删除,不存在则继续。

修改数据库:

-- 设置数据库的默认字符集
alter database <db_name> default character set utf8;  
-- 查看创建数据库的语句
show create database db_name;  

打开数据库:

use db_name
11.2.3.2 数据表管理
11.2.3.2.1 创建表
create table <table_name>(
   field_name 字段的类型(长度或大小) [ 约束 ] [comment "注释"],  -- 字段级约束
   field_name 字段的类型 [ 约束 ] [,]
   [ constraint 约束类型 字段 ]  -- 表级约束
) [ engine=InnoDB ] [ default charset=utf8]

字段类型:

varchar 字符类型
interger 整型,一般用于主键
float 小数类型
enum  枚举类型
date  日期类型, 年月日格式
time  时间类型,  时分秒格式
timestamp 日期时间类型
int  整型
long 长整型
text 文本类型

约束:

primary key 主键约束, 具有唯一、非空两个特性。
not null  非空约束, 数据不能为空。
unique    唯一
foreign key  外键, 只有 InnoDB引擎存在, 数据来源于主表(另一个关联表)中的主键。
auto_increment 自增, 常和主键约束组合使用。
default  默认值
check 检查约束, 目前MySQL不支持, SQLServer和Oracle(varchar2, number)支持。

示例:

-- /root/sql/day2_1.sql
-- 创建人员表(人员编号、 姓名、入职时间、 岗位)
drop table if exists tb_person;
create table tb_person(
   pid int primary key auto_increment,
   name varchar(20) not null,
   hire_date date,
   job_name varchar(20) default 'Python开发工程师'
);

-- 创建作业记录表(记录编号、 时间、标题、 正文、是否已完、作者)
drop table if exists workhome_record;
create table workhome_record(
  record_no int primary key ,
  record_time timestamp,
  record_content text not null,
  record_title varchar(50) unique comment '标题',
  status enum('Y', 'N') comment '作业状态:Y已完成, N 未完成',
  author_id int  comment '作业人员编号',
  constraint record_person_fk foreign key (author_id) references tb_person(pid)
);

【注意】表名的字母是区分大小写的。

11.2.3.2.2 查看表

查看数据库中存在哪些表:

show tables;
-- 查看stu_db0库下所有的表
-- 从字典表中查询: information_schema.tables
select table_name,table_type
from information_schema.tables
where table_schema='stu_db0';

查看表结构:

desc[ribe] <表名>
mysql> desc workhome_record;
+----------------+---------------+------+-----+---------+-------+
| Field          | Type          | Null | Key | Default | Extra |
+----------------+---------------+------+-----+---------+-------+
| record_no      | int           | YES  |     | NULL    |       |
| record_time    | timestamp     | YES  |     | NULL    |       |
| record_content | text          | YES  |     | NULL    |       |
| record_title   | varchar(50)   | YES  |     | NULL    |       |
| status         | enum('Y','N') | YES  |     | NULL    |       |
| author         | varchar(20)   | YES  |     | NULL    |       |
+----------------+---------------+------+-----+---------+-------+
6 rows in set (0.00 sec)
11.2.3.2.3 修改表

可以修改表的结构,包含字段、表名、约束等。

-- 修改表名
alter table <表名> rename <新表名>;
alter table workhome_record rename homework_record;

修改表字段:

alter table <表名>
modify <字段名> 字段类型[(长度)] [约束] [comment '注释说明']; 
-- 修改homework_record表中record_no字段,增加自增约束。
alter table homework_record 
modify record_no int auto_increment;

表字段重命名:

alter table <表名>
change [column] <原列名> <新列名> 类型 [约束] [comment ''];
-- 修改homework_record表中的record_no字段,重命令名record_id
alter table homework_record
change record_no record_id int;

【提示】change修改字段名时,原字段的约束则会失效(自动删除原列的约束, 除主键或外键之外)。

增加表字段:

alter table <表名>
add [column] <新列名> 类型 [约束] [comment ''];
-- 增加人员表tb_person的基本工资列 salary
alter table tb_person
add salary float default 10000;

删除表字段:

alter table <表名>
drop [column] <字段名>;
-- 删除tb_person表的job_name字段
alter table tb_person
drop job_name;

【扩展】SQL语句的分类: DDL(数据库定义语言)、DML(数据库操纵语言-CURD)、DTL(数据库事务语言)、DCL(数据库控制语言)。

11.2.3.2.4 删除表
drop table [if exists] <表名>;

【注意】表删除之后,表中的数据也会被删除。

11.2.4 表的CURD操作
11.2.4.1 select语句
select [*|表名.*] [,][ [表名.]字段名 [as 字段别名], [表名.]字段名2 [as 字段名2别名], ...]
from <表名> [as] [表别名]
[join <表名2>  on (表名.列名  = 表名2.列名 )  ]
[where 条件表达式]
[group by 分组的字段]
[having 聚合字段的条件表达式] 
[order by 排列字段]
[limit 起始行号, 每页显示的记录行数]
-- 查看information_schema.innodb_indexes表的所有字段(*)
-- 查看information_schema.innodb_indexes表的index_id, name,table_id, type
-- 表声明别名为 a
select index_id, name, table_id, type
from information_schema.innodb_indexes as a;

select name index_name 
from information_schema.innodb_indexes a;

select a.name as index_name 
from information_schema.innodb_indexes a;

select a.name as 索引名 from information_schema.innodb_indexes a;
select name 索引名,table_id 表ID, type 索引类型 
from information_schema.innodb_indexes a;

where条件表达式:

字段名 = 字段值  -- varchar类型的字段需要使用单引号
字段名 <关系运算符> 数值  -- > , < , >=, <= , !=
字段名 in (1, 值2...)  -- 查询字段值的范围在 (值列表中)
select name 索引名,table_id 表ID, type 索引类型 
from information_schema.innodb_indexes a
where type=1;

-- 查询索引类型为2以上的索引(包含2)
select name 索引名,table_id 表ID, type 索引类型 
from information_schema.innodb_indexes a
where type >= 2;

-- 查询索引类型为是0或3的索引
select name 索引名,table_id 表ID, type 索引类型
from information_schema.innodb_indexes a
where type in (0,3);

select name 索引名,table_id 表ID, type 索引类型
from information_schema.innodb_indexes a
where type not in (0,3);
select name 索引名,table_id 表ID, type 索引类型 
from information_schema.innodb_indexes a
where name='PRIMARY';
11.2.4.2 insert语句
-- 新增数据时,可以指定哪些字段及它的数据
-- 默认情况下,为所有的字段指定数据, 字段顺序按创建表时的字段顺序。
insert into 表名 [ ( 字段1, 字段2 ) ]
values (字段1值, 字段2);
-- 新增员工(李阳, 男,2020年5月21入职, 入职薪资15000,手机号18413219087, 暂无详细地址, 工作城市在西安)
insert into person
values(1, '男', '李阳', '18413219087', null, '西安', '2020-05-21', 15000.0);

-- 优化SQL语句
insert into person(name,phone,hire_date,salary)
values('李阳', '18413219087', '2020-05-21', 15000.0);
-- 新增三名员工
--(陈盼,女, 手机号19021887654, 入职时间2020年6月12日,入职薪资8000)
--(王盼,女, 手机号19021888654, 入职时间2020年6月29日,入职薪资18000)
--(王小军,男, 手机号19021878645, 入职时间2020年7月15日,入职薪资16500)

-- insert语句支持批量插入
insert into person(name,sex,phone,hire_date, salary) values
('陈盼','女','19021887654', '2020-06-12', 8000),
('王盼','女','19021888654', '2020-06-29', 18000),
('王小军', '男', '19021878645', '2020-07-15', 16500.0);
-- 新增技术部门,管理者是陈盼( person.id = 2)
insert into department(name, manager_id)
values('技术部', 5);
11.2.4.3 update语句
update 表名 set
字段1=字段1值 [, 字段2=字段2值, ...]
[where 条件表达式]

【注意】如果不增加where条件语句,则表示针对所有的记录(行)的特定的字段修改它的数据。

-- 增加财务部、行政部、项目部、商务部
-- 四个部门的负责人都是王盼(id: 3)

insert into department(name,manager_id) values
('财务部', 3),
('行政部', 3),
('项目部', 3),
('商务部', 3);
select * from department;
-- 修改技术部的负责人为王小军
-- 条件: 技术部
-- 修改字段: manager_id,  字段值为姓名为王小军人员的ID
update department
set manager_id=4
where name='技术部';
-- 修改陈盼这位员工的手机号为19877651234和工资为当前工资的1.2倍
-- 【提示】修改或查询SQL语句中,针对列支持算术运算。
update person set 
phone='19877651234',
salary=salary*1.2
where name='陈盼';
-- 变更技术部为研发部
update department set
name='研发部'
where name='技术部';
11.2.4.4 delete语句
delete from 表名
[where 条件表达式]

【提示】不加where条件的delete语句,表示清空表。

【事务相关的语句】

  • begin 开启事务
  • commit 提交事务
  • rollback 回滚事务

【建议】只要是DML语句(修改、插入和删除),都要开启事务。

-- 删除务商部
begin;
delete from department where name='商务部';
select * from department;
-- commit;  -- 最终删除
rollback;  -- 取消删除

外键约束的级联删除

  • 添加外键约束时, 可以指定级联选项
    • on delete cascade 当主表中的主键所在行的数据被删除时,外键字段所在行则被级联删除
    • on delete set null 当主表中的主键所在行的数据被删除时,外键字段则被设置为null
    • on update cascade 级联更新, 当主键的值发生变化后,外键的值也同步修改。
-- 增加员工的所属部门字段,默认值为Null
-- 设置级联操作项为级联删除
-- [外键约束在此无效]
alter table person
add depart_id int references department(id) on delete cascade;

-- 先添加字段,再添加约束
alter table person
add depart_id int;

alter table person
add constraint person_depart_fk foreign key (depart_id) 
references department(id) on delete cascade;

【注意】使用add column语句时,不能同时指定列的外键约束,需要通过add constraint添加。

【提示】所有的约束信息可以通过information_schema.table_constraints字典表查询。

-- 查询person表的所有约束信息
select table_schema,constraint_name,constraint_type 
from information_schema.table_constraints 
where table_name='person';
-- 查询person_db库下所有表的所有约束信息
select table_name,constraint_name,constraint_type 
from information_schema.table_constraints 
where table_schema='person_db';
-- 将李阳(1),陈盼(2)两位员调整到研发部(id: 2)
update person
set depart_id=2
where id in (1, 2);
-- 删除表中的外键约束
alter table person
drop constraint person_depart_fk;
-- 增加外键,指定 on delete set null 级联项
alter table person
add constraint person_depart_fk foreign key (depart_id) 
references department(id) on delete set null;
-- 增加外键,指定 on update cascade 级联项
alter table person
add constraint person_depart_fk foreign key (depart_id) 
references department(id) on update cascade;
11.2.4.5 join连表查询

join关键字可以连接其它表,并且可以指定连接表的条件。

-- 查看所有员工的ID、姓名和所在部门的名称
-- where等值条件连接(内连接)
select p.id, p.name as pname, d.name depart_name
from person p,department d
where p.depart_id=d.id;

【注意】

  • 多表连接查询时,查看多表存在相同列名时,需要指定表名前辍,如 person.id
  • 表可以使用别名,如果表指定了别名,在整个SQL语句中不能使用原表名。
  • 多表连接时,如果不指定等值连接条件,则会出现笛卡尔乘积(m*n)。
-- join-on方式实现(内连接、左外连接、右外连接)
select p.id, p.name as pname, d.name depart_name
from person p
join department d on (p.depart_id = d.id);
-- 查看所有员工的ID、姓名和所在部门的名称、员工的管理者姓名
select p.id, p.name as pname, d.name depart_name, mgr.name mgr_name
from person p,department d, person mgr
where p.depart_id=d.id
and   d.manager_id=mgr.id;

【总结】多表连接时,如果有n个表连接,则有n-1个等值连接条件。

insert into person(name,sex,phone,hire_date, salary,depart_id) values
('陈世美','男','19121887654', '2020-06-02', 18000,3),
('王佳美','女','19021777654', '2020-06-19', 12000, 4),
('蒋世林', '男', '19121878645', '2020-07-25', 11500.0,5);                                     
-- 查看所有员工的ID、姓名和所在部门的名称、员工的管理者姓名
select p.id, p.name as pname, d.name depart_name, mgr.name mgr_name
from person p
join department d on (p.depart_id=d.id)
join person mgr on (d.manager_id=mgr.id);

左外连接(left join): 查询的数据不仅包含内连接的数据,也包含左表的其它数据。

右外连接(right join): 查询的数据不仅包含内连接的数据,也包含右表的其它数据。

全外连接(left join union right join):包含内连接的数据、左外连接的数据和右外连接的数据,使用union联合查询。

select p.id, p.name, d.name dept_name
from person p
left join department d on (p.depart_id=d.id);
select p.id, p.name, d.name dept_name
from person p
right join department d on (p.depart_id=d.id);
select p.id, p.name, d.id dept_id, d.name dept_name
from person p
left join department d on (p.depart_id=d.id)
union 
select p.id, p.name, d.id dept_id, d.name dept_name
from person p
right join department d on (p.depart_id=d.id);

【注意】union 可以跟all表示两个查询SQL语句数据完全联合(不去重,即可能存在重复的数据)。使用union连接两个SQL时,每个SQL的select选择字段必须保持一致。

11.2.4.6 orderby子句

order by用于对查询数据进行排序的,支持升序(默认 ASC)和降序( DESC )两种方式。

python中sorted()的key参数是什么类型,它的默认排序算法是什么?
-- 查询所有员工的姓名、入职时间和薪资,并按薪资的降序(从大到小)方式排列
-- desc -> descend
-- asc -> ascend
select name,hire_date,salary
from person
order by salary desc;
-- 查询员工信息,按入职时间的先后顺序显示
select id,name,phone,hire_date,salary
from person
order by hire_date;
11.2.4.7 limit子句

limit子句可以对查询结构的数据进行分页显示,格式:

-- offset 表示起始行的索引编号 ,从0开始
-- rows 表示本次显示的行数,可以理解一页显示的记录数
-- 假如 每一页显示 10记录,第5页的offet是多少?是40
-- 每一页显示 5条记录,第6页的offset是多少?  
-- offset = (page-1)*rows
limit offset, rows
-- 按每一页3条记录,查看第2页的所有员工的数据
-- 对数据按入职时间进行排序
select id,name,phone,hire_date,salary
from person
order by hire_date
limit 3,3
11.2.4.8 like模糊查询

like是用于对文本类型的数据进行模糊查询,可以使用两个通配符:

`_` 任意一个字符
`%` 任意多个字符
-- 查询李姓员工所有的信息
select *
from person
where name like '李%';
-- 查询手机号第三位是0的所有员工信息
select * 
from person
where phone like '__0%';
11.2.4.9 子查询

子查询: 在查询语句包含内部的查询语句,内部的查询语句称之为子查询, 子查询一般使用小括号。

使用场景:

  • 复杂的查询语句使用,包含where条件中使用
  • 创建表或视图时,如分表存储时,创建第二张表的语句可以通过第一张表的查询来完成。
  • 插入数据
-- 查询最高薪资的员工的所有信息
select * from person
where salary = 
(
  select salary
  from person
  order by salary desc 
  limit 0,1);
select person.* from person
join 
(
  select salary
  from person
  order by salary desc 
  limit 0,1 
) A  on (A.salary=person.salary);
-- 根据薪资分布情况,分别创建3张表,表结构同员工表
-- <=10000 表1 person1
-- 大于10000, 小于等于15000  表2 person2
-- 大于 15000  表3 person3
create table if not exists person1
select * from person
where salary<=10000;

create table if not exists person2
select * from person
where salary>10000 and salary<=15000;

create table if not exists person3
select * from person
where salary>15000;
select * from 
(
  select * from person1
  union
  select * from person2
  union
  select * from person3
) A
order by id;

day37天-作业:

1. 创建数据库person_db
2. 创建表person员工表(id编号 ,sex性别, name姓名, phone手机号, address详细地址, city工作城市, hire_date 入职时间, salary入职薪资)
3. 创建表department部门表(id编号,name部门名称,manger_id管理者ID) -- 管理者ID也是员工ID,默认值为0
4. 创建表tb_salary工资表(id编号, pid员工编号, publish_date 发放工资日期, salary发放工资)
5. 练习select语句(mysql.user表-必查字段user,host, authentication_string)
-- 1. 创建数据库person_db
drop database if exists person_db;
create database person_db;
use person_db;

-- 2. 创建表person员工表
drop table if exists person;
create table person(
   id int primary key auto_increment,
   sex varchar(2) default '男',
   name varchar(50) not null,
   phone varchar(11) unique,
   address varchar(50),
   city varchar(10) default '西安',
   hire_date date,
   salary float default 5000.0
);

-- 3. 创建表department部门表
drop table if exists department;
create table department(
  id int primary key auto_increment,
  name varchar(20) unique,
  manager_id int default 0,
  constraint depart_person_fk foreign key (manager_id) references person(id)
);

-- 4. 创建表tb_salary工资表
drop table if exists tb_salary;
create table tb_salary(
  id int primary key auto_increment,
  pid int ,
  publish_date date,
  salary float,
  constraint salary_person_fk foreign key(pid) references person(id)
);

练习(day38任务):

创建以下4张表:
student 表:  sn 学号、name 姓名、 age 出生日期、 sex 性别
cource  表:  cn 课程号, name 课程名, tn 教师号
tearch  表:  tn 教师号, name 教师名
score   表:  sn 学号, cn 课程号 , score 成绩

它们之间的关系:  
1. cource 表的tn 是外键与 tearch表的tn关联
2. score 表中sn和cn是外键,分别与student表和cource进行关联
3. sn、cn、tn 都是主键
4. cource表的name是唯一的
-- vi /root/sql/1.sql
-- coding: utf8
drop database if exists stu;
create database stu charset utf8;
use stu;
create table student(
  sn varchar(20) primary key,
  name varchar(20) not null,
  age timestamp default current_timestamp,
  sex char(2) default "男"
);

create table cource(
  cn varchar(20) primary key,
  name varchar(20) not null unique,
  tn  varchar(20)
);

create table teacher(
  tn varchar(20) primary key,
  name varchar(20) not null
);

-- 修改cource 表增加外键约束
-- on delete cascade 级联删除(主表中的数据删除后,从表的数据也会删除)
-- on delete set null 级联置null(主表中的数据删除后,从表的数据则会设置为null)
-- on update cascade 级联更新
alter table cource
add constraint fk_cource_tn foreign key(tn)
references teacher(tn) on delete set null;

create table score(
  sn varchar(20) references student(sn), -- 列级别的外键约束
  cn varchar(20) references cource(cn),
  score float(10,2) comment "成绩"
) comment "成绩表";
-- 修改cource表名为 course
rename table cource to course;
mysql> source /root/sql/1.sql
mysql> show tables;
-- 2.sql
insert into student(sn, name, age, sex) values
("01" , "赵雷" , "1990-01-01" , "男"),
("02" , "钱电" , "1990-12-21" , "男"),
("03" , "孙风" , "1990-05-20" , "男"),
("04" , "李云" , "1990-08-06" , "男"),
("05" , "周梅" , "1991-12-01" , "女"),
("06" , "吴兰" , "1992-03-01" , "女"),
("07" , "郑竹" , "1989-07-01" , "女"),
("08" , "王菊" , "1990-01-20" , "女");

insert into teacher(tn, name) values
("01" , "张三"),
("02" , "李四"),
("03" , "王五");

insert into cource(cn, name, tn) values
("01" , "语文" , "02"),
("02" , "数学" , "01"),
("03" , "英语" , "03");

insert into score(sn, cn, score) values
("01" , "01" , 80),
("01" , "02" , 90),
("01" , "03" , 99),
("02" , "01" , 70),
("02" , "02" , 60),
("02" , "03" , 80),
("03" , "01" , 80),
("03" , "02" , 80),
("03" , "03" , 80),
("04" , "01" , 50),
("04" , "02" , 30),
("04" , "03" , 20),
("05" , "01" , 76),
("05" , "02" , 87),
("06" , "01" , 31),
("06" , "03" , 34);
-- 尝试删除 教师编号为01的教师记录
begin;  -- 开启事务
delete from teacher where tn="01";
select * from cource;  -- 发现数学的教师的编号为Null
-- 执行创建表或数据库的语句(DDL)后,事务会自动提交
create database a;
rollback;  -- 回滚事务,撤消开启事务之后的一切更新操作。
DDL(Database Define Language) 数据库描述或定义语言
    数据库、表结构、视图、索引、约束的创建、修改、删除等相关的SQL语句。
  
DML (Database Manipulation Language) 数据库操纵语言
    查询、修改、增加、删除表的数据
DCL (Database Control Language) 数据库控制语言
    修改数据库运行环境的语句:set 、 grant、 source
DTL (Database Transaction Language) 数据库事务语言
   begin
   commit
   rollback
-- 添加数学的老师为 张三, 01
-- desc[ribe] teacher  查看表结构
-- order by name desc  排序中的降序,默认是asc升序
insert into teacher(name, tn) values("张三", "01");
update cource set tn="01" where name="数学";

练习-2(day38任务):

-- 1、查询"01"课程比"02"课程成绩高的学生的信息及课程分数
-- 多表连接方式
select s.*, A.c1 课程01,A.c2 课程02
from student s
join
(
  select c1.sn,c1.score c1, c2.score c2
  from (select sn,score from score where cn='01') c1
  left join (select sn,score from score where cn='02') c2 on(c1.sn=c2.sn)
) A on(A.sn=s.sn)
where A.c1 > ifnull(A.c2,0);
-- 1、查询"01"课程比"02"课程成绩高的学生的信息及课程分数
-- 多表连接方式
select s.*, 
       s1.cn as "c01", 
       s1.score as "c01_score",
       s2.cn as "c02",
       s2.score as "c02_score"
from score s1
join score s2 on(s1.sn = s2.sn)
join student s on (s1.sn = s.sn)
where s1.cn="01"
and s2.cn="02"
and s1.score > s2.score

-- if(条件,为真的值,为假的值) 函数写法
select s.*, A.c01, A.c02
from student s
join (
    select sn,
      max(if(cn="01", score, 0)) as c01,
      max(if(cn="02", score, 0)) as c02
    from score
    GROUP BY sn
    ) A on (A.sn = s.sn)
WHERE A.c01 > A.c02

面试题:
现有表A数据:
year month amout
1991  1     1.1
1991  2     1.2
1992  1     2.1 
1992  2     2.2

如何编写SQL脚本,实现如下的查询结果:
year  m1   m2
1991  1.1  1.2
1992  2.1  2.2

select year, 
       max(if(month=1, amout,0)) as m1,
       max(if(month=2, amout,0)) as m2
from A
group by year;


-- 8、查询没学过"张三"老师授课的同学的信息 
select *
from student s
where s.sn not in (
  select distinct sc.sn
  from teacher t
  join cource c on (c.tn=t.tn)
  join score sc on (sc.cn=c.cn)
  where t.name = "张三"
);

-- 18、查询各科成绩最高分、最低分和平均分:以如下形式显示:课程ID,课程name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率
-- 及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90
select sn,cn,score,
      case when score >=60 then "及格" 
           when score >=70 then "中等"
           when score >=80 then "优良"
           when score >=90 then "优秀"
           else "不及格" end status
from score;

select sn,cn,score,
      case when score >=90 then 1
           else 0 end d1,
      case when score >=80 and score <90 then 1
           else 0 end d2,
      case when score >=70 and score <80 then 1
           else 0 end d3,
      case when score >=60 then 1
           else 0 end d4
from score;

select sn,cn,score,
      if(score>=90, 1, 0) d1,
      if(score>=80 and score < 90, 1, 0) d2,
      if(score >=70 and score < 80, 1, 0) d3,
      if(score >=60,1, 0) d4
from score;

-- 最终SQL 
select c.cn, c.name, 
       max(sc.score) "最高分", 
       min(sc.score) "最低分", 
       avg(sc.score) "平均分",
       round(sum(A.d1)/count(A.d1),2) "及格率",
       round(sum(A.d2)/count(A.d2),2) "中等率",
       round(sum(A.d3)/count(A.d3),2) "优良率",
       round(sum(A.d4)/count(A.d4),2) "优秀率"
from cource c
join score sc on (sc.cn = c.cn)
join (
   select cn,
          if(score>=60, 1, 0) d1,
          if(score>=70 and score<80, 1, 0) d2,
          if(score>=80 and score<90, 1, 0) d3,
          if(score>=90, 1, 0) d4
   from score
) A on (A.cn = sc.cn)
group by c.cn, c.name;

11.2.4 事务特性-ACID

事务: 处理某一件事件或功能的过程,在MySQL中处理事件的过程中,单独提供某一通道(Channel),在事件处理之前可以开启事件点,处理完成后,在结束时关闭事件点。在关闭事件之前,可以回滚到事件起点。

数据库的事务具有四大特性

Atomicity(原子性):  事务中的所有操作要么都成功,要么都失败。
Consistency(一致性): 事务开始和结束的数据保持一致的(保持数据的完整性)。
Isolation(隔离性):    事务之间互不影响
Durability(持久性):  数据存储之后,即使系统出现故障,数据也会持久保存。
事务的四个隔离级别 isolation level:
read uncommitted 读未提交: A事务可以读B事务未提交的修改数据。
                          问题:【脏读】【不可重复读】【幻读】
read committed   读已提交: A事务可以读取B事务已提交的数据。 【不可重复读】【幻读】
repeatable read 可重复读【默认】: A事务修改数据时,会读取其它事务提交后数据,再进一步修改,保持数据的一致性。 【幻读】
							
serializable    序列化或串行化:  多个事务在修改同一条数据时,必须等待其他事务完成才能执行。

【注意】当开启事务之后, 如果发生了DDL语句时,会自动地提交事务。

客户端设置事务隔离级别的语句:
set session transaction isolation level 隔离级别名;

查看当前会话中的事务隔离级别:

mysql> show variables like '%isolation%';
+-----------------------+-----------------+
| Variable_name         | Value           |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.00 sec)

设置当前会话的隔离级别为读未提交

set session transaction isolation level read uncommitted;

练习:

1. 打开A终端,设置事务隔离级别为 read uncommitted, 打开数据库,打开事务,查询person3表
2. 打开B终端, 打开数据库,打开事务
  - 【不可重复读】【脏读】
		o 删除person3数据, 在A 中查询person3数据时,发现数据被删除
		o rollback 事务,  在A中再次查询是,发现数据又存在
  - 【幻读】
    o 将person表中的1,2两位员工插入到 person3表中, 在A中查询,多了2位员工
    o 在A中,更新新员工中编号为1,将其薪资提高10%
    o 回滚事务, 在A中,再次查看修改之后数据,发现数据不存在了
set session transaction isolation level read committed;
set session transaction isolation level repeatable read;
set session transaction isolation level serializable;

11.2.3 常见数据库的函数

11.2.3.1 聚合函数

配合分组语句,针对特定的字段数据进行分组聚合计算相关的数据,如最大、最小、平均、总和、数量等。

【注意】聚合函数中sum/max/min/avg主要针对数值类型或日期时间类型(int, float)。

count()  统计行数
sum()    计算总和
max()    最大值
min()    最小值
avg()    平均值
-- 分别计算出员工表中男生和女生的平均薪资、最高薪资、最低薪资和总薪资及人数
select sex, 
       avg(salary) avg_sal,
       max(salary) max_sal,
       min(salary) min_sal,
       sum(salary) sum_sal,
       count(1) person_cnt
from person
group by sex;

【提示】如果在select子句中使用了聚合函数,且存在未使用聚合函数的字段,则将未使用聚合函数的字段添加到group by子句中。

-- 统计员工的总薪资和总人数
select sum(salary) sum_sal, count(1) total_person from person;
-- 练习1: 统计各部门的员工数量和平均薪资
select d.name, count(1) cnt, avg(salary) avg_sal
from person p
join department d on(p.depart_id=d.id)
group by d.name;


-- 练习2: 统计每月入职的员工数量和最高薪资【提示】month(hire_date) 获取月份
-- 【注意】 count()函数中的参数可以是除NULL以外任意内容
select month(hire_date) month,count('a') cnt, max(salary)
from person
group by month(hire_date);
11.2.3.2 字符函数
concat(a, b) 将a和b拼接成一个字符串
length("子符串") 计算字符串的字节长度
char_length()  计算字符个数
lower()  小写转换
upper()  大写转换
replace(str, sub, replace_str) 替换子子符串
trim() 同python的strip()函数,删除两连的空白
instr(str, sub) 返回sub在str的首次出现的位置, 索引位置是从1开始
lpad(str,len,sub)  如果str的长度不足时,在左边填充sub字符, 同Python的str对象的rjust()
rpad(str, len, sub) 如果str的长度不足时,在右边填充sub字符, 同Python的str对象的ljust()

char(N) 将AscII数值转成字符, N可以是一个字符,也可以是数值 【注意】在8.0中返回十六进制字符串。
ord(str) 将ASCII字符转成数值

format(n, 小数位长) 将n转成带千位符‘#,###.##’格式的字符串
mysql> select concat('a', 'b') title;
+-------+
| title |
+-------+
| ab    |
+-------+
mysql> select concat('a', 'b', 123, 15.56) title;
+------------+
| title      |
+------------+
| ab12315.56 |
+------------+
-- MySQL8.0 select子句中的表达式别名,可以用在group by和order by子句中
select concat(month(hire_date),'月') month,count('a') cnt, max(salary)
from person
group by month;
-- char 字符类型,给定的长度是固定的,给多少用多少(字符个数)。
-- varchar 可变长度的字符类型,字段的内容有多少,占用多少(字符个数)。
alter table person
add status char(1) default 'y';

update person set 
status='n' where id in(5, 6);
select replace('hi,disen', 'disen', 'jack');
+--------------------------------------+
| replace('hi,disen', 'disen', 'jack') |
+--------------------------------------+
| hi,jack                              |
+--------------------------------------+
select replace('hi,disen', 'disene', 'jack');
+---------------------------------------+
| replace('hi,disen', 'disene', 'jack') |
+---------------------------------------+
| hi,disen                              |
+---------------------------------------+
 select format(19292992, 2);
 select format('19292992', 2);
 
 select id,name,phone,sex, concat('$', format(salary,2)) as salary from person;
11.2.3.3 数值函数
round(n, 小数位长)  四舍五入
ceil(n) 上行取整,比n大的,最小的整数
floor() 下行取整,比n小的,最大的整数
pow(n, c) 计算n的c次方   
mod(n1, n2) 计算两者的余数
rand() 随机产生[0,1)之间数,如 ceil(rand()*15) + 1,生成[1,16]之间的数
abs()  求绝对值
sin()/asin()/cos()/acos()/tan()/atan()/con() 数学相关的三角函数
mysql> select id,name,salary, round(salary*rand()*0.1,2) jx from person;
+----+-----------+--------+---------+
| id | name      | salary | jx      |
+----+-----------+--------+---------+
|  1 | 李阳      |  15000 | 1297.34 |
|  2 | 陈盼      |   9600 |  365.46 |
|  3 | 狄玉环    |  22000 |  679.22 |
|  4 | 王小军    |  20000 |  803.27 |
|  5 | 陈世美    |  12000 |   98.34 |
|  6 | 王佳美    |  12000 |  245.84 |
|  7 | 蒋世林    |  11500 |  895.24 |
+----+-----------+--------+---------+
11.2.3.4 日期函数
current_date()/curdate()  当前日期
current_timestamp()/now() 当前日期时间
curerent_time()/curtime() 当前时间

year()  获取日期中年
month() 获取日期中的月
day()   获取日期中的天, 一个月中第几天
dayofweek()  获取星期几, 1是星期日, 7是星期六
dayofyear()  获取一年中的第几天
weekofyear() 获取一年中第几周
hour()
minute()
second()

datediff(now(), "2019-10-11")  获取两个日期之间的间隔天数
timediff("12:10:09", "15:10:09") 获取两时间之间的问隔时间(时-分-秒)

str_to_date(日期字符串, "格式")  将字符串转成日期, 格式: %Y-%m-%d %H:%i:%s
date_format(data, "格式")  将时间转成字符串

date_add(date, interval n 时间关键字) 某一时间添加 n的间隔(关键字)时长后的日期时间
		时间关键字: year,month, day, hour, minute, second, week
date_sub(data, interval n 时间关键字) 某一时间减少n的间隔(关键字)时长后的日期时间
select current_date;
select now();
select sn,name,date_format(age, '%Y年%m月%d日') birth_day from student;

-- 统计80后和90后的学生人数
select date_format(age, '%y')
from student;
select str_to_date('12/20/1990', '%m/%d/%Y');
-- 在'12/20/1990'时间点上,15天之后的日期
select date_add(str_to_date('12/20/1990', '%m/%d/%Y'),  interval 15 day);

-- 查看当前时间之后的69个小时之后的日期是星期几
select dayofweek(date_add(now(), interval 69 hour))-1 as week;
-- 15天之后是几号?星期几? 是一年中第几天?
select date_add(now(), interval 15 day) "15天后的日期", dayofweek(date_add(now(), interval 15 day))-1 "星期", dayofyear(date_add(now(), interval 15 day)) "年的天"; 
-- 46、查询各学生的年龄
 select sn,name,sex,ceil(datediff(now(), age)/365) from student;
 
-- 47、查询本周过生日的学生
select * 
from student
where weekofyear(now()) = weekofyear(age);

-- 48、查询下周过生日的学生
select * 
from student
where weekofyear(now())+1 = weekofyear(age);

-- 49、查询本月过生日的学生
select * 
from student
where month(now()) = month(age);

-- 50、查询下月过生日的学生
select * 
from student
where month(now())+1 = month(age);
11.2.3.5 条件函数【重点】
if(条件, 条件为真的结果, 条件为假的结果)
ifnull(v1, v2) 如果第一个值为null时,返回v2, 反之返回v1。
case 分支语句:
-- MySQL 不支持全外连接
select s.*, ifnull(sc.score, 0) score
from student s
left join score sc on (sc.sn = s.sn);
select sn, name, sex, age, 
       case month(age) when 1 then "A"
                       when 2 then "B"
                       when 3 then "C"
                       when 4 then "D"
                       when 5 then "E"
                       when 6 then "F"
                       when 7 then "G"
                       when 8 then "H"
                       when 9 then "I"
                       when 10 then "J"
                       when 11 then "K"
                       when 12 then "L"
                       else "Z" end "level"
from student
order by level;  -- 只有order by语句可以使用 查询中的新增列名
-- where level = "A"; where 语句只针对原有表字段

select sn, name, sex, age, 
       case when month(age)>=2 and month(age)<=4 then "A"
            when  month(age)>=5 and month(age)<=7 then "B"
            when month(age)>=8 and month(age)<=10 then "C"
            else "D" end "level"
            
from student
order by level;
11.2.3.6 加密算法函数
password()  -- MySQL5存在的, MySQL 8不存在
md5(str)  将str转成md5编码字符,长度32位 
sha1()   将str转成sha1编码字符,长度40位 
uuid()   获取uuid的字符串, 字符串中带有`-`符号,可以使用replace()函数将其去除。

11.2.4 高级子查询

11.2.4.1 多表连接查询
-- join on 方式
	-- 内连接 join on 
	-- 左外连接 left join on  
	-- 右外连接 right join on 
	
	select s.*, sc.*, c.*
	from student s 
	left join score sc on (sc.sn=s.sn)
	right join cource c on (c.cn = sc.cn)
	
	
-- 等值连接条件(属于内连接)
select s.*, sc.*, c.*
from student s, score sc , cource c
where s.sn  = sc.sn
and sc.cn = c.cn;


-- 联合查询
select * from s1
union all -- all 表示不去重
select * from s1;
11.2.4.2 子查询
作用: 
	- 可以作为条件使用
	- 可以创建视图或表
	- 批量插入数据
-- 将student表分成两张表, 以出生的月份作为条件(6月中位线)
drop table if exists s1;
create table s1
select * from student
where month(age) <=6;

drop table if exists s2;
create table s2
select * from student
where month(age) >6;
-- 将s1的数据合并到s2中
insert into s2
select * from s1;
11.2.4.3 视图
视图: 将复杂的查询语句进行封装,对视图查询,即是将视图对应的SQL语句作为子查询语句使用。
-- 查询学生的所有信息(基本信息,成绩、课程名、教师名)
create  view v_all_student as 
select s.*, sc.score, c.name cource, t.name as teacher
from student s
left join score sc on (sc.sn=s.sn)
join cource c on (c.cn=sc.cn)
join teacher t on (t.tn = c.tn)
删除视图:
drop view view_name;

11.3 Python的MySQL编程

11.3.1 pymysql库

pymysql是属于第三方库, 需要安装:

(py201) d:\codes> pip install pymysql -i https://mirrors.aliyun.com/pypi/simple
# 导包
from pymysql.connections import Connection
from pymysql.cursors import DictCursor

# DB配置
db_params = {
    "host": "10.12.153.232",
    "port": 3307,
    "user": "root",
    "passwd": "root",
    "charset": "utf8",
    "db": "stu",
    "autocommit": True,  # 自动提交事务
    "cursorclass": Cursor
}

# 测试连接
conn = Connection(**db_params)
print("--OK--")
# 执行查询SQL
# 查询没有上"张三"老师课程的学生信息
sql8 = """
select *
from student s
where s.sn not in (
  select distinct sc.sn
  from teacher t
  join cource c on (c.tn=t.tn)
  join score sc on (sc.cn=c.cn)
  where t.name = %s
)
"""

def query_not_cource(teacher_name):
    """
    查询未学习过指定老师所带课程的学生信息
    :param teacher_name 教师姓名
    :return:
    """
    cursor = conn.cursor() # 获取游标对象

    cursor.execute(sql8, (teacher_name, ))  # 执行SQL脚本

    # 获取查询结果
    print(cursor.fetchall())  # 返回是可迭代的对象,元素是元组

    # 关闭游标
    cursor.close()
if __name__ == "__main__":
    query_not_cource("张三")
    conn.close()  # 关闭连接
# 优化函数
def query_not_cource(teacher_name):
    """
    查询未学习过指定老师所带课程的学生信息
    :param teacher_name 教师姓名
    :return:
    """
    with conn as c:
       c.execute(sql8, (teacher_name, ))  # 执行SQL脚本

       # 获取查询结果
       print(c.fetchall())  
# 练习1-尝试获取rowcount属性值
# rowcount 返回查询数据的行数,插入、修改或删除操作后的影响的记录数
def add_teacher(**data):
    sql = "insert into teacher values( %(tn)s, %(name)s )"
    with conn as c:
        ret = c.execute(sql, data)
        print("-->>", ret)
        print("--rowcount--", c.rowcount)
        if c.rowcount > 0:
            print("新增 教师成功")
            
# 练习2-尝试获取stu库中所有的对象(表、视图、索引、函数和存储过程)
# mysql存在一个字典库: information_schema
def show_all_table():
    # mysqlclient 是否支持设置事务的isolation level 隔离级别
    with conn as c:
        sql = """
            select table_name,table_type
            from information_schema.tables
            where table_schema = "stu"
        """
        c.execute(sql)
        for table_name in c.fetchall():
            print("-->", table_name)
# 思考: 批量插入1000条数据,最优的写法是什么?
# 默认情况下,插入一条提交一次事务。最优的做法是先开事务、插入数据、提交事务。
conn.begin()
# 执行相关的语句
conn.commit()

11.3.2 DAO设计

类的类型:
- 数据类、 实体类(Entity),表示业务中与数据表对应的类。
- 业务类、功能类(Service),对数据类进行增删改查操作。
- 工具类,扩展功能(Utils或Helper),在处理业务时,方便调用,如md5加密、日志记录、缓存、短信验证、图片验证码、简化网络操作。
- 数据操作类 DAO类: 只对象数据实体类,简化对象与数据库的操作。
class Singleton:
    def __init__(self, cls):
        self.cls = cls

    def __call__(self, *args, **kwargs):
        if not hasattr(self.cls, "instance"):
            self.cls.instance = self.cls(*args, **kwargs)

        return self.cls.instance
@Singleton
class DB():
    def __init__(self):
        params = {
            "host": "10.12.153.232",
            "port": 3307,
            "user": "root",
            "passwd": "root",
            "charset": "utf8",
            "db": "stu",
            "cursorclass": pymysql.cursors.DictCursor  
        }
        self.conn = pymysql.Connect(**params)

    def __enter__(self):
        return self.conn.__enter__()

    def __exit__(self, exc_type, exc_val, exc_tb):
        return self.conn.__exit__(exc_type, exc_val, exc_tb)

    def __del__(self):
        self.conn.close()

DictCursor字典游标类,查询结果自动转化列表+字典的结构,这样的结果便于json序列化。

class BaseDao():
    def __init__(self, table_name=None, pk_name="id"):
        self.table = table_name  # 当前Dao操作的表名称
        self.pk = pk_name  # 当前Dao操作的表的主键名

        self.db = DB()

    def get_table(self, obj=None):
        if self.table is None:
            self.table = obj.__class__.__name__.lower()

        return self.table

    def save(self, t):
        # sql = "insert into teacher(tn, name) values( %(tn)s, %(name)s )"
        sql = "INSERT INTO %s(%s) VALUES (%s)"

        table = self.get_table(t)

        colums = ",".join(t.__dict__)
        colums_placeholds = ",".join(["%%(%s)s" % name for name in t.__dict__])

        with self.db as c:
            c.execute(sql % (table, colums, colums_placeholds), t.__dict__)
            self.msg("Add", c.rowcount)

    def update(self, t):
        # update student set name=%(name)s,age=%(age)s,sex=%(sex)s where sn="08"
        sql = "UPDATE %s SET %s WHERE %s"
        obj_dict = t.__dict__

        # 获取主键值
        pk = obj_dict.pop(self.pk)  # 弹出主键列和它的值, 避免set 语句中出现主键值更新

        update_cols = ",".join("%s=%%(%s)s" % (col_name, col_name) for col_name in obj_dict)
        where_str = f"{self.pk}={pk}"

        with self.db as c:
            c.execute(sql % (self.get_table(), update_cols, where_str), obj_dict)
            self.msg("Update", c.rowcount)

    def remove(self, pk):
        sql = f"delete from {self.get_table()} where {self.pk}={pk}"
        with self.db as c:
            c.execute(sql)
            self.msg("Delete", c.rowcount)

    def remove_batch(self, *pks):
        with self.db as c:
            for pk in pks:
                sql = f"delete from {self.get_table()} where {self.pk}={pk}"
                c.execute(sql)
            self.msg("Delete", c.rowcount)

    def get(self, cls, pk):
        # select * from student where sn=%s
        sql = "SELECT * FROM %s WHERE %s"
        with self.db as c:
            c.execute(sql % (self.table, f"{self.pk}={pk}"))
            ret = c.fetchone()

        return cls(**ret)

    def list(self, cls):
        sql = f"select * from {self.get_table()}"
        with self.db as c:
            c.execute(sql)
            ret = c.fetchall()

        # 将[{}] 转成 [<对象>]
        return [cls(**dict_obj) for dict_obj in ret]

    def msg(self, title, rowcount):
        if rowcount > 0:
            print(f"{title} OK")
        else:
            print(f"{title} Error")
class TeacherDao(BaseDao):
    def __init__(self):
        super(TeacherDao, self).__init__("teacher", "tn")

    def save(self, t):
        super(TeacherDao, self).save(t)

    def update(self, t):
        super(TeacherDao, self).update(t)

    def remove(self, pk):
        super(TeacherDao, self).remove(pk)

    def get(self,pk):
        return super(TeacherDao, self).get(Teacher, pk)

    def list(self):
        return super(TeacherDao, self).list(Teacher)

    def remove_batch(self, *pks):
        super(TeacherDao, self).remove_batch(*pks)


if __name__ == "__main__":
    dao = TeacherDao()

    # t1 = Teacher("08", "Disen")
    # dao.save(t1)
    #
    # t2 = dao.get("08")
    # t2.name = "狄老师"
    # dao.update(t2)
    #
    # print(dao.get("08"))
    # dao.remove("08")

    dao.remove_batch("04", "05", "06")
    print(dao.list())

11.3.3 ORM设计

# ORM(Object Relationshap Mapping)对象关系映射
# 元类: 创建类的类, 默认的元类是type
#!/usr/bin/python3
# coding: utf-8
class Field():
    pass

class CharField(Field):
    def __init__(self, max_length=50):
        self.max_length = max_length


class IntegerField(Field):
    def __init__(self, default=0):
        self.default = default


# 定义模型元类
class ModelMeta(type):
    def __new__(cls, cls_name, cls_bases, cls_attrs):
        if cls_name == "Model":
            return type.__new__(cls, cls_name, cls_bases, cls_attrs)
        # 提取实体类的字段名和表名
        attrs = {}
        for k, v in cls_attrs.items():
            if isinstance(v, Field):
                attrs[k] = v

        table_name = cls_name.lower()
        meta = cls_attrs.get("Meta", None)
        if meta:
            if hasattr(meta, "db_table"):
                table_name = meta.db_table

        cls_attrs.pop("Meta")
        cls_attrs["attrs"] = attrs
        cls_attrs["table_name"] = table_name

        return type.__new__(cls, cls_name, cls_bases, cls_attrs)


class Model(metaclass=ModelMeta):
    def get_db_type(self, field):
        if isinstance(field, CharField):
            return f"varchar({field.max_length})"
        elif isinstance(field, IntegerField):
            return f"integer default {field.default}"

        return "varchar(50)"

    def create(self):
        # create table table_name (col_name col_type(length), ..)
        cols = ",".join([
            "%s %s" % (k, self.get_db_type(v))
            for k, v in self.attrs.items()
        ])

        sql = f"create table {self.table_name}({cols})"
        print(sql)

    def save(self):
        pass

    def update(self):
        pass


class TeacherModel(Model):
    tn = CharField(max_length=20)
    name = CharField(max_length=20)
    level = IntegerField(default=10)

    class Meta:
        db_table = "teacher"


if __name__ == "__main__":
    t = TeacherModel()
    t.create()

你可能感兴趣的:(Centos7-Linux)