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)等。
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,最后回车确认退出并保存。
CentOS 7于2014年7月7号正式发布,这是一个企业级的Linux发行版本,基于Red Hat红帽免费公开的源代码。相对于之前版本,升级内容如下:
首个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网卡等。
[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
【目标】学习Linux时,除了认知它的文件体系之外,应该掌握常用的指令、Vim编缉器的使用、Shell脚本的写法和各类服务应用的安装与管理等。
学习Linux的命令,可以参考https://www.runoob.com/linux/linux-tutorial.html文档
在学习Linux丰富的指令之前,可以先学习以下几个实用的命令:
man 查看命令帮助信息
用法: man 命令
进入命令之后,有以下四个按键功能:
下一行示例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 命令
命令格式: ls [options] [file]
常用参数:
-a 显示所有文件及目录 (ls内定将文件名或目录名称开头为"."的视为隐藏档,不会列出)
-l 除文件名称外,亦将文件型态、权限、拥有者、文件大小等资讯详细列出
-r 将文件以相反次序显示(原定依英文字母次序)
-t 将文件依建立时间之先后次序列出
-A 同 -a ,但不列出 "." (目前目录) 及 ".." (父目录)
-F 在列出的文件名称后加一符号;例如可执行档则加 "*", 目录则加 "/"
-R 若目录下有文件,则以下之文件亦皆依序列出
-i 显示文件的iNode的信息(文件索引号)
--full-time 显示完整的文件修改时间(默认显示 MM dd hh:mm)
示例:
ls -a
ls -ltrF
是ls命令的别名,可以通过which 查看:
# which ll
alias ll='ls -l --color=auto'
【扩展】 alias 命令别名,可以将复杂的命令指定简化的名称。如:
alias la='ls -lA --color=auto'
取消一个命令的别名,可以使用unalias 命令的别名
,如:
unalias la
[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
第六列和第七列: 表示月份和几日
第八列: 表示文件修改的时间
最后列: 表示文件或目录的名称
格式: cd 目录名
目录名的特殊符号:
/c
功能:显示当前的目录
【扩展】在python中,获取当前目录位置:
os.getcwd()
os.path.dirname(os.path.abspath(__file__))
格式: 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"
格式: 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
【注意】可以更新多个文及目录的最近修改时间
命令格式同mkdir,如下示例:
rmdir -pv 目录/子目录
【注意】删除空的目录
格式: rm [options] 文件或目录名
功能: 可以删除文件和目录。
参数:
-f 强制删除
-r 删除所有子目录及文件
-d 删除空目录
-v 显示删除操作
示例:
rm -rf apiserver
rm -rf ./*
【注意】./*
表示当前目录下的所有文件及子目录。
格式: 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 文件的硬连接
功能: 将源文件或目录移动目标位置上, 可以实现文件重命名的功能。
格式:
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
【提示】.
当前目录, ..
父级目录
命令格式: 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
命令: cat [OPTION] FILE
参数:
-A, --show-all 显示所有内容
-n 显示行号
示例:
cat 1.sql # 显示文件的内容
cat -nb 1.sql # 显示文件内容,并显示行号, 但空行不显示行号
cat -nA 1.sql # 显示文件内容,包含行结束符 $
可以和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中正则不包含转义字符。
格式: 命令 | 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个字符
在Linux中,一个文件存储分为三块: 文件名、INode(文件的索引编号, ls -i可以查看)和 文件。
硬连接: 硬连接就是一个文件有两个名字。 【注意】文件目录不能创建硬连接
软连接: 软连接是一个完整的文件,软连接最终指向另外一个文件。
【提示】软连接指向的文件被删除后,软连接依然存在,另外重新在连接位置添加文件,还可以继续指向。
格式: 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文件
命令格式: 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
文件权限分三个部分(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--
格式:chmod [ugoa][+-=][rwx] filename
说明:
a: ugo 表示全部
权限操作:
+ 增加权限
- 去除权限
= 赋予权限
示例:
chmod 777 filename
chmod +x aa.sh
格式: 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
类似于 who am i
命令,显示当前用户的ID(名称)。
命令格式
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 天你的密码就要过期了,请尽快重新设置你的密码!”。
密码过期后的宽限天数: 可以在宽限的时间内,允许用户登录。
账号失效时间: 在规定的时间之外,不论你的密码是否过期,都将无法使用(或登录)。
格式:usermod 选项 用户名
参数:
同useradd
-l 新用户名, 新的用户名不能是已存在的。
示例:
usermod -G lili,dage davie # 为davie用户添加两个附加组。
usermod -e 2020-07-10 davie # 修改davie用户的过期时间
usermod -l Davied davie # 修改用户登录名
格式:userdel 选项 用户名, 默认用户删除不会删除家的
【重要】使用 -r 可以在删除用户的时候删除相关信息,如示例:
userdel -r judy
userdel 命令使用如下值退出:
0 成功
1 无法更新密码文件
2 无效的命令语法
6 指定的用户不存在
8 用户已经登录
10 无法更新组文件
12 无法删除主目录
命令格式: 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 # 执行删除口令操作前后,可以观察用户口令的信息变化。
格式: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组信息,:分隔的最一后一部分是组中用户成员,多个成员用逗号分隔。
格式: groupdel 组ID(名称)
示例:groupdel mysql
groupdel mysql
groupdel nice # 从/etc/group文件中删除组信息。
主组 -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:
附加组可以随时删除
和附加组相关联的用户会自动解除关系
【注意】组的文件在配置中/etc/group, 内容格式如下:
组名: 口令:组号:组内用户列表
【提示】x 或 * 表示组没有口令。
修改文件所属组及用户。
命令格式:
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目录下。
每一个服务都是一个进程,存在服务进程(后台进程或守护进程)。
此命令同service功能相同。在Window机器中通过 services.msc 命令打开服务面板。
格式: systemctl status 服务
示例:
systemctl status sshd
# service的命令
service sshd status
格式: systemctl start 服务名
示例:
systemctl start sshd
# service命令
service sshd start
格式: systemctl stop 服务名
示例:
systemctl stop sshd
# service命令
service sshd stop
格式: systemctl restart 服务名
示例:
systemctl restart sshd
# service命令
service sshd restart
格式: systemctl enable 服务名
示例:
systemctl enable sshd
格式: systemctl disable 服务名
示例:
systemctl disable sshd
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 :做一个更为完整的输出。
命令:
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 所下达的指令名称
命令:
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:该程序的实际指令。
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
在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
可以干掉或中断进程, 信号是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]
参考: /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客户端程序。
参数 :
-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和内存使用情况。
命令格式: df [选项] [文件]…
参数:
-a, --all 所有文件信息: pseudo 虚假的, duplicate 重复的, inaccessible 不可达
-h, --human-readable 显示人性化的文件大小 (如, 1K 234M 2G)
-i, --inodes 显示inode 信息而非块使用量
-k 即--block-size=1K
-l, --local 只显示本机的文件系统
-T, --print-type 显示文件类型
df -h
df -ah
df -ahlT
df -hT
格式:
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 显示版本信息并退出
du -sh
-s 统计求和,将所有目录及文件的大小的总和计算出来。结果只有一行。
du -h --max-depth=1 /home
统计/home下所有用户的目录的空间使用情况。
比较以下命令的结果:
du -c -d 1 /home
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
。
格式:
fdisk [选项] <磁盘> 更改分区表
fdisk [选项] -l <磁盘> 列出分区表
fdisk -s <分区> 给出分区大小(块数)
参数:
-b <大小> 扇区大小(512、1024、2048或4096)
-c[=<模式>] 兼容模式:“dos”或“nondos”(默认)
-h 打印此帮助文本
-u[=<单位>] 显示单位:“cylinders”(柱面)或“sectors”(扇区,默认)
-v 打印程序版本
-C <数字> 指定柱面数
-H <数字> 指定磁头数
-S <数字> 指定每个磁道的扇区数
fdisk -l
如,新增了一个硬盘 /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
格式化: mkfs 默认使用ext2文件格式
mkfs.ext3使用ext3文件格式,如对/dev/sdb1 命令如下所示:
mkfs.ext3 /dev/sdb1
格式:
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 输出版本信息并退出
mount 命令可以将某一个分区挂载到某一个空的目录中(可创建新的目录):
mkdir /mnt/sdb1
一般挂载的文件目录都会在 /mnt下创建。
mount /dev/sdb1 /mnt/sdb1
编辑 /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
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]#
格式:
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] ...
ifconfig # 所有网卡信息
ifconfig enp0s3 # 查看enp0s3网卡信息
格式: fconfig 网卡名称 ip地址 netmask 子网掩码
ifconfig enp0s3 192.168.31.90 netmask 255.255.255.0
修改之后,可以通过ifconfig查看,如果存在sshd远程连接服务,需要重启动network及sshd服务。
格式: ifconfig 网卡名称 down
ifconfig enp0s3 down
格式: 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()
格式:
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]}
ip a
ip address
ip r
ip route
hostname
hostname server1
格式: 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
通过 alias 为 firewall-cmd 定义名称为 fwc。
firewall-cmd --state
默认是未开启。
service firewalld start|stop|restart
# systemctl start|stop|restart firewalld
systemctl disable|enable firewalld
【注意】云服务开放端口还需要到安全组中,添加指定的端口。
firewall-cmd --list-port
如查询80端口是否开放:
firewall-cmd --query-port=80/tcp
firewall-cmd --add-port=80/tcp --permanent
开放端口后,需要重载配置,命令如下:
firewall-cmd --reload
firewall-cmd --remove-port=80/tcp
指定只允许哪些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服务。
修改/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,反之为1if [ $? -gt 0 ]
判断 检验结果是大否大于0,如果大于0表示黑名单中不存在这个IP缩写定时任务文件secure_ssh.cron 内容如下:
*/2 * * * * sh /usr/local/bin/secure_ssh.sh
比示每2分钟检查一次。前5位表示时间格式: 分钟 小时 日 月 周。
通过crontab 命令添加这个任务:
crontab secure_ssh.cron
crontab -l 查询当前用户的定时任务
通过vi 命令来编辑一些文本或脚本文件。
可以移动方向键
shift+: 进入命令模式
按i
键可以进入插入模式。
编程模式
ESC 进入普通模式
set number 显示行号
set nonumber 取消显示行号
n 移动第n行
/内容 搜索内容
%s/原内容/新内容/ig 全文或当前行替换内容
set ft=UNIX 设置文件格式为UNIX格式, 解决Window上开发的sh脚本不能在Linux中执行的问题。
w 写入
q 退出, q! 强制退出
x 写入并退出
普通模式可以转换为插入(按i、o、shift+o)和命令行模式
插入模式只能转换为普通模式(ESC)
命令行模式只能转换为普通模式(ESC, Del )
插入模式和命令行模式不能直接进行转换
【注意】普通模式是状态转换的桥梁
i:光标当前位置 insert
Shift + i: 光标所在行首 insert
a:光标所在的后一个位置 insert
Shift + a: 光标所在行尾 insert
o: 光标所在行下插入空行
Shift + o: 光标所在行上插入空行
cc: 剪切当前行,进入当前。 和dd命令相似,但dd不会进入编辑模式
r:只替换一个,之后变成 普通模式
Shift + r (R):替换模式,可连续替换,直到模式切换为止。
[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: 取消撤消
/ : 查找字符
.sh
执行脚本的Shell解释器
, 如/bin/sh
chmod +x bb.sh
./bb.sh
相对路径/home/disen/bb.sh
绝对路径格式:
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开始,显示第一个位置的信息
输入重定向到file中, 即将file作为输入的内容 传给命令。
示例:
wc -l < /etc/passwd
统计用户个数, /etc/passwd作为输入的内容传给wc 命令。
cat /etc/passwd|awk -F: '{print $1}'|uniq -c | wc -l
从键盘输入,输入到EOF结束, 可以输入多行.
示例:
wc -l << EOF
good
yes
no
EOF
将命令的结果重定向到 file中。
示例:
wc -l /etc/passwd > lines.txt
如果执行某一项目,不想输出也不想保留,可以重定向输出到 /dev/null
:
service sshd start > /dev/null
将命令的结果追加输出重定向到 file中。
示例:
wc -l /etc/passwd >> lines.txt
wc -l /etc/group >> lines.txt
输出指定的信息
示例:
echo 'hello'
printf 命令模仿 C 程序库(library)里的 printf() 程序
格式: printf format-string [arguments...]
参数说明:
对齐、宽度及小数点说明:
%-ns
-
左对齐, 没有-
表示右对齐(默认)%-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
if [ 2 == 2 ]; then echo good ; fi
【注意】中括号内部的两侧必须有空格, 关系运算符两侧建议有空格。
数值的关系运算时,除了 ==
和 !=
,其它的关系建议使用关系字符表示,参考test命令中的数值测试。
;
分隔if结构中的关键字,如then
、fi
,参考多行if语句。
read -p 第一个数: a
read -p 第二个数: b
echo "预测第一个数 $a 大于第二个数 $b"
if [ $a -gt $b ]
then
echo ok
fi
【注意】字符串包含变量时,必须使用双引号。
a=10
b=20
if [ $a -gt $b ]
then
echo ok
else
echo error
fi
关系运算符的字母表示:
-gt 大于
-ge 大于等于
-lt 小于
-le 小于等于
-eq 等于
a=100
b=20
if [ $a -gt $b ]
then
echo ok
elif [ $a -gt 20 ]
then
echo fail
else
echo error
fi
【注意】赋值语句时,=
符号前后不要加空格。
test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件的测试。
-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
= 等于则为真
!= 不相等则为真
-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"
-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
功能: 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
多行格式:
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循环可以迭代命令
执行结果。
格式:
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
until 循环执行一系列命令直至条件为 true 时停止
格式:
until condition
do
command
done
示例:
a=0
until [ ! a -lt 10 ]
do
echo a
a=$[a+1]
done
命令形式: ./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
service cron status
功能: 主要负责定时任务的管理
格式: 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 用户名
【提示】如果不指定用户名,则表示当前登录的用户
<分钟> <小时> <日> <月份> <星期> <命令>
特殊字符:
* 代表所有可能的值,如:分钟上表示每分钟
, 指定一个列表范围,如分钟上 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
添加当前用户的定时任务
ls /var/spool/cron/crontabs -l
重要说明:
1. cron是分用户的,即每个用户都有自己的cron文件(/var/spool/cron/crontabs/目录下,以用户名命名的文件)
2. 系统级别的命令(如/sbin/reboot等)应该用root身份来编辑和载入crontab(/etc/crontab 文件)
3. 普通用户的crontab会自动执行, 普通用户的crontab有系统级别命令,则不会执行
命名只能使用英文字母,数字和下划线,首个字符不能以数字开头
中间不能有空格,可以使用下划线“_”
不能使用标点符号
不能使用bash里的关键字, help命令查看保留关键字
定义变量名:name = 'disen'
使用变量名: echo $name
只读变量(作为常量): readonly 变量名
删除变量: unset 变量名
.bash_profile
当前用户的环境变量~/.bashrc
环境变量文件单引号
单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的
单引号字串中不能出现单引号(对单引号使用转义符后也不行)
双引号
双引号里可以有变量
双引号里可以出现转义字符
拼接
"hi, "$name"!"
、 "hi, $name!"
字符串长度, # 符号
, 如${#name}
提取, 如${name:1:5}
从name变量的第2个位置开始提取5个字符
Docker是镜像容器的管理工具, 实现虚拟化技术 替代类传于VMware或VirtualBox虑拟环境。Docker基于硬件虚拟化, 实现多个独立的容器(微型操作系统)在同等的硬件资源下和基础的操作系统(Ubuntu)上构建的虚拟环境。
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
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
类似于类和对象的关系, 镜像对应是类, 容器对应是对象, 一个镜像可以运行多次成为容器,同一个类可以创建多个实现的思想一样。当然,在容器中安装或修改相关的内容之后,可以将容器保存为镜像。
一般开发人员,通过容器配置项目开发环境,等开发完成后,将容器保存为镜像,并向本地仓库上传,其他相关人员即可以从本地仓库下载并运行。
重要的三个方面:
docker search mysql
根据搜索的镜像列表,可以看到完整的镜像名称,以便下载。
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
docker images
要求: 运行的容器没有一个属于待删除镜像的容器。
docker rmi [-f] 镜像全名[:tag]或ID
-f 强制删除镜像(镜像存在运行中的容器时)
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 指定容器的环境变量,容器的程序在运行时需要的变量。
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
下载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
。
因为容器是Docker守护进程(后台进程)的子进程,执行以下命令可以查看正在运行的容器信息:
docker ps
如果想查看最近一个运行的容器:
docker ps -l
如果想查看所有的容器,包含未运行的:
docker ps -a
docker stop 容器名或容器ID
docker start 容器名或容器ID
docker restart 容器名或容器ID
当容器启动后,可以进入到容器中,执行Linux相关的命令,完成软件安装或项目部署。
docker exec -it 容器名或ID bash
如果只想单独执行某一项命令:
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)。
通过docker cp命令将本地文件复制到容器中,或者容器中文件复制到本地来。
docker cp 容器名:源文件路径 本地目标路径
docker cp 本地源路径 容器名:目标文件路径
相关示例:
docker cp /root/2.sql db0:/
将本地的2.sql文件复制到 db0容器的/
目录下。
docker cp db0:/etc/mysql .
将容器db0中的/etc/mysql目录复制到本地的当前目录中。
当容器启动后,可以查看容器中服务的运行日志:
docker logs 容器名或ID
docker rm [-f] 容器名或ID
-f 如果容器运行的状态下,强制停止容器并删除。
可以下载的源如下: 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
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客户端。
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_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
mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY '密码';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '口令' WITH GRANT OPTION;
FLUSH PRIVILEGES;
编辑 /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)
配置docker 的加速器:
默认镜像是从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
将镜像启动之后, docker daemon进程将会创建一个容器子进程。
镜像和容器的关系,类传于类和实例对象的关系。镜像可以运行多次,即创建多个容器。容器也可以保存为镜像。
docker run -itd --name db0 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -v /root/sql:/usr/src/sql mysql
【注意】-v 是将本地的目录和容器的目录进行同步,这样方便调试sql脚本。
进入mysql容器中(db0):
root@tserver # docker exec -it db0 bash
root@facakaai # mysql -uroot -proot
mysql>
由于容器下拉的是MySQL8, 而它的加密规则同Mysql5不同,因此在使用pymysql连接数据库时,会存在密码不正确问题,以下是解决办法:
> use mysql
> alter user 'root'@'%' identified by 'root' password expire never;
> alter user 'root'@'%' identified with mysql_native_password by 'root';
安装 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。
数据库: 永久性存储数据的仓库,按数据的媒体类型分关系型(结构化)和非关系型(二进制或字节码)。
关系型数据库: 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个字节, 中文占三个字节
官方文档: https://dev.mysql.com/doc/refman/8.0/en/
安装完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
语句。
在MySQL中,存在不同的数据引擎,适应不同的开发环境(项目要求)。
mysql> show engines;
支持数据库引擎: MEMORY(内存)、InnoDB(默认的,支持事务、行级锁、外键)、MyISAM、CSV等
创建数据库:
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中作法:
删除数据库:
drop database ;
drop database [if exists] ; -- 如果db_name存在,则删除,不存在则继续。
修改数据库:
-- 设置数据库的默认字符集
alter database <db_name> default character set utf8;
-- 查看创建数据库的语句
show create database db_name;
打开数据库:
use db_name
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)
);
【注意】表名的字母是区分大小写的。
查看数据库中存在哪些表:
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)
可以修改表的结构,包含字段、表名、约束等。
-- 修改表名
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(数据库控制语言)。
drop table [if exists] <表名>;
【注意】表删除之后,表中的数据也会被删除。
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';
-- 新增数据时,可以指定哪些字段及它的数据
-- 默认情况下,为所有的字段指定数据, 字段顺序按创建表时的字段顺序。
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);
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='技术部';
delete from 表名
[where 条件表达式]
【提示】不加where条件的delete语句,表示清空表。
【事务相关的语句】
【建议】只要是DML语句(修改、插入和删除),都要开启事务。
-- 删除务商部
begin;
delete from department where name='商务部';
select * from department;
-- commit; -- 最终删除
rollback; -- 取消删除
外键约束的级联删除
-- 增加员工的所属部门字段,默认值为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;
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;
【注意】
-- 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选择字段必须保持一致。
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;
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
like是用于对文本类型的数据进行模糊查询,可以使用两个通配符:
`_` 任意一个字符
`%` 任意多个字符
-- 查询李姓员工所有的信息
select *
from person
where name like '李%';
-- 查询手机号第三位是0的所有员工信息
select *
from person
where phone like '__0%';
子查询: 在查询语句包含内部的查询语句,内部的查询语句称之为子查询, 子查询一般使用小括号。
使用场景:
-- 查询最高薪资的员工的所有信息
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;
事务: 处理某一件事件或功能的过程,在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;
配合分组语句,针对特定的字段数据进行分组聚合计算相关的数据,如最大、最小、平均、总和、数量等。
【注意】聚合函数中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);
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;
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 |
+----+-----------+--------+---------+
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);
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;
password() -- MySQL5存在的, MySQL 8不存在
md5(str) 将str转成md5编码字符,长度32位
sha1() 将str转成sha1编码字符,长度40位
uuid() 获取uuid的字符串, 字符串中带有`-`符号,可以使用replace()函数将其去除。
-- 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;
作用:
- 可以作为条件使用
- 可以创建视图或表
- 批量插入数据
-- 将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;
视图: 将复杂的查询语句进行封装,对视图查询,即是将视图对应的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;
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()
类的类型:
- 数据类、 实体类(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())
# 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()