本文为本人在 实验楼 学习网站摘下的部分学习笔记
目录
12.3 grep 命令,在文本中或 stdin 中查找匹配字符串
15、正则表达式基础(Regular Expression)
1、基础操作
快捷键 | 用处 |
Ctrl+s | 暂停当前程序,暂停后按下任意键恢复运行 |
Ctrl+c | 强制终止程序 |
Ctrl+d | 键盘输入结束或退出终端 |
Ctrl+z | 将当前程序放到后台运行,恢复到前台为命令fg |
cd、ls、pwd、ll | clear |
Ctrl+a | 将光标移至输入行头,相当于Home键 |
Ctrl+e | 将光标移至输入行末,相当于End键 |
Ctrl+k | 删除从光标所在位置到行末 |
Shift+PgUp | 将终端显示向上滚动 |
touch | 创建文件 |
man +(区段号)+命令名 | 查看手册 |
命令名+ --help | 查看手册 |
Shell 常用通配符:
字符 | 含义 |
* | 匹配 0 或多个字符 |
? | 匹配任意一个字符 |
[list] | 匹配 list 中的任意单一字符 |
[^list] | 匹配 除list 中的任意单一字符以外的字符 |
[c1-c2] | 匹配 c1-c2 中的任意单一字符 如:[0-9] [a-z] |
{string1,string2,...} | 匹配 string1 或 string2 (或更多)其一字符串 |
{c1..c2} | 匹配 c1-c2 中全部字符 如{1..10} |
banner例子:
$ sudo apt-get update $ sudo apt-get install sysvbanner
$ banner shiyanlou
或者你觉得这字体不好看,那么你还可以使用默认已经安装的一个命令printerbanner:
$ printerbanner -w 50 A
-w参数指定打印宽度,因为我们的环境在屏幕中显示比较小,必须要加上宽度限制。
还有两个类似的命令toilet,figlet
2、用户管理
Linux 是一个可以实现多用户登录的操作系统,比如“李雷”和“韩梅梅”都可以同时登录同一台主机,他们共享一些主机的资源,但他们也分别有自己的用户空间,用于存放各自的文件。但实际上他们的文件都是放在同一个物理磁盘上的甚至同一个逻辑分区或者目录里,但是由于 Linux 的 用户管理 和 权限机制,不同用户不可以轻易地查看、修改彼此的文件。
2.1 查看用户
请打开终端,输入命令:
$ who am i或者 $ who mom likes
输出的第一列表示打开当前伪终端的用户的用户名
(要查看当前登录用户的用户名,去掉空格直接使用 whoami 即可)
第二列的 pts/0 中 pts 表示伪终端,所谓伪是相对于 /dev/tty 设备而言的
“真终端”:七个使用 [Ctrl]+[Alt]+[F1]~[F7] 进行切换的 /dev/tty 设备么
伪终端就是当你在图形用户界面使用 /dev/tty7 时每打开一个终端就会产生一个伪终端, pts/0 后面那个数字就表示打开的伪终端序号,再打开一个终端,然后在里面输入 who am i ,看第二变 成 pts/1 了,第三列则表示当前伪终端的启动时间。
who 命令其它常用参数
参数 | 说明 |
-a | 打印能打印的全部 |
-d | 打印死掉的进程 |
-m | 同am i,mom likes |
-q | 打印当前登录用户数及用户名 |
-u | 打印当前登录用户登录信息 |
-r | 打印运行等级 |
2.2 创建用户
在 Linux 系统里, root 账户拥有整个系统至高无上的权利,比如 新建/添加 用户。
root 权限,系统权限的一种,与 SYSTEM 权限可以理解成一个概念,但高于 Administrator 权限,root 是 Linux 和 UNIX 系统中的超级管理员用户帐户,该帐户拥有整个系统至高无上的权力,所有对象他都可以操作,所以很多黑客在入侵系统的时候,都要把权限提升到 root 权限,用 Windows 的方法理解也就是将自己的非法帐户添加到 Administrators 用户组。更比如安卓操作系统中(基于 Linux 内核)获得 root 权限之后就意味着已经获得了手机的最高权限,这时候你可以对手机中的任何文件(包括系统文件)执行所有增、删、改、查的操作。
使用 sudo命令有两个大前提,一是知道当前登录用户的密码,二是当前用户必须在 sudo用户组。
su,su- 与 sudo
su - <user> 命令也是切换用户,同时环境变量也会跟着改变成目标用户的环境变量。
su <user> 可以切换到用户 user,执行时需要输入目标用户的密码
sudo <cmd> 可以特权级别运行 cmd 命令,需要当前用户属于 sudo 组,且需要输入当前用户的密码。
现在我们新建一个叫 lilei 的用户:
$ sudo adduser lilei
shiyanlou 用户密码通过 sudo passwd shiyanlou命令进行设置。然后是给 lilei 用户设置密码,后面的选项的一些内容你可以选择直接回车使用默认值:
这个命令不但可以添加用户到系统,同时也会默认为新用户创建 home 目录:
$ ls /home
现在你已经创建好一个用户,并且你可以使用你创建的用户登录了,使用如下命令切换登录用户:
$ su -l lilei
2.3 用户组
在 Linux 里面每个用户都有一个归属(用户组),用户组简单地理解就是一组用户的集合,它们共享一些资源和权限,同时拥有私有资源,就跟家的形式差不多,你的兄弟姐妹(不同的用户)属于同一个家(用户组),你们可以共同拥有这个家(共享资源),爸妈对待你们都一样(共享权限),你偶尔写写日记,其他人未经允许不能查看(私有资源和权限)。当然一个用户是可以属于多个用户组的,正如你既属于家庭,又属于学校或公司。
在 Linux 里面如何知道自己属于哪些用户组呢?
方法一:使用 groups 命令
$ groups shiyanlou
其中冒号之前表示用户,后面表示该用户所属的用户组。
每次新建用户如果不指定用户组的话,默认会自动创建一个与用户名相同的用户组(相当于家长)。
默认情况下在 sudo 用户组里的可以使用 sudo 命令获得 root 权限。
为什么这里没有显示在 sudo 用户组里呢?可以查看下 /etc/sudoers.d/shiyanlou 文件,我们在 /etc/sudoers.d 目录下创建了这个文件,从而给 shiyanlou 用户赋予了 sudo 权限:
方法二:查看 /etc/group 文件
$ cat /etc/group | sort
这里 cat 命令用于读取指定文件的内容并打印到终端输出。| sort 表示将读取的文本进行一个字典排序再输出,然后你将看到如下一堆输出,你可以在最下面看到 shiyanlou 的用户组信息:
没找到,没关系,你可以使用命令过滤掉一些你不想看到的结果:
$ cat /etc/group | grep -E "shiyanlou"
/etc/group 文件格式说明
/etc/group 的内容包括用户组(Group)、用户组口令、GID 及该用户组所包含的用户(User),每个用户组一条记录。格式如下:
group_name:password:GID:user_list
你看到上面的 password 字段为一个 x 并不是说密码就是它,只是表示密码不可见而已。
这里需要注意,如果用户主用户组,即用户的 GID 等于用户组的 GID,那么最后一个字段 user_list 就是空的,比如lilei 用户,在 /etc/group 中的 lilei 用户组后面是不会显示的。
将其它用户加入 sudo 用户组
默认情况下新创建的用户是不具有 root 权限的,也不在 sudo 用户组,可以让其加入 sudo 用户组从而获取 root 权限:
# 注意 Linux 上输入密码是不会显示的 $ su -l lilei $ sudo ls
会提示 lilei 不在 sudoers 文件中,意思就是 lilei 不在 sudo 用户组中,至于 sudoers 文件(/etc/sudoers)你现在最好不要动它,操作不慎会导致比较麻烦的后果。
使用 usermod 命令可以为用户添加用户组,同样使用该命令你必需有 root 权限,你可以直接使用 root 用户为其它用户添加用户组,或者用其它已经在 sudo 用户组的用户使用 sudo 命令获取权限来执行该命令。
这里我用 shiyanlou 用户执行 sudo 命令将 lilei 添加到 sudo 用户组,让它也可以使用 sudo 命令获得 root 权限: $ sudo usermod -G sudo lilei $ groups lilei
然后你再切换回 lilei 用户,现在就可以使用 sudo 获取 root 权限了。
2.4 删除用户
删除用户是很简单的事:
$ sudo deluser lilei --remove-home
3、文件权限管理
文件权限就是文件的访问控制权限,即哪些用户和组群可以访问文件以及可以执行什么样的操作。
Unix/Linux系统是一个典型的多用户系统,不同的用户处于不同的地位,对文件和目录有不同的访问权限。
为了保护系统的安全性,Unix/Linux系统除了对用户权限作了严格的界定外,还在用户身份认证、访问控制、传输安全、文件读写权限等方面作了周密的控制。
在 Unix/Linux中的每一个文件或目录都包含有访问权限,这些访问权限决定了谁能访问和如何访问这些文件和目录。
3.1 查看文件权限
使用较长格式列出文件:
$ ls -l
你可能除了知道最后面那一项是文件名之外,其它项就不太清楚了,那么到底是什么意思呢:
可能你还是不太明白,比如第一项文件类型和权限那一堆东西具体指什么,链接又是什么,何为最后修改时间,下面一一道来:
- 文件类型
关于文件类型,这里有一点你必需时刻牢记 Linux 里面一切皆文件,正因为这一点才有了设备文件( /dev目录下有各种设备文件,大都跟具体的硬件设备相关)这一说。
pipe 管道,这个东西很重要,我们以后将会讨论到,这里你先知道有它的存在即可。软链接文件:链接文件是分为两种的,另一种当然是“硬链接”(硬链接不常用,具体内容不作为本课程讨论重点,而软链接等同于 Windows 上的快捷方式,你记住这一点就够了)。
- 文件权限
读权限,表示你可以使用 cat <file name> 之类的命令来读取某个文件的内容;
写权限,表示你可以编辑和修改某个文件;
执行权限,通常指可以运行的二进制程序文件或者脚本文件。
你需要注意的一点是,一个目录同时具有读权限和执行权限才可以打开并查看内部文件
一个目录要有写权限才允许在其中创建其它文件,这是因为目录文件实际保存着该目录里面的文件的列表等信息。
所有者权限,这一点相信你应该明白了,至于所属用户组权限,是指你所在的用户组中的所有其它用户对于该文件的权限,比如,你有一个 iPad,那么这个用户组权限就决定了你的兄弟姐妹有没有权限使用它破坏它和占有它。
- 文件大小
以 inode 结点大小为单位来表示的文件大小,你可以给 ls 加上 -lh 参数来更直观的查看文件的大小。
明白了文件权限的一些概念,我们顺带补充一下关于 ls 命令的一些其它常用的用法:
- 显示除了 .(当前目录)和 ..(上一级目录)之外的所有文件,包括隐藏文件(Linux 下以 . 开头的文件为隐藏文件)。使用 ls -a 命令查看隐藏文件
$ ls -A
当然,你可以同时使用 -A 和 -l 参数:
$ ls -Al
查看某一个目录的完整属性,而不是显示目录里面的文件属性:
$ ls -dl <目录名>
显示所有文件大小,并以普通人类能看懂的方式呈现:
$ ls -AsSh
其中小 s 为显示文件大小,大 S 为按文件大小排序,若需要知道如何按其它方式排序,请使用“man”命令查询。
3.2 变更文件所有者
假设目前是 lilei 用户登录,新建一个文件,命名为 “ iphone6 ”:
# 注意当前的用户必须是 lilei # 如果是 shiyanlou 用户需要切换到 lilei(如果之前已经删除需要重新创建下) $ su lilei $ cd /home/lilei $ touch iphone6
可见文件所有者是 lilei :
现在,换回到 shiyanlou 用户身份,使用以下命令变更文件所有者为 shiyanlou :
# 需要切换到 shiyanlou 用户执行以下操作 $ cd /home/lilei $ ls iphone6 $ sudo chown shiyanlou iphone6
现在查看,发现 文件所有者成功修改为 shiyanlou :
3.3 修改文件权限
如果你有一个自己的文件不想被其他用户读、写、执行,那么就需要对文件的权限做修改,这里有两种方式:
- 方式一:二进制数字表示
每个文件的三组权限(拥有者,所属用户组,其他用户,记住这个顺序是一定的)对应一个 " rwx ",也就是一个 “ 7 ” ,所以如果我要将文件“ iphone6 ”的权限改为只有我自己可以用那么就这样:
为了演示,我先在文件里加点内容:
$ echo "echo \"hello shiyanlou\"" > iphone6
然后修改权限:
$ chmod 600 iphone6
现在,其他用户已经不能读这个“ iphone6 ”文件了:
- 方式二:加减赋值操作
完成上述相同的效果,你可以:
$ chmod go-rw iphone6
g、o 还有 u 分别表示 group、others 和 user,+ 和 -分别表示增加和去掉相应的权限。
adduser 和 useradd 的区别是什么?
答:useradd 只创建用户,创建完了用 passwd lilei 去设置新用户的密码。adduser 会创建用户,创建目录,创建密码(提示你设置),做这一系列的操作。其实 useradd、userdel 这类操作更像是一种命令,执行完了就返回。而 adduser 更像是一种程序,需要你输入、确定等一系列操作。
4、Linux目录结构
Linux 是以树形目录结构的形式来构建整个系统的,可以理解为树形目录是一个用户可操作系统的骨架。虽然本质上无论是目录结构还是操作系统内核都是存储在磁盘上的,但从逻辑上来说 Linux 的磁盘是“挂在”(挂载在)目录上的,每一个目录不仅能使用本地磁盘分区的文件系统,也可以使用网络上的文件系统。举例来说,可以利用网络文件系统(Network File System,NFS)服务器载入某特定目录等。
4.1 FHS标准
FHS(英文:Filesystem Hierarchy Standard 中文:文件系统层次结构标准),多数 Linux 版本采用这种文件组织形式,FHS 定义了系统中每个区域的用途、所需要的最小构成的文件和目录同时还给出了例外处理与矛盾处理。
FHS 定义了两层规范:
第一层是, / 下面的各个目录应该要放什么文件数据,例如 /etc 应该放置设置文件,/bin 与 /sbin 则应该放置可执行文件等等。
第二层则是针对 /usr 及 /var 这两个目录的子目录来定义。例如 /var/log 放置系统日志文件,/usr/share放置共享数据等等。
FHS 依据文件系统使用的频繁与否以及是否允许用户随意改动(注意,不是不能,学习过程中,不要怕这些),将目录定义为四种交互作用的形态,如下表所示:
4.2 目录路径
- 表示上一次所在目录,~ 通常表示当前用户的 home 目录。
绝对路径
关于绝对路径,简单地说就是以根" / "目录为起点的完整路径
相对路径
相对路径,也就是相对于你当前的目录的路径,相对路径是以当前目录 . 为起点,以你所要到的目录为终点。
提示:在进行目录切换的过程中请多使用 Tab 键自动补全,可避免输入错误,连续按两次 Tab 可以显示全部候选结果。
5、Linux文件的基本操作
5.1 新建目录
新建目录
使用 mkdir(make directories)命令可以创建一个空目录,也可同时指定创建目录的权限属性。
创建名为“ mydir ”的空目录:
$ mkdir mydir
使用 -p 参数,同时创建父目录(如果不存在该父目录),如下我们同时创建一个多级目录(这在安装软件、配置安装路径时非常有用):
$ mkdir -p father/son/grandson
5.2 复制
复制文件:使用 cp(copy)命令复制一个文件到指定目录。
将之前创建的“ test ”文件复制到“ /home/shiyanlou/father/son/grandson ”目录中:
$ cp test father/son/grandson
复制目录
如果直接使用 cp 命令复制一个目录的话,会出现如下错误:
要成功复制目录需要加上 -r 或者 -R 参数,表示递归复制,就是说有点“株连九族”的意思: $ cp -r father family
5.3 删除
删除文件:使用 rm(remove files or directories)命令删除一个文件:
$ rm test
有时候你会遇到想要删除一些为只读权限的文件,直接使用 rm 删除会显示一个提示,如下:
你如果想忽略这提示,直接删除文件,可以使用 -f 参数强制删除:
$ rm -f test
删除目录
跟复制目录一样,要删除一个目录,也需要加上 -r 或 -R 参数:
$ rm -r family
5.4 移动文件与文件重命名
移动文件
使用 mv(move or rename files)命令移动文件(剪切)。将文件“ file1 ”移动到 Documents 目录:
mv 源目录文件 目的目录:
$ mkdir Documents $ touch file1 $ mv file1 Documents
重命名文件
将文件“ file1 ”重命名为“ myfile ”:
mv 旧的文件名 新的文件名:
$ mv file1 myfile
批量重命名 # 使用通配符批量创建 5 个文件: $ touch file{1..5}.txt # 批量将这 5 个后缀为 .txt 的文本文件重命名为以 .c 为后缀的文件: $ rename 's/\.txt/\.c/' *.txt # 批量将这 5 个文件,文件名和后缀改为大写: $ rename 'y/a-z/A-Z/' *.c
5.5 查看文件
使用 cat,tac 和 nl 命令查看文件
前两个命令都是用来打印文件内容到标准输出(终端),其中 cat 为正序显示,tac 为倒序显示。
标准输入输出:当我们执行一个 shell 命令行时通常会自动打开三个标准文件,即标准输入文件(stdin),默认对应终端的键盘、标准输出文件(stdout)和标准错误输出文件(stderr),后两个文件都对应被重定向到终端的屏幕,以便我们能直接看到输出内容。进程将从标准输入文件中得到输入数据,将正常输出数据输出到标准输出文件,而将错误信息送到标准错误文件中。
比如我们要查看之前从 /etc 目录下拷贝来的 passwd文件:
可以加上 -n 参数显示行号:
$ cat -n passwd
# 查看 /etc/protocols 中的不可见字符,可以看到很多 ^I ,这其实就是 Tab 转义成可见字符的符号 $ cat -A /etc/protocols
nl 命令,添加行号并打印,这是个比 cat -n 更专业的行号打印命令。
这里简单列举它的常用的几个参数:
-b : 指定添加行号的方式,主要有两种: -b a:表示无论是否为空行,同样列出行号("cat -n"就是这种方式) -b t:只列出非空行的编号并列出(默认为这种方式) -n : 设置行号的样式,主要有三种: -n ln:在行号字段最左端显示 -n rn:在行号字段最右边显示,且不加 0 -n rz:在行号字段最右边显示,且加 0 -w : 行号字段占用的位数(默认为 6 位)
使用 more 和 less 命令分页查看文件
如果说上面的 cat 是用来快速查看一个文件的内容的,那么这个 more 和 less 就是天生用来"阅读"一个文件的内容的,比如说 man 手册内部就是使用的 less来显示内容。其中 more 命令比较简单,只能向一个方向滚动,而 less 为基于 more 和 vi (一个强大的编辑器,我们有单独的课程来让你学习)开发,功能更强大。less 的使用基本和 more 一致,具体使用请查看 man 手册,这里只介绍 more 命令的使用。
使用 more 命令打开 passwd 文件:
$ more passwd
打开后默认只显示一屏内容,终端底部显示当前阅读的进度。可以使用 Enter 键向下滚动一行,使用 Space键向下滚动一屏,按下 h 显示帮助,q 退出。
使用 head 和 tail 命令查看文件
只查看文件的头几行(默认为 10 行,不足 10 行则显示全部)和尾几行。
$ tail /etc/passwd
甚至更直接的只看一行, 加上 -n 参数,后面紧跟行数:
$ tail -n 1 /etc/passwd
关于 tail 命令,不得不提的还有它一个很牛的参数 -f,这个参数可以实现不停地读取某个文件的内容并显示。这可以让我们动态查看日志,达到实时监视的目的。
5.6 查看文件类型
在 Linux 中文件的类型不是根据文件后缀来判断的,我们通常使用 file 命令查看文件的类型:
$ file /bin/ls
说明这是一个可执行文件,运行在 64 位平台,并使用了动态链接文件(共享库)
今天就教你怎么召唤一双眼睛出来监督你:
$ xeyes
你可以使用如下命令将它放到后台运行:
$ nohup xeyes &
6、环境变量
使用 declare 命令创建一个变量名为 tmp 的变量:
$ declare tmp
其实也可以不用 declare 预声明一个变量,直接即用即创建,这里只是告诉你 declare 的作用,这在创建其它指定类型的变量(如数组)时会用到。
$ tmp=shiyanlou
$ echo $tmp
注意:并不是任何形式的变量名都是可用的,变量名只能是英文字母、数字或者下划线,且不能以数字作为开头。
6.1 环境变量
环境变量的作用域比自定义变量的要大,如 Shell 的环境变量作用于自身和它的子进程。在所有的 UNIX 和类 UNIX 系统中,每个进程都有其各自的环境变量设置,且默认情况下,当一个进程被创建时,除了创建过程中明确指定的话,它将继承其父进程的绝大部分环境设置。Shell 程序也作为一个进程运行在操作系统之上,而我们在 Shell 中运行的大部分命令都将以 Shell 的子进程的方式运行。
通常我们会涉及到的变量类型有三种:
- 当前 Shell 进程私有用户自定义变量,如上面我们创建的 tmp 变量,只在当前 Shell 中有效。
- Shell 本身内建的变量。
- 从自定义变量导出的环境变量。
也有三个与上述三种环境变量相关的命令:set,env,export。这三个命令很相似,都是用于打印环境变量信息,区别在于涉及的变量范围不同。详见下表:
命 令 | 说 明 |
set | 显示当前 Shell 所有变量,包括其内建环境变量(与 Shell 外观等相关),用户自定义变量及导出的环境变量。 |
env | 显示与当前用户相关的环境变量,还可以让命令在指定环境中运行。 |
export | 显示从 Shell 中导出成环境变量的变量,也能通过它将自定义变量导出为环境变量。 |
你可以更直观的使用 vimdiff 工具比较一下它们之间的差别:
$ temp=shiyanlou $ export temp_env=shiyanlou $ env|sort>env.txt $ export|sort>export.txt $ set|sort>set.txt
上述操作将命令输出通过管道 | 使用 sort 命令排序,再重定向到对象文本文件中。
$ vimdiff env.txt export.txt set.txt
使用 vimdiff 工具比较导出的几个文件的内容:
关于哪些变量是环境变量,可以简单地理解成在当前进程的子进程有效则为环境变量
我们这里用 export 命令来体会一下,先在 Shell 中设置一个变量 temp=shiyanlou,然后再新创建一个子 Shell 查看 temp 变量的值:
注意:为了与普通变量区分,通常我们习惯将环境变量名设为大写。
按变量的生存周期来划分,Linux 变量可分为两类:
- 永久的:需要修改配置文件,变量永久生效;
- 临时的:使用 export 命令行声明即可,变量在关闭 shell 时失效。
这里介绍两个重要文件 /etc/bashrc(有的 Linux 没有这个文件) 和 /etc/profile ,它们分别存放的是 shell 变量和环境变量。还有要注意区别的是每个用户目录下的一个隐藏文件:
.profile 可以用 ls -a 查看
这个 .profile 只对当前用户永久生效。而写在 /etc/profile 里面的是对所有用户永久生效,所以如果想要添加一个永久生效的环境变量,只需要打开 /etc/profile,在最后加上你想添加的环境变量就好啦
6.2 命令的查找路径与顺序
你可能很早之前就有疑问,我们在 Shell 中输入一个命令,Shell 是怎么知道去哪找到这个命令然后执行的呢?这是通过环境变量 PATH 来进行搜索的,熟悉 Windows 的用户可能知道 Windows 中的也是有这么一个 PATH 环境变量。这个 PATH 里面就保存了 Shell 中执行的命令的搜索路径。
查看 PATH 环境变量的内容
创建一个 Shell 脚本文件: $ touch hello_shell.sh $ gedit hello_shell.sh
在脚本中添加如下内容,保存并退出(注意不要省掉第一行,这不是注释,论坛有用户反映有语法错误,就是因为没有了第一行):
#!/bin/bash for ((i=0; i<10; i++));do echo "hello shell" done exit 0
为文件添加可执行权限:
$ chmod 755 hello_shell.sh
执行脚本:
$ cd /home/shiyanlou $ ./hello_shell.sh
创建一个 C 语言“ hello world ”程序: $ gedit hello_world.c
#include <stdio.h> int main(void) { printf("hello world!\n"); return 0; }
保存后使用 gcc 生成可执行文件:
$ gcc -o hello_world hello_world.c
gcc 生成二进制文件默认具有可执行权限,不需要修改
$ ./hello_world
6.3 添加自定义路径到“ PATH ”环境变量
在前面我们应该注意到 PATH 里面的路径是以 : 作为分割符的,所以我们可以这样添加自定义路径:
$ PATH=$PATH:/home/shiyanlou/mybin
注意这里一定要使用绝对路径。
现在你就可以在任意目录执行那两个命令了(注意需要去掉前面的 ./)。你可能会意识到这样还并没有很好的解决问题,因为我给 PATH 环境变量追加了一个路径,它也只是在当前 Shell 有效,我一旦退出终端,再打开就会发现又失效了。
有没有方法让添加的环境变量全局有效?或者每次启动 Shell 时自动执行上面添加自定义路径到 PATH 的命令?下面我们就来说说后一种方式——让它自动执行。
在每个用户的 home 目录中有一个 Shell 每次启动时会默认执行一个配置脚本,以初始化环境,包括添加一些用户自定义环境变量等等。zsh 的配置文件是 .zshrc,相应 Bash 的配置文件为 .bashrc 。它们在 etc 下还都有一个或多个全局的配置文件,不过我们一般只修改用户目录下的配置文件。
我们可以简单地使用下面命令直接添加内容到 .zshrc中:
$ echo "PATH=$PATH:/home/shiyanlou/mybin" >> .zshrc
上述命令中 >> 表示将标准输出以追加的方式重定向到一个文件中,注意前面用到的 > 是以覆盖的方式重定向到一个文件中,使用的时候一定要注意分辨。在指定文件不存在的情况下都会创建新的文件
6.4 修改和删除已有变量
变量修改
变量的修改有以下几种方式:
变量设置方式 | 说明 |
${变量名#匹配字串} | 从头向后开始匹配,删除符合匹配字串的最短数据 |
${变量名##匹配字串} | 从头向后开始匹配,删除符合匹配字串的最长数据 |
${变量名%匹配字串} | 从尾向前开始匹配,删除符合匹配字串的最短数据 |
${变量名%%匹配字串} | 从尾向前开始匹配,删除符合匹配字串的最长数据 |
${变量名/旧的字串/新的字串} | 将符合旧字串的第一个字串替换为新的字串 |
${变量名//旧的字串/新的字串} | 将符合旧字串的全部字串替换为新的字串 |
比如要修改我们前面添加到 PATH 的环境变量。为了避免操作失误导致命令找不到,我们先将 PATH 赋值给一个新的自定义变量 path:
$ path=$PATH $ echo $path $ path=${path%/home/shiyanlou/mybin} # 或使用通配符,*表示任意多个任意字符 $ path=${path%*/mybin}
temp=ttyy
temp=$temp:11
echo $temp
>>ttyy:11
变量删除
可以使用 unset 命令删除一个环境变量:
$ unset temp
6.5 如何让环境变量立即生效
前面我们在 Shell 中修改了一个配置脚本文件之后(比如 zsh 的配置文件 home 目录下的 .zshrc),每次都要退出终端重新打开甚至重启主机之后其才能生效,很是麻烦,我们可以使用 source 命令来让其立即生效,如: $ source .zshrc
source 命令还有一个别名就是 .,上面的命令如果替换成 . 的方式就该是:
$ . ./.zshrc
在使用.的时候,需要注意与表示当前路径的那个点区分开。
注意第一个点后面有一个空格,而且后面的文件必须指定完整的绝对或相对路径名,source 则不需要。
6.6 搜索文件
与搜索相关的命令常用的有 whereis,locate, which,find 。
- whereis 简单快速
$ whereis who $ whereis find
你会看到 whereis find 找到了三个路径,两个可执行文件路径和一个 man 在线帮助文件所在路径,这个搜索很快,因为它并没有从硬盘中依次查找,而是直接从数据库中查询。whereis 只能搜索二进制文件(-b),man 帮助文件(-m)和源代码文件(-s)。如果想要获得更全面的搜索结果可以使用 locate 命令。
- locate 快而全
通过“ /var/lib/mlocate/mlocate.db ”数据库查找,不过这个数据库也不是实时更新的,系统会使用定时任务每天自动执行 updatedb 命令更新一次,所以有时候你刚添加的文件,它可能会找不到,需要手动执行一次 updatedb 命令(在我们的环境中必须先执行一次该命令)。它可以用来查找指定目录下的不同文件类型,如查找 /etc 下所有以 sh 开头的文件:
$ sudo apt-get update $ sudo apt-get install locate $ locate /etc/sh
注意,它不只是在 /bin 目录下查找,还会自动递归子目录进行查找。
查找 /usr/share/ 下所有 jpg 文件:
$ locate /usr/share/\*.jpg
注意要添加 * 号前面的反斜杠转义,否则会无法找到。
如果想只统计数目可以加上 -c 参数,-i 参数可以忽略大小写进行查找,whereis 的 -b、-m、-s 同样可以使用。
- which 小而精
which 本身是 Shell 内建的一个命令,我们通常使用 which 来确定是否安装了某个指定的软件,因为它只从 PATH 环境变量指定的路径中去搜索命令:
$ which man
- find 精而细
find 应该是这几个命令中最强大的了,它不但可以通过文件类型、文件名进行查找而且可以根据文件的属性(如文件的时间戳,文件的权限等)进行搜索。
这条命令表示去 /etc/ 目录下面 ,搜索名字叫做 interfaces 的文件或者目录。这是 find 命令最常见的格式,千万记住 find 的第一个参数是要搜索的地方:
$ sudo find /etc/ -name interfaces
注意 find 命令的路径是作为第一个参数的, 基本命令格式为 find [path] [option] [action] 。
与时间相关的命令参数:
参数 | 说明 |
-atime | 最后访问时间 |
-ctime | 最后修改文件内容的时间 |
-mtime | 最后修改文件属性的时间 |
下面以 -mtime 参数举例:
- -mtime n:n 为数字,表示为在 n 天之前的“一天之内”修改过的文件
- -mtime +n:列出在 n 天之前(不包含 n 天本身)被修改过的文件
- -mtime -n:列出在 n 天之内(包含 n 天本身)被修改过的文件
- -newer file:file 为一个已存在的文件,列出比 file 还要新的文件名
列出 home 目录中,当天(24 小时之内)有改动的文件:
$ find ~ -mtime 0
列出用户家目录下比 Code 文件夹新的文件:
$ find ~ -newer /home/shiyanlou/Code
数字雨
$ sudo apt-get update; sudo apt-get install cmatrix
装好之后先不要急着执行,为了看到更好的效果,我们需要先修改终端的主题配色,在终端上面的【选项】中修改,修改为黑底绿字:
然后按下 F11 可以全屏显示。
还可以改变代码的颜色:
cmatrix -C red
7、文件打包与解压缩
在 Windows 上最常见的不外乎这两种 *.zip,*.7z 后缀的压缩文件。而在 Linux 上面常见的格式除了以上两种外,还有 .rar,*.gz,*.xz,*.bz2,*.tar,*.tar.gz,*.tar.xz,*.tar.bz2,简单介绍如下:
文件后缀名 | 说明 |
*.zip | zip 程序打包压缩的文件 |
*.rar | rar 程序压缩的文件 |
*.7z | 7zip 程序压缩的文件 |
*.tar | tar 程序打包,未压缩的文件 |
*.gz | gzip 程序(GNU zip)压缩的文件 |
*.xz | xz 程序压缩的文件 |
*.bz2 | bzip2 程序压缩的文件 |
*.tar.gz | tar 打包,gzip 程序压缩的文件 |
*.tar.xz | tar 打包,xz 程序压缩的文件 |
*tar.bz2 | tar 打包,bzip2 程序压缩的文件 |
*.tar.7z | tar 打包,7z 程序压缩的文件 |
7.1 zip 压缩打包程序
- 使用 zip 打包文件夹: $ zip -r -q(-v) -o shiyanlou.zip /home/shiyanlou/Desktop $ du -h shiyanlou.zip #查看容量 $ file shiyanlou.zip
-r 参数表示递归打包包含子目录的全部内容。(打包目录)
-q 参数表示为安静模式,即不向屏幕输出信息。-v 参数则相反
-o,表示输出文件,需在其后紧跟打包输出文件名。
后面使用 du 命令查看打包后文件的大小。
- 设置压缩级别为 9 和 1(9 最大,1 最小),重新打包:
$ zip -r -9 -q -o shiyanlou_9.zip /home/shiyanlou/Desktop -x ~/*.zip $ zip -r -1 -q -o shiyanlou_1.zip /home/shiyanlou/Desktop -x ~/*.zip
-x 是为了排除我们上一次创建的 zip 文件,否则又会被打包进这一次的压缩文件中,注意:这里只能使用绝对路径,否则不起作用。
用 du 命令分别查看默认压缩级别、最低、最高压缩级别及未压缩的文件的大小:
$ du -h -d 0 *.zip ~ | sort
通过 man 手册可知:
- h, --human-readable(顾名思义,你可以试试不加的情况)
- d, --max-depth(所查看文件的深度)
- 创建加密 zip 包
使用 -e 参数可以创建加密压缩包:
$ zip -r -e -o shiyanlou_encryption.zip /home/shiyanlou/Desktop
$ zip -r -l -o shiyanlou.zip /home/shiyanlou/Desktop
需要加上 -l 参数将 LF 转换为 CR+LF 来达到以上目的。
7.2 使用 unzip 命令解压缩 zip 文件
$ 指定路径:-d
将 shiyanlou.zip 解压到当前目录:
$ unzip shiyanlou.zip
使用安静模式,将文件解压到指定目录:
$ unzip -q(-v) shiyanlou.zip -d ziptest
上述指定目录不存在,将会自动创建。如果你不想解压只想查看压缩包的内容你可以使用 -l 参数:
$ unzip -l shiyanlou.zip
注意: 使用 unzip 解压文件时我们同样应该注意兼容问题,中文编码的问题,通常 Windows 系统上面创建的压缩文件,如果有有包含中文的文档或以中文作为文件名的文件时默认会采用 GBK 或其它编码,而 Linux 上面默认使用的是 UTF-8 编码,如果不加任何处理,直接解压的话可能会出现中文乱码的问题(有时候它会自动帮你处理),为了解决这个问题,我们可以在解压时指定编码类型。
使用 -O(英文字母,大写 o)参数指定编码类型:
unzip -O GBK 中文压缩文件.zip
7.3 tar 打包工具
在 Linux 上面更常用的是 tar 工具,tar 原本只是一个打包工具,只是同时还是实现了对 7z、gzip、xz、bzip2 等工具的支持,这些压缩工具本身只能实现对文件或目录(单独压缩目录中的文件)的压缩,没有实现对文件的打包压缩,所以我们也无需再单独去学习其他几个工具,tar 的解压和压缩都是同一个命令,只需参数不同,使用比较方便。
下面先掌握 tar 命令一些基本的使用方式,即不进行压缩只是进行打包(创建归档文件)和解包的操作。
$ 指定路径:-C
- 创建一个 tar 包:
$ tar -v -cf shiyanlou.tar /home/shiyanlou/Desktop
-c 表示创建一个 tar 包文件
-f 用于指定创建的文件名,注意文件名必须紧跟在 -f 参数之后。
-v 参数以可视的的方式输出打包的文件。上面会自动去掉表示绝对路径的 /,你也可以使用 -P 保留绝对路径符。
- 解包一个文件(-x 参数)到指定路径的已存在目录(-C 参数):
$ mkdir tardir $ tar -xf shiyanlou.tar -C tardir
- 只查看不解包文件 -t 参数:
$ tar -tf shiyanlou.tar
- 保留文件属性和跟随链接(符号链接或软链接),有时候我们使用 tar 备份文件当你在其他主机还原时希望保留文件的属性(-p 参数)和备份链接指向的源文件而不是链接本身(-h 参数):
$ tar -cphf etc.tar /etc
对于创建不同的压缩格式的文件,对于 tar 来说是相当简单的,需要的只是换一个参数,这里我们就以使用 gzip 工具创建 *.tar.gz 文件为例来说明。
- 只需要在创建 tar 文件的基础上添加 -z 参数,使用 gzip 来压缩文件:
$ tar -czf shiyanlou.tar.gz home/shiyanlou/Desktop
- 解压 *.tar.gz 文件:
$ tar -xzf shiyanlou.tar.gz
现在我们要使用其它的压缩工具创建或解压相应文件只需要更改一个参数即可:
压缩文件格式 | 参数 |
*.tar.gz | -z |
*.tar.xz | -J |
*tar.bz2 | -j |
天冷的时候,要是有个火炉就好了。这里有个有趣的程序:
$ sudo apt-get install libaa-bin # 提示 command not found ,请自行解决 $ aafire
8、文件系统操作与磁盘管理
8.1 查看磁盘和目录的容量
du(estimate file space usage)命令与df(report file system disk space usage)命令。
$ df
在实验楼的环境中你将看到如下的输出内容:
但在实际的物理主机上会更像这样:
物理主机上的 /dev/sda2 是对应着主机硬盘的分区,后面的数字表示分区号,数字前面的字母 a 表示第几块硬盘(也可能是可移动磁盘),你如果主机上有多块硬盘则可能还会出现 /dev/sdb,/dev/sdc 这些磁盘设备都会在 /dev 目录下以文件的存在形式。
接着你还会看到"1k-块"这个陌生的东西,它表示以磁盘块大小的方式显示容量,后面为相应的以块大小表示的已用和可用容量
$ df -h
使用 du 命令查看目录的容量
这个命令前面其实已经用了很多次了:
# 默认同样以 块 的大小展示 $ du
-d参数指定查看目录的深度
# 只查看1级目录的信息 $ du -h -d 0 ~ # 查看2级 $ du -h -d 1 ~
常用参数
du -h #同--human-readable 以K,M,G为单位,提高信息的可读性。 du -a #同--all 显示目录中所有文件的大小。 du -s #同--summarize 仅显示总计,只列出最后加总的值。
8.2 创建虚拟磁盘
dd 命令简介
dd命令用于转换和复制文件,不过它的复制不同于cp。之前提到过关于 Linux 的很重要的一点,一切即文件,在 Linux 上,硬件的设备驱动(如硬盘)和特殊设备文件(如/dev/zero和/dev/random)都像普通文件一样,只是在各自的驱动程序中实现了对应的功能。
dd 也可以读取文件或写入这些文件。这样,dd也可以用在备份硬件的引导扇区、获取一定数量的随机数据或者空数据等任务中。dd程序也可以在复制时处理数据,例如转换字节序、或在 ASCII 与 EBCDIC 编码间互换。
dd的命令行语句与其他的 Linux 程序不同,因为它的命令行选项格式为选项=值,而不是更标准的--选项 值或-选项=值。dd默认从标准输入中读取,并写入到标准输出中,但可以用选项if(input file,输入文件)和of(output file,输出文件)改变。
我们先来试试用dd命令从标准输入读入用户的输入到标准输出或者一个文件中:
# 输出到文件 $ dd of=test bs=10 count=1 # 或者 dd if=/dev/stdin of=test bs=10 count=1 # 输出到标准输出 $ dd if=/dev/stdin of=/dev/stdout bs=10 count=1 # 注 在打完了这个命令后,继续在终端打字,作为你的输入
上述命令从标准输入设备读入用户输入(缺省值,所以可省略)然后输出到 test 文件
bs(block size)用于指定块大小(缺省单位为 Byte,也可为其指定如'K','M','G'等单位)
count用于指定块数量。如上图所示,我指定只读取总共 10 个字节的数据,当我输入了“hello shiyanlou”之后加上空格回车总共 16 个字节(一个英文字符占一个字节)内容,显然超过了设定大小。使用du和cat命令看到的写入完成文件实际内容确实只有 10 个字节(那个黑底百分号表示这里没有换行符),而其他的多余输入将被截取并保留在标准输入。
前面说到dd在拷贝的同时还可以实现数据转换,那下面就举一个简单的例子:将输出的英文字符转换为大写再写入文件:
$ dd if=/dev/stdin of=test bs=10 count=1 conv=ucase
使用 dd 命令创建虚拟镜像文件
通过上面一小节,你应该掌握了dd的基本使用,下面就来使用dd命令来完成创建虚拟磁盘的第一步。
从/dev/zero设备创建一个容量为 256M 的空文件:
$ dd if=/dev/zero of=virtual.img bs=1M count=256 $ du -h virtual.img
然后我们要将这个文件格式化(写入文件系统)
使用 mkfs 命令格式化磁盘(我们这里是自己创建的虚拟磁盘镜像)
你可以在命令行输入 sudo mkfs 然后按下Tab键,你可以看到很多个以 mkfs 为前缀的命令,这些不同的后缀其实就是表示着不同的文件系统,可以用 mkfs 格式化成的文件系统。
我们可以简单的使用下面的命令来将我们的虚拟磁盘镜像格式化为ext4文件系统:
$ sudo mkfs.ext4 virtual.img
可以看到实际 mkfs.ext4 是使用 mke2fs 来完成格式化工作的。
如果你想知道 Linux 支持哪些文件系统你可以输入ls -l /lib/modules/$(uname -r)/kernel/fs(我们的环境中无法查看)查看。
使用 mount 命令挂载磁盘到目录树
用户在 Linux/UNIX 的机器上打开一个文件以前,包含该文件的文件系统必须先进行挂载的动作,此时用户要对该文件系统执行 mount 的指令以进行挂载。
该指令通常是使用在 USB 或其他可移除存储设备上,而根目录则需要始终保持挂载的状态。又因为 Linux/UNIX 文件系统可以对应一个文件而不一定要是硬件设备,所以可以挂载一个包含文件系统的文件到目录树。
Linux/UNIX 命令行的 mount 指令是告诉操作系统,对应的文件系统已经准备好,可以使用了,而该文件系统会对应到一个特定的点(称为挂载点)。挂载好的文件、目录、设备以及特殊文件即可提供用户使用。
我们先来使用mount来查看下主机已经挂载的文件系统:
$ sudo mount
输出的结果中每一行表示一个设备或虚拟设备,每一行最前面是设备名,然后是 on 后面是挂载点,type 后面表示文件系统类型,再后面是挂载选项(比如可以在挂载时设定以只读方式挂载等等)。
那么我们如何挂载真正的磁盘到目录树呢,mount命令的一般格式如下:
mount [options] [source] [directory]
一些常用操作:
mount [-o [操作选项]] [-t 文件系统类型] [-w|--rw|--ro] [文件系统源] [挂载点]
现在直接来挂载我们创建的虚拟磁盘镜像到/mnt目录:
$ mount -o loop -t ext4 virtual.img /mnt # 也可以省略挂载类型,很多时候 mount 会自动识别 # 以只读方式挂载 $ mount -o loop --ro virtual.img /mnt # 或者mount -o loop,ro virtual.img /mnt
使用 umount 命令卸载已挂载磁盘
注意:由于实验楼的环境限制,mount 命令挂载及 umount 卸载都无法进行操作,可以简单了解这些步骤。
# 命令格式 sudo umount 已挂载设备名或者挂载点,如: $ sudo umount /mnt
另外关于 loop 设备,你可能会有诸多疑问,那么请看下面来自维基百科/dev/loop的说明:
在类 UNIX 系统中,/dev/loop(或称vnd (vnode disk)、lofi(循环文件接口))是一种伪设备,这种设备使得文件可以如同块设备一般被访问。
在使用之前,循环设备必须与现存文件系统上的文件相关联。这种关联将提供给用户一个应用程序接口,接口将允许文件视为块特殊文件(参见设备文件系统)使用。
因此,如果文件中包含一个完整的文件系统,那么这个文件就能如同磁盘设备一般被挂载。
这种设备文件经常被用于光盘或是磁盘镜像。通过循环挂载来挂载包含文件系统的文件,便使处在这个文件系统中的文件得以被访问。这些文件将出现在挂载点目录。如果挂载目录中本身有文件,这些文件在挂载后将被禁止使用。
使用 fdisk 为磁盘分区
# 查看硬盘分区表信息 $ sudo fdisk -l
输出结果中开头显示了我主机上的磁盘的一些信息,包括容量扇区数,扇区大小,I/O 大小等信息。
中间的分区信息,/dev/sda1,/dev/sda2 为主分区分别安装了 Windows 和 Linux 操作系统
/dev/sda3 为交换分区(可以理解为虚拟内存),/dev/sda4 为扩展分区其中包含 /dev/sda5,/dev/sda6,/dev/sda7,/dev/sda8 四个逻辑分区,因为主机上有几个分区之间有空隙,没有对齐边界扇区,所以分区之间不是完全连续的。
# 进入磁盘分区模式 $ sudo fdisk virtual.img
在进行操作前我们首先应先规划好我们的分区方案,这里我将在使用 128M(可用 127M 左右)的虚拟磁盘镜像创建一个 30M 的主分区剩余部分为扩展分区包含 2 个大约 45M 的逻辑分区。
操作完成后输入p查看结果如下:
最后不要忘记输入w写入分区表。
使用 losetup 命令建立镜像与回环设备的关联
同样因为环境原因中没有物理磁盘,也没有 loop device 的原因我们就无法实验练习使用该命令了,下面我将以我的物理主机为例讲解。
$ sudo losetup /dev/loop0 virtual.img # 如果提示设备忙你也可以使用其它的回环设备,"ls /dev/loop*"参看所有回环设备 # 解除设备关联 $ sudo losetup -d /dev/loop0
然后再使用mkfs格式化各分区(前面我们是格式化整个虚拟磁盘镜像文件或磁盘),不过格式化之前,我们还要为各分区建立虚拟设备的映射,用到kpartx工具,需要先安装:
$ sudo apt-get install kpartx $ sudo kpartx -av /dev/loop0 # 取消映射 $ sudo kpartx -dv /dev/loop0
接着再是格式化,我们将其全部格式化为 ext4:
$ sudo mkfs.ext4 -q /dev/mapper/loop0p1 $ sudo mkfs.ext4 -q /dev/mapper/loop0p5 $ sudo mkfs.ext4 -q /dev/mapper/loop0p6
格式化完成后在/media目录下新建四个空目录用于挂载虚拟磁盘:
$ mkdir -p /media/virtualdisk_{1..3}
# 挂载磁盘分区 $ sudo mount /dev/mapper/loop0p1 /media/virtualdisk_1 $ sudo mount /dev/mapper/loop0p5 /media/virtualdisk_2 $ sudo mount /dev/mapper/loop0p6 /media/virtualdisk_3 # 卸载磁盘分区 $ sudo umount /dev/mapper/loop0p1 $ sudo umount /dev/mapper/loop0p5 $ sudo umount /dev/mapper/loop0p6
然后:
$ df -h
轻松一下
cowsay命令,可以让你在终端里以一种动物说话的形式打印出一段话。
# 更新软件包 $ sudo apt update # 安装 $ sudo apt install -y cowsay # 默认是一只牛 $ cowsay hello shiyanlou # 加上'-l'参数打印所有支持的动物(其实不只是动物)种类 $ cowsay -l # 使用'-f'参数选择动物种类 $ cowsay -f elephant hello shiyanlou # 安装 fortune-zh $ sudo apt-get install fortune-zh # 此外它还可以结合我们之前的作业讲过的 fortune 命令一起使用 $ /usr/games/fortune | cowsay -f daemon
小明在管理服务器的时候发现空间不足了,他找到了一个目录,这个目录占用的空间异常的大,他需要找到这个目录中占用最大的前10个文件,但是这个目录下面的内容太多了,或许一条命令就能够帮助到他,这条命令该怎么写呢?
简而言之:找出当前目录下面占用最大的前十个文件。
du -hx * | sort -rh | head -10 # 一层
du -h * | sort -rh | head -10 # 一层
du -h /home/shiyanlou/ | sort -rh | head -10 # 迭代多层
9、Linux下的帮助命令
9.1 内建命令与外部命令
因为有一些查看帮助的工具在内建命令与外建命令上是有区别对待的。
内建命令实际上是 shell 程序的一部分,其中包含的是一些比较简单的 Linux 系统命令
这些命令是写在bash源码的builtins里面的,由 shell 程序识别并在 shell 程序内部完成运行,通常在 Linux 系统加载运行时 shell 就被加载并驻留在系统内存中。而且解析内部命令 shell 不需要创建子进程,因此其执行速度比外部命令快。比如:history、cd、exit 等等。
外部命令是 Linux 系统中的实用程序部分,因为实用程序的功能通常都比较强大,所以其包含的程序量也会很大,在系统加载时并不随系统一起被加载到内存中,而是在需要时才将其调入内存。虽然其不包含在 shell 中,但是其命令执行过程是由 shell 程序控制的。外部命令是在 Bash 之外额外安装的,通常放在/bin,/usr/bin,/sbin,/usr/sbin等等。比如:ls、vi等。
简单来说就是:一个是天生自带的天赋技能,一个是后天得来的附加技能。
我们可以使用 type 命令来区分命令是内建的还是外部的
#得到这样的结果说明是内建命令,正如上文所说内建命令都是在 bash 源码中的 builtins 的.def中 xxx is a shell builtin #得到这样的结果说明是外部命令,正如上文所说,外部命令在/usr/bin or /usr/sbin等等中 xxx is /usr/bin/xxx #若是得到alias的结果,说明该指令为命令别名所设定的名称; xxx is an alias for xx -
9.2 help命令
本实验环境是 zsh,而 zsh 中内置并没有 help 命令,我们可以进入 bash 中,在 bash 中内置有该命令
bash
做好了以上的准备,我们就可以愉快的使用 help 命令了,我们可以尝试下这个命令:
help ls # 对内部命令
ls --help # 对外部命令
9.3 man命令
你可以尝试下这个命令
man ls
得到的内容比用 help 更多更详细
在尝试上面这个命令时我们会发现最左上角显示“ LS (1)”,在这里,“ LS ”表示手册名称,而“(1)”表示该手册位于第一章节。这个章节又是什么?在 man 手册中一共有这么几个章节
章节数 | 说明 |
1 | Standard commands (标准命令) |
2 | System calls (系统调用) |
3 | Library functions (库函数) |
4 | Special devices (设备说明) |
5 | File formats (文件格式) |
6 | Games and toys (游戏和娱乐) |
7 | Miscellaneous (杂项) |
8 | Administrative Commands (管理员命令) |
9 | 其他(Linux特定的), 用来存放内核例行程序的文档。 |
打开手册之后我们可以通过 pgup 与 pgdn 或者上下键来上下翻看,可以按 q 退出当前页面
9.4 info命令
要是你觉得man显示的信息都还不够,满足不了你的需求,那试试 info 命令 $ info ls
10、Linux任务计划crontab
10.1 crontab 简介
老师布置作业,学生记作业,开闹钟提醒写作业
crontab 命令从输入设备读取指令,并将其存放于 crontab 文件中,以供之后读取和执行。
通常,crontab 储存的指令被守护进程激活,crond 为其守护进程,crond 常常在后台运行,每一分钟会检查一次是否有预定的作业需要执行。
通过 crontab 命令,我们可以在固定的间隔时间执行指定的系统指令或 shell script 脚本。时间间隔的单位可以是分钟、小时、日、月、周的任意组合。
crontab 的格式
# Example of job definition: # .---------------- minute (0 - 59) # | .------------- hour (0 - 23) # | | .---------- day of month (1 - 31) # | | | .------- month (1 - 12) OR jan,feb,mar,apr ... # | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat # | | | | | # * * * * * user-name command to be executed
10.2 crontab 准备
crontab 在本实验环境中需要做一些特殊的准备,首先我们会启动 rsyslog,以便我们可以通过日志中的信息来了解我们的任务是否真正的被执行了(在本实验环境中需要手动启动,而在自己本地中 Ubuntu 会默认自行启动不需要手动启动)
sudo apt-get install -y rsyslog sudo service rsyslog start
在本实验环境中 crontab 是不被默认启动的,同时不能在后台由 upstart 来管理,需要我们来启动它:
sudo cron -f &
vim编辑器一点小知识
1、输入你要输入的内容.
如果你想退出文本编辑模式.并且保存刚刚编辑的文本.首先点击一下键盘上的"ESC",然后输入":wq!"这样就将文本保存了.
如果不想保存就按一下键盘上的"ESC",输入":q!".如果你不确定是否成功
光标的移动
除了键盘上的上下左右键.也可以是小写模式下,按键盘上的"h,j,k,l",
"ctrl+b":屏幕往后移动一页
"ctrl+f":屏幕往前移动一页
"ctrl+u":屏幕往后移动半页
"ctrl+d":屏幕往前移动半页
输入任何一个整数,然后输入"shift+G" 就可以到这一页的开头了.
按键盘上的大写"G"移动到文章的最后.
按"$"符号将光标移动到行尾
"^"将光标移到行头
"w":将光标移到下一行头
"b":跟"w"相反.移到上一行行头
"e":将光标移到下一行尾.
如果想让文本显示行号.就在命令行 模式下,在文本最后输入":set nu"命令.就可以了
- 5
复制 「yw」:将光标所在之处到字尾的字符复制到缓冲区中。 「yy」:复制光标所在行到缓冲区。 「#yy」:例如,「6yy」表示拷贝从光标所在的该行"往下数"6行文字。 「p」:将缓冲区内的字符贴到光标所在位置。注意:所有与"y"有关的复制命令都必须与"p"配合才能完成复制与粘贴功能。
- 6
查找
用"/"加上要查找的内容.例如"/hello"或者是"?hello"也是可以的.
- 7
替换 「r」:替换光标所在处的字符。 ,按一下"r"然后输入要即可. 「R」:替换光标所到之处的字符,直到按下「ESC」键为止。
- 8
恢复上一次操作 「u」:如果您误执行一个命令,可以马上按下「u」,回到上一个操作。按多次"u"可以执行多次恢复.就相当于"CTRL +Z"的操作.
10.3 crontab 使用
添加一个计划任务
$ crontab -e 表示为当前用户添加计划任务 $ sudo crontab -e 表示为root用户添加计划任务
第一次启动会出现这样一个画面,这是让我们选择编辑的工具,选择第二个基本的 vim 就可以了
而选择后我们会进入这样一个画面,这就是添加计划的地方了,与一般的配置文档相同
在文档的最后一排加上这样一排命令,该任务是每分钟我们会在/home/shiyanlou目录下创建一个以当前的年月日时分秒为名字的空白文件
*/1 * * * * touch /home/shiyanlou/$(date +\%Y\%m\%d\%H\%M\%S)
注意 “ % ” 在 crontab 文件中,有结束命令行、换行、重定向的作用,前面加 ” \ ” 符号转义,否则,“ % ” 符号将执行其结束命令行或者换行的作用,并且其后的内容会被做为标准输入发送给前面的命令。
添加成功后我们会得到最后一排 installing new crontab 的一个提示
查看我们添加了哪些任务:crontab -l
通过图中的显示,我们也可以看出,我们正确的保存并且添加成功了该任务的
虽然我们添加了任务,但是如果 cron 的守护进程并没有启动,它根本都不会监测到有任务,当然也就不会帮我们执行,我们可以通过以下2种方式来确定我们的 cron 是否成功的在后台启动,默默的帮我们做事,若是没有就得执行上文准备中的第二步了
ps aux | grep cron or pgrep cron
通过下图可以看到任务在创建之后,执行了几次,生成了一些文件,且每分钟生成一个:
查看到执行任务命令之后在日志中的信息反馈
sudo tail -f /var/log/syslog
删除任务
crontab -r
10.4 crontab 的深入
每个用户使用 crontab -e 添加计划任务,都会在 /var/spool/cron/crontabs 中添加一个该用户自己的任务文档,这样目的是为了隔离。
如果是系统级别的定时任务,应该如何处理?
只需要以 sudo 权限编辑 /etc/crontab 文件就可以。
cron 服务监测时间最小单位是分钟,所以 cron 会每分钟去读取一次 /etc/crontab 与 /var/spool/cron/crontabs 里面的內容。
在 /etc 目录下,cron 相关的目录有下面几个:
每个目录的作用:
- /etc/cron.daily,目录下的脚本会每天执行一次,在每天的6点25分时运行;
- /etc/cron.hourly,目录下的脚本会每个小时执行一次,在每小时的17分钟时运行;
- /etc/cron.monthly,目录下的脚本会每月执行一次,在每月1号的6点52分时运行;
- /etc/cron.weekly,目录下的脚本会每周执行一次,在每周第七天的6点47分时运行;
系统默认执行时间可以根据需求进行修改。
参考:http://linux.vbird.org/linux_basic/0430cron.php
10.5 挑战:备份日志
目标
- 为 shiyanlou 用户添加计划任务
- 每天凌晨 3 点的时候定时备份 /var/log/ alternatives.log到 /home/shiyanlou/tmp/ 目录,并删除之前在 /home/shiyanlou/tmp/ 目录的日志。
- 命名格式为 年-月-日,比如今天是2017年4月1日,那么文件名为 2017-04-01
参考答案
$ sudo cron -f & # 启动crontab
$ crontab -e # 为当前用户添加计划任务
$ crontab -e # 为当前用户添加计划任务
0 3 * * * sudo rm /home/shiyanlou/tmp/* 0 3 * * * sudo cp /var/log/alternatives.log /home/shiyanlou/tmp/$(date +\%Y-\%m-\%d)
11、命令执行顺序控制
11.1 顺序执行多条命令
简单的顺序执行你可以使用;来完成,比如上述操作你可以:
$ sudo apt-get update ; sudo apt-get install some-tool ; some-tool
11.2 有选择的执行命令
使用which来查找是否安装某个命令,如果找到就执行该命令,否则什么也不做
$ which cowsay>/dev/null && cowsay -f head-in ohch~
你如果没有安装cowsay,你可以先执行一次上述命令,你会发现什么也没发生,你再安装好之后你再执行一次上述命令,你也会发现一些惊喜。
上面的&&就是用来实现选择性执行的,它表示如果前面的命令执行结果(不是表示终端输出的内容,而是表示命令执行状态的结果)返回0则执行后面的,否则不执行,你可以从$?环境变量获取上一次命令的返回结果:
||在这里就是与&&相反的控制效果,当上一条命令执行结果为≠0($?≠0)时则执行它后面的命令:
$ which cowsay>/dev/null || echo "cowsay has not been install, please run 'sudo apt-get install cowsay' to install"
除了上述基本的使用之外,我们还可以结合着&&和||来实现一些操作,比如:
$ which cowsay>/dev/null && echo "exist" || echo "not exist"
12、管道
管道是一种通信机制,通常用于进程间的通信(也可通过socket进行网络通信),它表现出来的形式就是将前面每一个进程的输出(stdout)直接作为下一个进程的输入(stdin)。
12.1 试用
$ ls -al /etc | less
通过管道将前一个命令(ls)的输出作为下一个命令(less)的输入,然后就可以一行一行地看。
12.2 cut 命令,打印每一行的某一字段
打印/etc/passwd文件中以:为分隔符的第1个字段和第6个字段分别表示用户名和其家目录:
$ cut /etc/passwd -d ':' -f 1,6
打印/etc/passwd文件中每一行的前N个字符:
# 前五个(包含第五个) $ cut /etc/passwd -c -5 # 前五个之后的(包含第五个) $ cut /etc/passwd -c 5- # 第五个 $ cut /etc/passwd -c 5 # 2到5之间的(包含第五个) $ cut /etc/passwd -c 2-5
12.3 grep 命令,在文本中或 stdin 中查找匹配字符串
grep命令的一般形式为:
grep [命令选项]... 用于匹配的表达式 [文件]...
还是先体验一下,我们搜索/home/shiyanlou目录下所有包含"shiyanlou"的文本文件,并显示出现在文本中的行号:
$ grep -rnI "shiyanlou" ~
-r 参数表示递归搜索子目录中的文件
-n表示打印匹配项行号
-I表示忽略二进制文件。这个操作实际没有多大意义
# 查看环境变量中以"yanlou"结尾的字符串 $ export | grep ".*yanlou$" 其中$就表示一行的末尾。
12.4 wc 命令,简单小巧的计数工具
wc 命令用于统计并输出一个文件中行、单词和字节的数目,比如输出/etc/passwd文件的统计信息:
$ wc /etc/passwd
分别只输出行数、单词数、字节数、字符数和输入文本中最长一行的字节数:
# 行数 $ wc -l /etc/passwd # 单词数 $ wc -w /etc/passwd # 字节数 $ wc -c /etc/passwd # 字符数 $ wc -m /etc/passwd # 最长行字节数 $ wc -L /etc/passwd
注意:对于西文字符来说,一个字符就是一个字节,但对于中文字符一个汉字是大于2个字节的,具体数目是由字符编码决定的
结合管道来操作一下,下面统计 /etc 下面所有目录数:
$ ls -dl /etc/*/ | wc -l
12.5 sort 排序命令
支持的排序有按字典排序,数字排序,按月份排序,随机排序,反转排序,指定特定字段进行排序等等。
默认为字典排序:
$ cat /etc/passwd | sort
反转排序:
$ cat /etc/passwd | sort -r
按特定字段排序:
$ cat /etc/passwd | sort -t':' -k 3
-t参数用于指定字段的分隔符,这里是以":"作为分隔符;
-k 字段号用于指定对哪一个字段进行排序。
/etc/passwd文件的第三个字段为数字,默认以字典序排序的,如果要按照数字排序就要加上-n参数:
$ cat /etc/passwd | sort -t':' -k 3 -n
12.6 uniq 去重命令
uniq命令可以用于过滤或者输出重复行。
- 过滤重复行
我们可以使用history命令查看最近执行过的命令(实际为读取${SHELL}_history文件,如我们环境中的~/.zsh_history文件)
$ history | cut -c 8- | cut -d ' ' -f 1 | uniq
然后经过层层过滤,你会发现确是只输出了执行的命令那一列,不过去重效果好像不明显,仔细看你会发现它确实去重了,只是不那么明显,之所以不明显是因为uniq命令只能去连续重复的行,不是全文去重,所以要达到预期效果,我们先排序:
$ history | cut -c 8- | cut -d ' ' -f 1 | sort | uniq # 或者$ history | cut -c 8- | cut -d ' ' -f 1 | sort -u
这就是 Linux/UNIX 哲学吸引人的地方,大繁至简,一个命令只干一件事却能干到最好。
- 输出重复行
# 输出重复过的行(重复的只输出一个)及重复次数 $ history | cut -c 8- | cut -d ' ' -f 1 | sort | uniq -dc # 输出所有重复的行 $ history | cut -c 8- | cut -d ' ' -f 1 | sort | uniq -D
轻松一下
使用以前介绍过的方法,安装aview和imagemagick,然后用asciiview命令显示图片,使用方法可以用 man 命令查看。
Linus大神的照片地址 http://labfile.oss.aliyuncs.com/courses/1/Linus.png
执行以下命令安装工具包并下载图片:
$ sudo apt update $ sudo apt install -y aview imagemagick $ wget http://labfile.oss.aliyuncs.com/courses/1/Linus.png
执行 asciiview [图片文件名] 即可打开图片:
13、简单文本处理
13.1 tr 命令
tr 命令可以用来删除一段文本信息中的某些文字。或者将其进行转换。
使用方式:
tr [option]...SET1 [SET2]
常用的选项有:
选项 | 说明 |
-d | 删除和set1匹配的字符,注意不是全词匹配也不是按字符顺序匹配 |
-s | 去除set1指定的在输入文本中连续并重复的字符 |
操作举例:
# 删除 "hello shiyanlou" 中所有的'o','l','h' $ echo 'hello shiyanlou' | tr -d 'olh'
# 将"hello" 中的ll,去重为一个l $ echo 'hello' | tr -s 'l'
# 将输入文本,全部转换为大写或小写输出 $ echo 'input some text here' | tr '[:lower:]' '[:upper:]' # 上面的'[:lower:]' '[:upper:]'你也可以简单的写作'[a-z]' '[A-Z]',当然反过来将大写变小写也是可以的
13.2 col 命令
col 命令可以将Tab换成对等数量的空格键,或反转这个操作。
使用方式:
col [option]
常用的选项有:
选项 | 说明 |
-x | 将Tab转换为空格 |
-h | 将空格转换为Tab(默认选项) |
操作举例:
# 查看 /etc/protocols 中的不可见字符,可以看到很多 ^I ,这其实就是 Tab 转义成可见字符的符号 $ cat -A /etc/protocols # 使用 col -x 将 /etc/protocols 中的 Tab 转换为空格,然后再使用 cat 查看,你发现 ^I 不见了 $ cat /etc/protocols | col -x | cat -A
13.3 join命令
join 命令就是用于将两个文件中包含相同内容的那一行合并在一起。
使用方式:
join [option]... file1 file2
常用的选项有:
选项 | 说明 |
-t | 指定分隔符,默认为空格 |
-i | 忽略大小写的差异 |
-1 | 指明第一个文件要用哪个字段来对比,默认对比第一个字段 |
-2 | 指明第二个文件要用哪个字段来对比,默认对比第一个字段 |
操作举例: # 创建两个文件 $ echo '1 hello' > file1 $ echo '1 shiyanlou' > file2 $ join file1 file2
# 将/etc/passwd与/etc/shadow两个文件合并,指定以':'作为分隔符 $ sudo join -t':' /etc/passwd /etc/shadow
# 将/etc/passwd与/etc/group两个文件合并,指定以':'作为分隔符, 分别比对第4和第3个字段 $ sudo join -t':' -1 4 /etc/passwd -2 3 /etc/group
13.4 paste命令
paste命令与join 命令类似,它是在不对比数据的情况下,简单地将多个文件合并一起,以Tab隔开。
使用方式:
paste [option] file...
常用的选项有:
选项 | 说明 |
-d | 指定合并的分隔符,默认为Tab |
-s | 不合并到一行,每个文件为一行 |
操作举例:
$ echo hello > file1 $ echo shiyanlou > file2 $ echo www.shiyanlou.com > file3 $ paste -d ':' file1 file2 file3 $ paste -s file1 file2 file3
轻松一下
还记得小时候在小霸王上面玩的小蜜蜂游戏么?它的正统名字应该是Space Invaders:太空侵略者。
使用下面这个命令可以安装,之所以叫 ninvaders 是因为这款游戏是基于 ncurses 命令行图形库做的:
$ sudo apt-get install ninvaders $ /usr/games/ninvaders
14、数据流重定向
14.1 简单的重定向
Linux 默认提供了三个特殊设备,用于终端的显示和输出:
stdin(标准输入,对应于你在终端的输入),
stdout(标准输出,对应于终端的输出),
stderr(标准错误输出,对应于终端的输出)。
文件描述符 | 设备文件 | 说明 |
0 | /dev/stdin | 标准输入 |
1 | /dev/stdout | 标准输出 |
2 | /dev/stderr | 标准错误 |
文件描述符:文件描述符在形式上是一个非负整数。
实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。
当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。
默认使用终端的标准输入作为命令的输入和标准输出作为命令的输出
$ cat (按Ctrl+C退出)
将cat的连续输出(heredoc方式)重定向到一个文件
$ mkdir Documents $ cat > Documents/test.c <<EOF #include <stdio.h> int main() { printf("hello world\n"); return 0; } EOF
将一个文件作为命令的输入,标准输出作为命令的输出
$ cat Documents/test.c
将echo命令通过管道传过来的数据作为cat命令的输入,将标准输出作为命令的输出
$ echo 'hi' | cat
将echo命令的输出从默认的标准输出重定向到一个普通文件
$ echo 'hello shiyanlou' > redirect $ cat redirect
管道默认是连接前一个命令的输出到下一个命令的输入
重定向通常是需要一个文件来建立两个命令的连接。如将标准输出不定向到屏幕而定向到一个文件,或者将标准错误不定向到屏幕而定向到一个文件,就是将东西放到别的地方去
14.2 标准错误重定向
# 使用cat 命令同时读取两个文件,其中一个存在,另一个不存在 $ cat Documents/test.c hello.c # 你可以看到除了正确输出了前一个文件的内容,还在末尾出现了一条错误信息 # 下面我们将输出重定向到一个文件 $ cat Documents/test.c hello.c > somefile
遗憾的是,这里依然出现了那条错误信息,这正是因为如我上面说的那样,标准输出和标准错误虽然都指向终端屏幕,实际它们并不一样。
那有的时候我们就是要隐藏某些错误或者警告,那又该怎么做呢。这就需要用到我们前面讲的文件描述符了:
# 将标准错误重定向到标准输出,再将标准输出重定向到文件,注意要将重定向到文件写到前面 $ cat Documents/test.c hello.c >somefile 2>&1 # 或者只用bash提供的特殊的重定向符号"&"将标准错误和标准输出同时重定向到文件 $ cat Documents/test.c hello.c &>somefilehell
注意你应该在输出重定向文件描述符前加上&,否则shell会当做重定向到一个文件名为1的文件中
14.3 使用tee命令同时重定向到多个文件
你可能还有这样的需求,除了需要将输出重定向到文件,也需要将信息打印在终端
可以使用tee命令来实现:
$ echo 'hello shiyanlou' | tee hello
14.4 永久重定向
我们可以使用exec命令实现“永久”重定向。exec命令的作用是使用指定的命令替换当前的 Shell,即使用一个进程替换当前进程,或者指定新的重定向:
# 先开启一个子 Shell $ zsh # 使用exec替换当前进程的重定向,将标准输出重定向到一个文件 $ exec 1>somefile # 后面你执行的命令的输出都将被重定向到文件中,直到你退出当前子shell,或取消exec的重定向(后面将告诉你怎么做) $ ls $ exit $ cat somefile
14.5 创建输出文件描述符
在 Shell 中有9个文件描述符。上面我们使用了也是它默认提供的0,1,2号文件描述符。另外我们还可以使用3-8的文件描述符,只是它们默认没有打开而已。你可以使用下面命令查看当前 Shell 进程中打开的文件描述符:
$ cd /dev/fd/;ls -Al
同样使用exec命令可以创建新的文件描述符:
$ zsh $ exec 3>somefile # 将文件描述符3永久重定向到somefile文件 # 先进入目录,再查看,否则你可能不能得到正确的结果,然后再回到上一次的目录 $ cd /dev/fd/;ls -Al;cd - # 注意下面的命令>与&之间不应该有空格,如果有空格则会出错 $ echo "this is test" >&3 $ cat somefile $ exit
14.6 关闭文件描述符
如上面我们打开的3号文件描述符,可以使用如下操作将它关闭:
$ exec 3>&- $ cd /dev/fd;ls -Al;cd -
14.7 完全屏蔽命令的输出
在 Linux 中有一个被称为“黑洞”的设备文件,所有导入它的数据都将被“吞噬”。
在类 UNIX 系统中,/dev/null,或称空设备,是一个特殊的设备文件,它通常被用于丢弃不需要的输出流,或作为用于输入流的空文件,这些操作通常由重定向完成。读取它则会立即得到一个EOF。
我们可以利用设个/dev/null屏蔽命令的输出:
$ cat Documents/test.c nefile 1>/dev/null 2>&1 #标准输出重定向到/dev/null/ 标准错误重定向到输出
上面这样的操作将使你得不到任何输出结果。
14.8 使用 xargs 分割参数列表
xargs 是一条 UNIX 和类 UNIX 操作系统的常用命令。它的作用是将参数列表转换成小块分段传递给其他命令,以避免参数列表过长的问题。
这个命令在有些时候十分有用,特别是当用来处理产生大量输出结果的命令如 find,locate 和 grep 的结果,详细用法请参看 man 文档。
$ cut -d: -f1 < /etc/passwd | sort | xargs echo
上面这个命令用于将/etc/passwd文件按:分割取第一个字段排序后,使用echo命令生成一个列表。
14.8 挑战:历史命令
介绍
在 Linux 中,对于文本的处理和分析是极为重要的,有一个文件叫做 data1,使用下面的命令下载:
$ cd /home/shiyanlou $ wget http://labfile.oss.aliyuncs.com/courses/1/data1
data1 文件里记录是一些命令的操作记录,现在需要你从里面找出出现频率次数前3的命令并保存在 /home/shiyanlou/result。
目标
- 处理文本文件 /home/shiyanlou/data1
- 将结果写入 /home/shiyanlou/result
- 结果包含三行内容,每行内容都是出现的次数和命令名称,如“100 ls”
来源
2016 年百度校招面试题
参考答案
注意:请务必自己独立思考解决问题之后再对照参考答案,一开始直接看参考答案收获不大。
cat data1 |cut -c 8-|sort|uniq -dc|sort -rn -k1 |head -3 > /home/shiyanlou/result
轻松一下
之前介绍过一个在命令行将图片转换为 ascii 字符查看的工具 aview/asciiview,不过它是黑白的。现在,这里是个彩色的:
$ sudo apt-get install caca-utils $ cacaview <pic_file> $ cacademo $ cacafire
15、正则表达式基础(Regular Expression)
shi*
如果这作为一个正则表达式,它将只能匹配 shi
如果不是作为正则表达式*作为一个通配符,则将同时匹配这两个字符串。
因为在正则表达式中*表示匹配前面的子表达式(这里就是它前面一个字符)零次或多次,比如它可以匹配"sh","shii","shish","shiishi"等等,
而作为通配符表示匹配通配符后面任意多个任意字符,所以它可以匹配"shiyanlou",和"shilouyan"两个字符。
基本语法
一个正则表达式通常被称为一个模式(pattern),为用来描述或者匹配一系列符合某个句法规则的字符串。
- +表示前面的字符必须出现至少一次(1次或多次),例如,"goo+gle",可以匹配"gooogle","goooogle"等;
- ?表示前面的字符最多出现一次(0次或1次),例如,"colou?r",可以匹配"color"或者"colour";
- *星号代表前面的字符可以不出现,也可以出现一次或者多次(0次、或1次、或多次),例如,“0*42”可以匹配42、042、0042、00042等。
正则表达式有多种不同的风格,下面列举一些常用的作为 PCRE 子集的适用于perl和python编程语言及grep或egrep的正则表达式匹配规则:(由于markdown表格解析的问题,下面的竖直分隔符用全角字符代替,实际使用时请换回半角字符)
PCRE(Perl Compatible Regular Expressions中文含义:perl语言兼容正则表达式)是一个用 C 语言编写的正则表达式函数库,由菲利普.海泽(Philip Hazel)编写。PCRE是一个轻量级的函数库,比Boost 之类的正则表达式库小得多。PCRE 十分易用,同时功能也很强大,性能超过了 POSIX 正则表达式库和一些经典的正则表达式库。
匹配规则
python学习10.0——进程、线程、正则
优先级
优先级为从上到下从左到右,依次降低:
运算符 | 说明 |
\ | 转义符 |
(), (?:), (?=), [] | 括号和中括号 |
*、+、?、{n}、{n,}、{n,m} | 限定符 |
^、$、\任何元字符 | 定位点和序列 |
| | 选择 |
更多正则表达式的内容可以参考以下链接:
regex的思导图:
15.1 grep模式匹配命令
grep命令用于打印输出文本中匹配的模式串,它使用正则表达式作为模式匹配的条件。
grep支持三种正则表达式引擎,分别用三个参数指定:
参数 | 说明 |
-E | POSIX扩展正则表达式,ERE |
-G | POSIX基本正则表达式,BRE |
-P | Perl正则表达式,PCRE |
在通过grep命令使用正则表达式之前,先介绍一下它的常用参数:
参数 | 说明 |
-b | 将二进制文件作为文本来进行匹配 |
-c | 统计以模式匹配的数目 |
-i | 忽略大小写 |
-n | 显示匹配文本所在行的行号 |
-v | 反选,输出不匹配行的内容 |
-r | 递归匹配查找 |
-A n | n为正整数,表示after的意思,除了列出匹配行之外,还列出后面的n行 |
-B n | n为正整数,表示before的意思,除了列出匹配行之外,还列出前面的n行 |
--color=auto | 将输出中的匹配项设置为自动颜色显示 |
注:在大多数发行版中是默认设置了grep的颜色的,你可以通过参数指定或修改GREP_COLOR环境变量。
- 选择
# grep默认是区分大小写的,这里将匹配所有的小写字母 $ echo '1234\nabcd' | grep '[a-z]' # 将匹配所有的数字 $ echo '1234\nabcd' | grep '[0-9]' # 将匹配所有的数字 $ echo '1234\nabcd' | grep '[[:digit:]]' # 将匹配所有的小写字母 $ echo '1234\nabcd' | grep '[[:lower:]]' # 将匹配所有的大写字母 $ echo '1234\nabcd' | grep '[[:upper:]]' # 将匹配所有的字母和数字,包括0-9,a-z,A-Z $ echo '1234\nabcd' | grep '[[:alnum:]]' # 将匹配所有的字母 $ echo '1234\nabcd' | grep '[[:alpha:]]'
特殊符号 | 说明 |
[:alnum:] | 代表英文大小写字母及数字,亦即 0-9, A-Z, a-z |
[:alpha:] | 代表任何英文大小写字母,亦即 A-Z, a-z |
[:blank:] | 代表空白键与 [Tab] 按键两者 |
[:cntrl:] | 代表键盘上面的控制按键,亦即包括 CR, LF, Tab, Del.. 等等 |
[:digit:] | 代表数字而已,亦即 0-9 |
[:graph:] | 除了空白字节 (空白键与 [Tab] 按键) 外的其他所有按键 |
[:lower:] | 代表小写字母,亦即 a-z |
[:print:] | 代表任何可以被列印出来的字符 |
[:punct:] | 代表标点符号 (punctuation symbol),亦即:" ' ? ! ; : # $... |
[:upper:] | 代表大写字母,亦即 A-Z |
[:space:] | 任何会产生空白的字符,包括空白键, [Tab], CR 等等 |
[:xdigit:] | 代表 16 进位的数字类型,因此包括: 0-9, A-F, a-f 的数字与字节 |
注意:之所以要使用特殊符号,是因为上面的[a-z]不是在所有情况下都管用,这还与主机当前的语系有关,即设置在LANG环境变量的值,zh_CN.UTF-8的话[a-z],即为所有小写字母,其它语系可能是大小写交替的如,"a A b B...z Z",[a-z]中就可能包含大写字母。所以在使用[a-z]时请确保当前语系的影响,使用[:lower:]则不会有这个问题。
使用扩展正则表达式,ERE
要通过grep使用扩展正则表达式需要加上-E参数,或使用egrep。
- 数量
# 只匹配"zo" $ echo 'zero\nzo\nzoo' | grep -E 'zo{1}' # 匹配以"zo"开头的所有单词 $ echo 'zero\nzo\nzoo' | grep -E 'zo{1,}'
- 选择
# 匹配"www.shiyanlou.com"和"www.google.com" $ echo 'www.shiyanlou.com\nwww.baidu.com\nwww.google.com' | grep -E 'www\.(shiyanlou|google)\.com' # 或者匹配不包含"baidu"的内容 $ echo 'www.shiyanlou.com\nwww.baidu.com\nwww.google.com' | grep -Ev 'www\.baidu\.com'
注意:因为.号有特殊含义,所以需要转义。
15.2 sed 流编辑器
sed工具在 man 手册里面的全名为"sed - stream editor for filtering and transforming text ",意即,用于过滤和转换文本的流编辑器。
在 Linux/UNIX 的世界里敢称为编辑器的工具,大都非等闲之辈,比如前面的"vi/vim(编辑器之神)","emacs(神的编辑器)","gedit"这些个编辑器。sed与上述的最大不同之处在于它是一个非交互式的编辑器,下面我们就开始介绍sed这个编辑器。
sed常用参数介绍
sed 命令基本格式:
sed [参数]... [执行命令] [输入文件]... # 形如: $ sed -i 's/sad/happy/' test # 表示将test文件中的"sad"替换为"happy"
参数 | 说明 |
-n | 安静模式,只打印受影响的行,默认打印输入数据的全部内容 |
-e | 用于在脚本中添加多个执行命令一次执行,在命令行中执行多个命令通常不需要加该参数 |
-f filename | 指定执行filename文件中的命令 |
-r | 使用扩展正则表达式,默认为标准正则表达式 |
-i | 将直接修改输入文件内容,而不是打印到标准输出设备 |
sed执行命令格式:
[n1][,n2]command [n1][~step]command # 其中一些命令可以在后面加上作用范围,形如: $ sed -i 's/sad/happy/g' test # g表示全局范围 $ sed -i 's/sad/happy/4' test # 4表示指定行中的第四个匹配字符串
其中n1,n2表示输入内容的行号,它们之间为,逗号则表示从n1到n2行,如果为~波浪号则表示从n1开始以step为步进的所有行;command为执行动作,下面为一些常用动作指令:
命令 | 说明 |
s | 行内替换 |
c | 整行替换 |
a | 插入到指定行的后面 |
i | 插入到指定行的前面 |
p | 打印指定行,通常与-n参数配合使用 |
d | 删除指定行 |
我们先找一个用于练习的文本文件:
$ cp /etc/passwd ~
打印指定行
# 打印2-5行 $ nl passwd | sed -n '2,5p' # 打印奇数行 $ nl passwd | sed -n '1~2p'
行内替换
# 将输入文本中"shiyanlou" 全局替换为"hehe",并只打印替换的那一行,注意这里不能省略最后的"p"命令 $ sed -n 's/shiyanlou/hehe/gp' passwd
注意: 行内替换可以结合正则表达式使用。
行间替换 # 删除第21行 $ sed -n '21c\www.shiyanlou.com' passwd (这里我们只把要删的行打印出来了,并没有真正的删除,如果要删除的话,请使用-i参数)
15.3 awk文本处理语言
AWK是一种优良的文本处理工具,Linux及Unix环境中现有的功能最强大的数据处理引擎之一.其名称得自于它的创始人Alfred Aho(阿尔佛雷德·艾侯)、Peter Jay Weinberger(彼得·温伯格)和Brian Wilson Kernighan(布莱恩·柯林汉)姓氏的首个字母.AWK程序设计语言,三位创建者已将它正式定义为“样式扫描和处理语言”。它允许您创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。最简单地说,AWK是一种用于处理文本的编程语言工具。
在大多数linux发行版上面,实际我们使用的是gawk(GNU awk,awk的GNU版本),在我们的环境中ubuntu上,默认提供的是mawk,不过我们通常可以直接使用awk命令(awk语言的解释器),因为系统已经为我们创建好了awk指向mawk的符号链接。
$ ll /usr/bin/awk
nawk: 在 20 世纪 80 年代中期,对 awk语言进行了更新,并不同程度地使用一种称为 nawk(new awk) 的增强版本对其进行了替换。许多系统中仍然存在着旧的awk 解释器,但通常将其安装为 oawk (old awk) 命令,而 nawk 解释器则安装为主要的 awk 命令,也可以使用 nawk 命令。Dr. Kernighan 仍然在对 nawk 进行维护,与 gawk 一样,它也是开放源代码的,并且可以免费获得; gawk: 是 GNU Project 的awk解释器的开放源代码实现。尽管早期的 GAWK 发行版是旧的 AWK 的替代程序,但不断地对其进行了更新,以包含 NAWK 的特性; mawk 也是awk编程语言的一种解释器,mawk遵循 POSIX 1003.2 (草案 11.3)定义的 AWK 语言,包含了一些没有在AWK 手册中提到的特色,同时 mawk 提供一小部分扩展,另外据说mawk是实现最快的awk
awk命令基本格式
awk [-F fs] [-v var=value] [-f prog-file | 'program text'] [file...]
其中-F参数用于预先指定前面提到的字段分隔符(还有其他指定字段的方式) ,-v用于预先为awk程序指定变量,-f参数用于指定awk命令要执行的程序文件,或者在不加-f参数的情况下直接将程序语句放在这里,最后为awk需要处理的文本输入
awk操作体验
先用vim新建一个文本文档
$ vim test
包含如下内容:
I like linux www.shiyanlou.com
- 使用awk将文本内容打印到终端 $ awk '{print}' test
说明:在这个操作中我是省略了pattern,所以awk会默认匹配输入文本的全部内容,然后在"{}"花括号中执行动作,即print打印所有匹配项,这里是全部文本内容
- 将test的第一行的每个字段单独显示为一行
$ awk '{ > if(NR==1){ > print $1 "\n" $2 "\n" $3 > } else { > print} > }' test # 或者 $ awk '{ > if(NR==1){ > OFS="\n" > print $1, $2, $3 > } else { > print} > }' test
说明:你首先应该注意的是,这里我使用了awk语言的分支选择语句if,它的使用和很多高级语言如C/C++语言基本一致,如果你有这些语言的基础,这里将很好理解。另一个你需要注意的是NR与OFS,这两个是awk内建的变量,NR表示当前读入的记录数,你可以简单的理解为当前处理的行数,OFS表示输出时的字段分隔符,默认为" "空格,如上图所见,我们将字段分隔符设置为\n换行符,所以第一行原本以空格为字段分隔的内容就分别输出到单独一行了。然后是$N其中N为相应的字段号,这也是awk的内建变量,它表示引用相应的字段,因为我们这里第一行只有三个字段,所以只引用到了$3。除此之外另一个这里没有出现的$0,它表示引用当前记录(当前行)的全部内容。
- 将test的第二行的以点为分段的字段换成以空格为分隔
$ awk -F'.' '{ > if(NR==2){ > print $1 "\t" $2 "\t" $3 > }}' test # 或者 $ awk ' > BEGIN{ > FS="." > OFS="\t" # 如果写为一行,两个动作语句之间应该以";"号分开 > }{ > if(NR==2){ > print $1, $2, $3 > }}' test
说明:这里的-F参数,前面已经介绍过,它是用来预先指定待处理记录的字段分隔符。我们需要注意的是除了指定OFS我们还可以在print 语句中直接打印特殊符号如这里的\t,print打印的非变量内容都需要用""一对引号包围起来。上面另一个版本,展示了实现预先指定变量分隔符的另一种方式,即使用BEGIN,就这个表达式指示了,其后的动作将在所有动作之前执行,这里是FS赋值了新的"."点号代替默认的" "空格
awk常用的内置变量
变量名 | 说明 |
FILENAME | 当前输入文件名,若有多个文件,则只表示第一个。如果输入是来自标准输入,则为空字符串 |
$0 | 当前记录的内容 |
$N | N表示字段号,最大值为NF变量的值 |
FS | 字段分隔符,由正则表达式表示,默认为" "空格 |
RS | 输入记录分隔符,默认为"\n",即一行为一个记录 |
NF | 当前记录字段数 |
NR | 已经读入的记录数 |
FNR | 当前输入文件的记录数,请注意它与NR的区别 |
OFS | 输出字段分隔符,默认为" "空格 |
ORS | 输出记录分隔符,默认为"\n" |
15.4 数据提取
数据文件可以使用以下命令下载:
$ cd /home/shiyanlou $ wget http://labfile.oss.aliyuncs.com/courses/1/data2
下载后的数据文件路径为 /home/shiyanlou/data2。
目标
- 在文件 /home/shiyanlou/data2 中匹配数字开头的行,将所有以数字开头的行都写入 /home/shiyanlou/num 文件。
- 在文件 /home/shiyanlou/data2 中匹配出正确格式的邮箱,将所有的邮箱写入 /home/shiyanlou/mail 文件,注意该文件中每行为一个邮箱。
来源
2016 年 TapFun 校招面试题
参考答案
注意:请务必自己独立思考解决问题之后再对照参考答案,一开始直接看参考答案收获不大。
grep '^[0-9]' data2 > num grep -E '^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+$' num > mail
cat data2 | grep '^[0-9]' > num
cat num | grep '^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+$' > mail
16、Linux下软件安装
注意:如果你在安装一个软件之后,无法立即使用Tab键补全这个命令,你可以尝试先执行source ~/.zshrc,然后你就可以使用补全操作。
apt-get 是用于处理 apt包的公用程序集,我们可以用它来在线安装、卸载和升级软件包等,下面列出一些apt-get包含的常用的一些工具:
工具 | 说明 |
install | 其后加上软件包名,用于安装一个软件包 |
update | 从软件源镜像服务器上下载/更新用于更新本地软件源的软件包列表 |
upgrade | 升级本地可更新的全部软件包,但存在依赖问题时将不会升级,通常会在更新之前执行一次update |
dist-upgrade | 解决依赖关系并升级(存在一定危险性) |
remove | 移除已安装的软件包,包括与被移除软件包有依赖关系的软件包,但不包含软件包的配置文件 |
autoremove | 移除之前被其他软件包依赖,但现在不再被使用的软件包 |
purge | 与remove相同,但会完全移除软件包,包含其配置文件 |
clean | 移除下载到本地的已经安装的软件包,默认保存在/var/cache/apt/archives/ |
autoclean | 移除已安装的软件的旧版本软件包 |
下面是一些apt-get常用的参数:
参数 | 说明 |
-y | 自动回应是否安装软件包的选项,在一些自动化安装脚本中使用这个参数将十分有用 |
-s | 模拟安装 |
-q | 静默安装方式,指定多个q或者-q=#,#表示数字,用于设定静默级别,这在你不想要在安装软件包时屏幕输出过多时很有用 |
-f | 修复损坏的依赖关系 |
-d | 只下载不安装 |
--reinstall | 重新安装已经安装但可能存在问题的软件包 |
--install-suggests | 同 |
掌握的是如何重新安装软件包。 很多时候我们需要重新安装一个软件包,比如你的系统被破坏,或者一些错误的配置导致软件无法正常工作。
你可以使用如下方式重新安装:
$ sudo apt-get --reinstall install w3m
软件搜索
当自己刚知道了一个软件,、需要确认软件仓库里面有没有,就需要用到搜索功能了,命令如下:
sudo apt-cache search softname1 softname2 softname3……
apt-cache 命令则是针对本地数据进行相关操作的工具,search 顾名思义在本地的数据库中寻找有关 softname1 softname2 …… 相关软件的信息。现在我们试试搜索一下之前我们安装的软件 w3m ,如图:
结果显示了4个 w3m 相关的软件,并且有相关软件的简介。
16.1 使用dpkg
dpkg 介绍
dpkg 是 Debian 软件包管理器的基础,它被伊恩·默多克创建于 1993 年。dpkg 与 RPM 十分相似,同样被用于安装、卸载和供给和 .deb 软件包相关的信息。
dpkg 本身是一个底层的工具。上层的工具,像是 APT,被用于从远程获取软件包以及处理复杂的软件包关系。"dpkg"是"Debian Package"的简写。
我们经常可以在网络上见到以deb形式打包的软件包,就需要使用dpkg命令来安装。
dpkg常用参数介绍:
参数 | 说明 |
-i | 安装指定deb包 |
-R | 后面加上目录名,用于安装该目录下的所有deb安装包 |
-r | remove,移除某个已安装的软件包 |
-I | 显示deb包文件的信息 |
-s | 显示已安装软件的信息 |
-S | 搜索已安装的软件包 |
-L | 显示已安装软件包的目录信息 |
使用dpkg安装deb软件包
我们先使用apt-get加上-d参数只下载不安装,下载emacs编辑器的deb包,下载完成后,我们可以查看/var/cache/apt/archives/目录下的内容,如下图:
然后我们将第一个deb拷贝到 /home/shiyanlou 目录下,并使用dpkg安装
$ cp /var/cache/apt/archives/emacs24_24.5+1-6ubuntu1.1_amd64.deb ~
# 安装之前参看deb包的信息 $ sudo dpkg -I emacs24_24.5+1-6ubuntu1.1_amd64.deb
如你所见,这个包还额外依赖了一些软件包,这意味着,如果主机目前没有这些被依赖的软件包,直接使用dpkg安装可能会存在一些问题,因为dpkg并不能为你解决依赖关系。
我们将如何解决这个错误呢?这就要用到apt-get了,使用它的-f参数了,修复依赖关系的安装
$ sudo apt-get update $ sudo apt-get -f install
# 使用dpkg安装 $ sudo dpkg -i emacs24_24.5+1-6ubuntu1.1_amd64.deb
没有任何错误,这样我们就安装成功了,然后你可以运行emacs程序
使用dpkg -L查看deb包目录信息
$ sudo dpkg -L emacs24
轻松一下:
$ sudo apt-get update $ sudo apt-get install bb $ /usr/games/bb
17、Linux进程
17.1 概念的理解
首先程序与进程是什么?程序与进程又有什么区别?
程序(procedure):不太精确地说,程序就是执行一系列有逻辑、有顺序结构的指令,帮我们达成某个结果。就如我们去餐馆,给服务员说我要牛肉盖浇饭,她执行了做牛肉盖浇饭这么一个程序,最后我们得到了这么一盘牛肉盖浇饭。它需要去执行,不然它就像一本武功秘籍,放在那里等人翻看。
进程(process):进程是程序在一个数据集合上的一次执行过程,在早期的UNIX、Linux 2.4及更早的版本中,它是系统进行资源分配和调度的独立基本单位。同上一个例子,就如我们去了餐馆,给服务员说我要牛肉盖浇饭,她执行了做牛肉盖浇饭这么一个程序,而里面做饭的是一个进程,做牛肉汤汁的是一个进程,把牛肉汤汁与饭混合在一起的是一个进程,把饭端上桌的是一个进程。它就像是我们在看武功秘籍这么一个过程,然后一个篇章一个篇章地去练。
简单来说,程序是为了完成某种任务而设计的软件,比如 vim 是程序。而进程就是运行中的程序。
程序只是一些列指令的集合,是一个静止的实体,而进程不同,进程有以下的特性:
- 动态性:进程的实质是一次程序执行的过程,有创建、撤销等状态的变化。而程序是一个静态的实体。
- 并发性:进程可以做到在一个时间段内,有多个程序在运行中。程序只是静态的实体,所以不存在并发性。
- 独立性:进程可以独立分配资源,独立接受调度,独立地运行。
- 异步性:进程以不可预知的速度向前推进。
- 结构性:进程拥有代码段、数据段、PCB(进程控制块,进程存在的唯一标志)。也正是因为有结构性,进程才可以做到独立地运行。
并发:在一个时间段内,宏观来看有多个程序都在活动,有条不紊的执行(每一瞬间只有一个在执行,只是在一段时间有多个程序都执行过)
并行:在每一个瞬间,都有多个程序都在同时执行,这个必须有多个 CPU 才行
线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。因为线程中几乎不包含系统资源,所以执行更快、更有效率。
进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。就如下图所示:
17.2 进程的分类
以进程的功能与服务的对象来分,我们可以分为用户进程与系统进程:
- 用户进程:通过执行用户程序、应用程序或称之为内核之外的系统程序而产生的进程,此类进程可以在用户的控制下运行或关闭。
- 系统进程:通过执行系统内核程序而产生的进程,比如可以执行内存资源分配和进程切换等相对底层的工作;而且该进程的运行不受用户的干预,即使是 root 用户也不能干预系统进程的运行
以应用程序的服务类型来分,我们可以将进程分为交互进程、批处理进程、守护进程
- 交互进程:由一个 shell 终端启动的进程,在执行过程中,需要与用户进行交互操作,可以运行于前台,也可以运行在后台。
- 批处理进程:该进程是一个进程集合,负责按顺序启动其他的进程。
- 守护进程:守护进程是一直运行的一种进程,在 Linux 系统启动时启动,在系统关闭时终止。它们独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件。例如 httpd 进程,一直处于运行状态,等待用户的访问。还有经常用的 cron(在 centOS 系列为 crond)进程,这个进程为 crontab 的守护进程,可以周期性的执行用户设定的某些任务。
17.3 进程的衍生
关于父进程与子进程便会提及这两个系统调用 fork()与 exec()
fork-exec是由 Dennis M. Ritchie 创造的
fork() 是一个系统调用(system call),它的主要作用就是为当前的进程创建一个子进程,这个子进程除了父进程的返回值和 PID 以外其他的都一模一样,如进程的执行代码段,内存信息,文件描述,寄存器状态等等
exec() 也是系统调用,作用是切换子进程中的执行程序也就是替换其从父进程复制过来的代码段与数据段
子进程就是父进程通过系统调用 fork() 而产生的复制品,fork() 就是把父进程的 PCB 等进程的数据结构信息直接复制过来,只是修改了 PID,所以一模一样,只有在执行 exec() 之后才会不同,而早先的 fork() 比较消耗资源后来进化成 vfork(),效率高了不少
这就是子进程产生的由来。简单的实现逻辑就如下方所示【注释1】
pid_t p; p = fork(); if (p == (pid_t) -1) /* ERROR */ else if (p == 0) /* CHILD */ else /* PARENT */
当一个子进程要正常的终止运行时,或者该进程结束时它的主函数 main() 会执行 exit(n); 或者 return n,这里的返回值 n 是一个信号,系统会把这个 SIGCHLD 信号传给其父进程,当然若是异常终止也往往是因为这个信号。
在将要结束时的子进程代码执行部分已经结束执行了,系统的资源也基本归还给系统了,但若是其进程的进程控制块(PCB)仍驻留在内存中,代表这个进程还存在(因为 PCB 就是进程存在的唯一标志,里面有 PID 等消息),并没有消亡,这样的进程称之为僵尸进程(Zombie)。
如图中第四列标题是 S,S 表示的是进程的状态,而在下属的第三行的 Z 表示的是 Zombie 的意思。
正常情况下,父进程会收到两个返回值:exit code(SIGCHLD 信号)与 reason for termination。
之后,父进程会使用 wait(&status) 系统调用以获取子进程的退出状态,然后内核就可以从内存中释放已结束的子进程的 PCB;而如若父进程没有这么做的话,子进程的 PCB 就会一直驻留在内存中,一直留在系统中成为僵尸进程(Zombie)。
虽然僵尸进程是已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,在进程列表中保留一个位置,记载该进程的退出状态等信息供其父进程收集,从而释放它。但是 Linux 系统中能使用的 PID 是有限的,如果系统中存在有大量的僵尸进程,系统将会因为没有可用的 PID 从而导致不能产生新的进程。
另外如果父进程结束(非正常的结束),未能及时收回子进程,子进程仍在运行,这样的子进程称之为孤儿进程。在 Linux 系统中,孤儿进程一般会被 init 进程所“收养”,成为 init 的子进程。由 init 来做善后处理,所以它并不至于像僵尸进程那样无人问津,不管不顾,大量存在会有危害。
进程 0 是系统引导时创建的一个特殊进程,也称之为内核初始化,其最后一个动作就是调用 fork() 创建出一个子进程运行 /sbin/init 可执行文件,而该进程就是 PID=1 的进程 1,而进程 0 就转为交换进程(也被称为空闲进程),进程 1 (init 进程)是第一个用户态的进程,再由它不断调用 fork() 来创建系统里其他的进程,所以它是所有进程的父进程或者祖先进程。同时它是一个守护程序,直到计算机关机才会停止。
通过以下的命令我们可以很明显的看到这样的结构
pstree
或者从此图我们可以更加形象的看清子父进程的关系
通过以上的显示结果我们可以看的很清楚,init 为所有进程的父进程或者说是祖先进程
我们还可以使用这样一个命令来看,其中 pid 就是该进程的一个唯一编号,ppid 就是该进程的父进程的 pid,command 表示的是该进程通过执行什么样的命令或者脚本而产生的
ps -fxo user,ppid,pid,pgid,command
可以在图中看见我们执行的 ps 就是由 zsh 通过 fork-exec 创建的子进程而执行的
使用这样的一个命令我们也能清楚的看见 init 如上文所说是由进程 0 这个初始化进程来创建出来的子进程,而其他的进程基本是由 init 创建的子进程,或者是由它的子进程创建出来的子进程。所以 init 是用户进程的第一个进程也是所有用户进程的父进程或者祖先进程。(ps 命令将在后续课程详解)
就像一个树状图,而 init 进程就是这棵树的根,其他进程由根不断的发散,开枝散叶
17.4 进程组与 Sessions
每一个进程都会是一个进程组的成员,而且这个进程组是唯一存在的,他们是依靠 PGID(process group ID)来区别的,而每当一个进程被创建的时候,它便会成为其父进程所在组中的一员。
一般情况,进程组的 PGID 等同于进程组的第一个成员的 PID,并且这样的进程称为该进程组的领导者,也就是领导进程,进程一般通过使用 getpgrp() 系统调用来寻找其所在组的 PGID,领导进程可以先终结,此时进程组依然存在,并持有相同的PGID,直到进程组中最后一个进程终结。
与进程组类似,每当一个进程被创建的时候,它便会成为其父进程所在 Session 中的一员,每一个进程组都会在一个 Session 中,并且这个 Session 是唯一存在的,
Session 主要是针对一个 tty 建立,Session 中的每个进程都称为一个工作(job)。每个会话可以连接一个终端(control terminal)。当控制终端有输入输出时,都传递给该会话的前台进程组。Session 意义在于将多个 jobs 囊括在一个终端,并取其中的一个 job 作为前台,来直接接收该终端的输入输出以及终端信号。 其他 jobs 在后台运行。
前台(foreground)就是在终端中运行,能与你有交互的
后台(background)就是在终端中运行,但是你并不能与其任何的交互,也不会显示其执行的过程
17.5 工作管理
bash(Bourne-Again shell)支持工作控制(job control),而 sh(Bourne shell)并不支持。
并且每个终端或者说 bash 只能管理当前终端中的 job,不能管理其他终端中的 job。比如我当前存在两个 bash 分别为 bash1、bash2,bash1 只能管理其自己里面的 job 并不能管理 bash2 里面的 job
我们都知道当一个进程在前台运作时我们可以用 ctrl + c 来终止它,但是若是在后台的话就不行了。
我们可以通过 & 这个符号,让我们的命令在后台中运行
图中所显示的 [1] 236分别是该 job 的 job number 与该进程的 PID,而最后一行的 Done 表示该命令已经在后台执行完毕。
我们还可以通过 ctrl + z 使我们的当前工作停止并丢到后台中去
被停止并放置在后台的工作我们可以使用这个命令来查看
jobs
第一列显示的为被放置后台 job 的编号
+ 表示最近(刚刚、最后)被放置后台的 job,同时也表示预设的工作,也就是若是有什么针对后台 job 的操作,首先对预设的 job
- 表示倒数第二(也就是在预设之前的一个)被放置后台的工作,倒数第三个(再之前的)以后都不会有这样的符号修饰,第三列表示它们的状态,而最后一列表示该进程执行的命令
我们可以通过这样的一个命令将后台的工作拿到前台来
#后面不加参数提取预设工作,加参数提取指定工作的编号 #ubuntu 在 zsh 中需要 %,在 bash 中不需要 % fg %jobnumber
之前我们通过 ctrl + z 使得工作停止放置在后台,若是我们想让其在后台运作我们就使用这样一个命令
#与fg类似,加参则指定,不加参则取预设 bg %jobnumber
删除一个工作,或者重启等等
#kill的使用格式如下 kill -signal %jobnumber #signal从1-64个信号值可以选择,可以这样查看 kill -l
其中常用的有这些信号值
信号值 | 作用 |
-1 | 重新读取参数运行,类似与restart |
-2 | 如同 ctrl+c 的操作退出 |
-9 | 强制终止该任务 |
-15 | 正常的方式终止该任务 |
注意
若是在使用kill+信号值然后直接加 pid,你将会对 pid 对应的进程进行操作
若是在使用kill+信号值然后 %jobnumber,这时所操作的对象是 job,这个数字就是就当前 bash 中后台的运行的 job 的 ID
kill 进程
上节课程我们使用 kill 命令来管理我们的一些 job,这节课我们将尝试用 kill 来操作下一些不属于 job 范畴的进程,直接对 pid 下手
#首先我们使用图形界面打开了 gedit、gvim,用 ps 可以查看到 ps aux #使用9这个信号强制结束 gedit 进程 kill -9 1608 #我们再查找这个进程的时候就找不到了 ps aux | grep gedit
17.6 top 工具的使用
top 工具是我们常用的一个查看工具,能实时的查看我们系统的一些关键信息的变化:
top
top 是一个在前台执行的程序,所以执行后便进入到这样的一个交互界面,正是因为交互界面我们才可以实时的获取到系统与进程的信息。在交互界面中我们可以通过一些指令来操作和筛选。在此之前我们先来了解显示了哪些信息。
我们看到 top 显示的第一排,
内容 | 解释 |
top | 表示当前程序的名称 |
11:05:18 | 表示当前的系统的时间 |
up 8 days,17:12 | 表示该机器已经启动了多长时间 |
1 user | 表示当前系统中只有一个用户 |
load average: 0.29,0.20,0.25 | 分别对应1、5、15分钟内cpu的平均负载 |
load average 在 wikipedia 中的解释是 the system load is a measure of the amount of work that a computer system is doing 也就是对当前 CPU 工作量的度量,具体来说也就是指运行队列的平均长度,也就是等待 CPU 的平均进程数相关的一个计算值。
我们该如何看待这个load average 数据呢?
假设我们的系统是单 CPU、单内核的,把它比喻成是一条单向的桥,把CPU任务比作汽车。
- load = 0 的时候意味着这个桥上并没有车,cpu 没有任何任务;
- load < 1 的时候意味着桥上的车并不多,一切都还是很流畅的,cpu 的任务并不多,资源还很充足;
- load = 1 的时候就意味着桥已经被车给占满了,没有一点空隙,cpu 的已经在全力工作了,所有的资源都被用完了,当然还好,这还在能力范围之内,只是有点慢而已;
- load > 1 的时候就意味着不仅仅是桥上已经被车占满了,就连桥外都被占满了,cpu 已经在全力工作,系统资源的用完了,但是还是有大量的进程在请求,在等待。若是这个值大于2、大于3,表示进程请求超过 CPU 工作能力的 2 到 3 倍。而若是这个值 > 5 说明系统已经在超负荷运作了。
这是单个 CPU 单核的情况,而实际生活中我们需要将得到的这个值除以我们的核数来看。我们可以通过以下的命令来查看 CPU 的个数与核心数
#查看物理CPU的个数 #cat /proc/cpuinfo |grep "physical id"|sort |uniq|wc -l #每个cpu的核心数 cat /proc/cpuinfo |grep "physical id"|grep "0"|wc -l
通过上面的指数我们可以得知 load 的临界值为 1 ,但是在实际生活中,比较有经验的运维或者系统管理员会将临界值定为0.7。这里的指数都是除以核心数以后的值,不要混淆了
- 若是 load < 0.7 并不会去关注他;
- 若是 0.7< load < 1 的时候我们就需要稍微关注一下了,虽然还可以应付但是这个值已经离临界不远了;
- 若是 load = 1 的时候我们就需要警惕了,因为这个时候已经没有更多的资源的了,已经在全力以赴了;
- 若是 load > 5 的时候系统已经快不行了,这个时候你需要加班解决问题了
通常我们都会先看 15 分钟的值来看这个大体的趋势,然后再看 5 分钟的值对比来看是否有下降的趋势。
查看 busybox 的代码可以知道,数据是每 5 秒钟就检查一次活跃的进程数,然后计算出该值,然后 load 从 /proc/loadavg 中读取的。而这个 load 的值是如何计算的呢,这是 load 的计算的源码
#define FSHIFT 11 /* nr of bits of precision */ #define FIXED_1 (1<<FSHIFT) /* 1.0 as fixed-point(定点) */ #define LOAD_FREQ (5*HZ) /* 5 sec intervals,每隔5秒计算一次平均负载值 */ #define CALC_LOAD(load, exp, n) \ load *= exp; \ load += n*(FIXED_1 - exp); \ load >>= FSHIFT; unsigned long avenrun[3]; EXPORT_SYMBOL(avenrun); /* * calc_load - given tick count, update the avenrun load estimates. * This is called while holding a write_lock on xtime_lock. */ static inline void calc_load(unsigned long ticks) { unsigned long active_tasks; /* fixed-point */ static int count = LOAD_FREQ; count -= ticks; if (count < 0) { count += LOAD_FREQ; active_tasks = count_active_tasks(); CALC_LOAD(avenrun[0], EXP_1, active_tasks); CALC_LOAD(avenrun[1], EXP_5, active_tasks); CALC_LOAD(avenrun[2], EXP_15, active_tasks); } }
有兴趣的朋友可以研究一下,是如何计算的。代码中的后面这部分相当于它的计算公式
我们回归正题,来看 top 的第二行数据,基本上第二行是进程的一个情况统计
内容 | 解释 |
Tasks: 26 total | 进程总数 |
1 running | 1个正在运行的进程数 |
25 sleeping | 25个睡眠的进程数 |
0 stopped | 没有停止的进程数 |
0 zombie | 没有僵尸进程数 |
来看 top 的第三行数据,这一行基本上是 CPU 的一个使用情况的统计了
内容 | 解释 |
Cpu(s): 1.0%us | 用户空间进程占用CPU百分比 |
1.0% sy | 内核空间运行占用CPU百分比 |
0.0%ni | 用户进程空间内改变过优先级的进程占用CPU百分比 |
97.9%id | 空闲CPU百分比 |
0.0%wa | 等待输入输出的CPU时间百分比 |
0.1%hi | 硬中断(Hardware IRQ)占用CPU的百分比 |
0.0%si | 软中断(Software IRQ)占用CPU的百分比 |
0.0%st | (Steal time) 是 hypervisor 等虚拟服务中,虚拟 CPU 等待实际 CPU 的时间的百分比 |
CPU 利用率是对一个时间段内 CPU 使用状况的统计,通过这个指标可以看出在某一个时间段内 CPU 被占用的情况,而 Load Average 是 CPU 的 Load,它所包含的信息不是 CPU 的使用率状况,而是在一段时间内 CPU 正在处理以及等待 CPU 处理的进程数情况统计信息,这两个指标并不一样。
来看 top 的第四行数据,这一行基本上是内存的一个使用情况的统计了:
内容 | 解释 |
8176740 total | 物理内存总量 |
8032104 used | 使用的物理内存总量 |
144636 free | 空闲内存总量 |
313088 buffers | 用作内核缓存的内存量 |
注意
系统中可用的物理内存最大值并不是 free 这个单一的值,而是 free + buffers + swap 中的 cached 的和
来看 top 的第五行数据,这一行基本上是交换区的一个使用情况的统计了
内容 | 解释 |
total | 交换区总量 |
used | 使用的交换区总量 |
free | 空闲交换区总量 |
cached | 缓冲的交换区总量,内存中的内容被换出到交换区,而后又被换入到内存,但使用过的交换区尚未被覆盖 |
再下面就是进程的一个情况了
列名 | 解释 |
PID | 进程id |
USER | 该进程的所属用户 |
PR | 该进程执行的优先级 priority 值 |
NI | 该进程的 nice 值 |
VIRT | 该进程任务所使用的虚拟内存的总数 |
RES | 该进程所使用的物理内存数,也称之为驻留内存数 |
SHR | 该进程共享内存的大小 |
S | 该进程进程的状态: S=sleep R=running Z=zombie |
%CPU | 该进程CPU的利用率 |
%MEM | 该进程内存的利用率 |
TIME+ | 该进程活跃的总时间 |
COMMAND | 该进程运行的名字 |
注意
NICE 值叫做静态优先级,是用户空间的一个优先级值,其取值范围是-20至19。这个值越小,表示进程”优先级”越高,而值越大“优先级”越低。nice值中的 -20 到 19,中 -20 优先级最高, 0 是默认的值,而 19 优先级最低
PR 值表示 Priority 值叫动态优先级,是进程在内核中实际的优先级值,进程优先级的取值范围是通过一个宏定义的,这个宏的名称是 MAX_PRIO,它的值为 140。Linux 实际上实现了 140 个优先级范围,取值范围是从 0-139,这个值越小,优先级越高。而这其中的 0 - 99 是实时进程的值,而 100 - 139 是给用户的。
其中 PR 中的 100 to 139 值部分有这么一个对应 PR = 20 + (-20 to +19),这里的 -20 to +19 便是nice值,所以说两个虽然都是优先级,而且有千丝万缕的关系,但是他们的值,他们的作用范围并不相同
** VIRT **任务所使用的虚拟内存的总数,其中包含所有的代码,数据,共享库和被换出 swap空间的页面等所占据空间的总数
在上文我们曾经说过 top 是一个前台程序,所以是一个可以交互的
常用交互命令 | 解释 |
q | 退出程序 |
I | 切换显示平均负载和启动时间的信息 |
P | 根据CPU使用百分比大小进行排序 |
M | 根据驻留内存大小进行排序 |
i | 忽略闲置和僵死的进程,这是一个开关式命令 |
k | 终止一个进程,系统提示输入 PID 及发送的信号值。一般终止进程用 15 信号,不能正常结束则使用 9 信号。安全模式下该命令被屏蔽。 |
好好的利用 top 能够很有效的帮助
17.7 ps 工具的使用
ps 也是我们最常用的查看进程的工具之一,我们通过这样的一个命令来了解一下,他能给我带来哪些信息
我们来总体了解下会出现哪些信息给我们,这些信息又代表着什么(更多的 keywords 大家可以通过 man ps 了解)
内容 | 解释 |
F | 进程的标志(process flags),当 flags 值为 1 则表示此子程序只是 fork 但没有执行 exec,为 4 表示此程序使用超级管理员 root 权限 |
USER | 进程的拥有用户 |
PID | 进程的 ID |
PPID | 其父进程的 PID |
SID | session 的 ID |
TPGID | 前台进程组的 ID |
%CPU | 进程占用的 CPU 百分比 |
%MEM | 占用内存的百分比 |
NI | 进程的 NICE 值 |
VSZ | 进程使用虚拟内存大小 |
RSS | 驻留内存中页的大小 |
TTY | 终端 ID |
S or STAT | 进程状态 |
WCHAN | 正在等待的进程资源 |
START | 启动进程的时间 |
TIME | 进程消耗CPU的时间 |
COMMAND | 命令的名称和参数 |
TPGID栏写着-1的都是没有控制终端的进程,也就是守护进程
STAT表示进程的状态,而进程的状态有很多,如下表所示
状态 | 解释 |
R | Running.运行中 |
S | Interruptible Sleep.等待调用 |
D | Uninterruptible Sleep.不可中断睡眠 |
T | Stoped.暂停或者跟踪状态 |
X | Dead.即将被撤销 |
Z | Zombie.僵尸进程 |
W | Paging.内存交换 |
N | 优先级低的进程 |
< | 优先级高的进程 |
s | 进程的领导者 |
L | 锁定状态 |
l | 多线程状态 |
+ | 前台进程 |
其中的 D 是不能被中断睡眠的状态,处在这种状态的进程不接受外来的任何 signal,所以无法使用 kill 命令杀掉处于D状态的进程,无论是 kill,kill -9 还是 kill -15,一般处于这种状态可能是进程 I/O 的时候出问题了。
ps 工具有许多的参数,下面给大家解释部分常用的参数
使用 -l 参数可以显示自己这次登陆的 bash 相关的进程信息罗列出来
ps -l
相对来说我们更加常用下面这个命令,他将会罗列出所有的进程信息
ps aux
若是查找其中的某个进程的话,我们还可以配合着 grep 和正则表达式一起使用
ps aux | grep zsh
此外我们还可以查看时,将连同部分的进程呈树状显示出来
ps axjf
当然如果你觉得使用这样的此时没有把你想要的信息放在一起,我们也可以是用这样的命令,来自定义我们所需要的参数显示
ps -afxo user,ppid,pid,pgid,command
17.8 pstree 工具的使用
通过 pstree 可以很直接的看到相同的进程数量,最主要的还是我们可以看到所有进程之间的相关性。
pstree
pstree -up #参数选择: #-A :各程序树之间以 ASCII 字元來連接; #-p :同时列出每个 process 的 PID; #-u :同时列出每个 process 的所屬账户名称。
17.9 进程的执行顺序
我们在使用 ps 命令的时候可以看到大部分的进程都是处于休眠的状态,如果这些进程都被唤醒,那么该谁最先享受 CPU 的服务,后面的进程又该是一个什么样的顺序呢?进程调度的队列又该如何去排列呢?
当然就是靠该进程的优先级值来判定进程调度的优先级,而优先级的值就是上文所提到的 PR 与 nice 来控制与体现了
而 nice 的值我们是可以通过 nice 命令来修改的,而需要注意的是 nice 值可以调整的范围是 -20 ~ 19,其中 root 有着至高无上的权力,既可以调整自己的进程也可以调整其他用户的程序,并且是所有的值都可以用,而普通用户只可以调制属于自己的进程,并且其使用的范围只能是 0 ~ 19,因为系统为了避免一般用户抢占系统资源而设置的一个限制
#这个实验在环境中无法做,因为权限不够,可以自己在本地尝试 #打开一个程序放在后台,或者用图形界面打开 nice -n -5 vim & #用 ps 查看其优先级 ps -afxo user,ppid,pid,stat,pri,ni,time,command | grep vim
我们还可以用 renice 来修改已经存在的进程的优先级,同样因为权限的原因在实验环境中无法尝试
renice -5 pid
18、Linux日志系统
18.1 常见的日志
在 Linux 中大部分的发行版都内置使用 syslog 系统日志,那么通过前期的课程我们了解到常见的日志一般存放在 /var/log 中,我们来看看其中有哪些日志
根据图中所显示的日志,我们可以根据服务对象粗略的将日志分为两类
- 系统日志
- 应用日志
系统日志主要是存放系统内置程序或系统内核之类的日志信息如 alternatives.log 、btmp 等等,应用日志主要是我们装的第三方应用所产生的日志如 tomcat7 、apache2 等等。
接下来我们来看看常见的系统日志有哪些,他们都记录了怎样的信息
日志名称 | 记录信息 |
alternatives.log | 系统的一些更新替代信息记录 |
apport.log | 应用程序崩溃信息记录 |
apt/history.log | 使用 apt-get 安装卸载软件的信息记录 |
apt/term.log | 使用 apt-get 时的具体操作,如 package 的下载、打开等 |
auth.log | 登录认证的信息记录 |
boot.log | 系统启动时的程序服务的日志信息 |
btmp | 错误的信息记录 |
Consolekit/history | 控制台的信息记录 |
dist-upgrade | dist-upgrade 这种更新方式的信息记录 |
dmesg | 启动时,显示屏幕上内核缓冲信息,与硬件有关的信息 |
dpkg.log | dpkg 命令管理包的日志。 |
faillog | 用户登录失败详细信息记录 |
fontconfig.log | 与字体配置有关的信息记录 |
kern.log | 内核产生的信息记录,在自己修改内核时有很大帮助 |
lastlog | 用户的最近信息记录 |
wtmp | 登录信息的记录。wtmp可以找出谁正在进入系统,谁使用命令显示这个文件或信息等 |
syslog | 系统信息记录 |
而在本实验环境中没有 apport.log 是因为 apport 这个应用程序需要读取一些内核的信息来收集判断其他应用程序的信息,从而记录应用程序的崩溃信息。而在本实验环境中我们没有这个权限,所以将 apport 从内置应用值剔除,自然而然就没有它的日志信息了。
在 apt 文件夹中的日志信息,其中有两个日志文件 history.log 与 term.log,
history.log 主要记录了进行了哪个操作,相关的依赖有哪些,
term.log 则是较为具体的一些操作,主要就是下载包,打开包,安装包等等的细节操作。
less /var/log/apt/history.log less /var/log/apt/term.log
然后我们来安装 git 这个程序,因为本实验环境中本有预装 git 所以这里真正执行的操作是一个更新的操作,但这并不影响
sudo apt-get install git
成功的执行之后我们再来查看两个日志的内容变化
其他的日志格式也都类似于之前我们所查看的日志,主要便是时间,操作。
而这其中有两个比较特殊的日志,其查看的方式比较与众不同,因为这两个日志并不是 ASCII 文件而是被编码成了二进制文件,所以我们并不能直接使用 less、cat、more 这样的工具来查看,这两个日志文件是 wtmp,lastlog
我们查看的方法是使用 last 与 lastlog 工具来提取其中的信息
18.2 配置的日志
这些日志是如何产生的?通过上面的例子我们可以看出大部分的日志信息似乎格式都很类似,并且都出现在这个文件夹中。
这样的实现可以通过两种方式:
- 一种是由软件开发商自己来自定义日志格式然后指定输出日志位置;
- 一种方式就是 Linux 提供的日志服务程序,而我们这里系统日志是通过 syslog 来实现,提供日志管理服务。
syslog 是一个系统日志记录程序,在早期的大部分 Linux 发行版都是内置 syslog,让其作为系统的默认日志收集工具,虽然随着时代的进步与发展,syslog 已经年老体衰跟不上时代的需求,所以他被 rsyslog 所代替了,较新的 Ubuntu、Fedora 等等都是默认使用 rsyslog 作为系统的日志收集工具
rsyslog的全称是 rocket-fast system for log,它提供了高性能,高安全功能和模块化设计。rsyslog 能够接受各种各样的来源,将其输入,输出的结果到不同的目的地。rsyslog 可以提供超过每秒一百万条消息给目标文件。
这样能实时收集日志信息的程序是有其守护进程的,如 rsyslog 的守护进程便是 rsyslogd
因为一些原因本实验环境中默认并没有打开这个服务,我们可以手动开启这项服务,然后来查看
sudo apt-get update sudo apt-get install -y rsyslog sudo service rsyslog start ps aux | grep syslog
既然它是一个服务,那么它便是可以配置,为我们提供一些我们自定义的服务
首先我们来看 rsyslog 的配置文件是什么样子的,而 rsyslog 的配置文件有两个,
- 一个是 /etc/rsyslog.conf
- 一个是 /etc/rsyslog.d/50-default.conf。
第一个主要是配置的环境,也就是 rsyslog 加载什么模块,文件的所属者等;而第二个主要是配置的 Filter Conditions
vim /etc/rsyslog.conf vim /etc/rsyslog.d/50-default.conf
也不知道他在写什么,我们还是来看看 rsyslog 的结构框架,数据流的走向吧。
通过这个简单的流程图我们可以知道 rsyslog 主要是由 Input、Output、Parser 这样三个模块构成的,并且了解到数据的简单走向,首先通过 Input module 来收集消息,然后将得到的消息传给 Parser module,通过分析模块的层层处理,将真正需要的消息传给 Output module,然后便输出至日志文件中。
上文提到过 rsyslog 号称可以提供超过每秒一百万条消息给目标文件,怎么只是这样简单的结构。我们可以通过下图来做更深入的了解
(图片来源于http://www.rsyslog.com/doc/queues_analogy.html)
Rsyslog 架构如图中所示,从图中我们可以很清楚的看见,rsyslog 还有一个核心的功能模块便是 Queue,也正是因为它才能做到如此高的并发。
第一个模块便是 Input,该模块的主要功能就是从各种各样的来源收集 messages,通过这些接口实现:
接口名 | 作用 |
im3195 | RFC3195 Input Module |
imfile | Text File Input Module |
imgssapi | GSSAPI Syslog Input Module |
imjournal | Systemd Journal Input Module |
imklog | Kernel Log Input Module |
imkmsg | /dev/kmsg Log Input Module |
impstats | Generate Periodic Statistics of Internal Counters |
imptcp | Plain TCP Syslog |
imrelp | RELP Input Module |
imsolaris | Solaris Input Module |
imtcp | TCP Syslog Input Module |
imudp | UDP Syslog Input Module |
imuxsock | Unix Socket Input |
而 Output 中也有许多可用的接口,可以通过 man 或者官方的文档查看
而这些模块接口的使用需要通过 $ModLoad 指令来加载,那么返回上文的图中,配置生效的头两行可以看懂了,默认加载了 imklog、imuxsock 这两个模块
在配置中 rsyslog 支持三种配置语法格式:
- sysklogd
- legacy rsyslog
- RainerScript
sysklogd 是老的简单格式,一些新的语法特性不支持。而 legacy rsyslog 是以 dollar 符($)开头的语法,在 v6 及以上的版本还在支持,就如上文所说的 $ModLoad 还有一些插件和特性只在此语法下支持。而以 $ 开头的指令是全局指令,全局指令是 rsyslogd 守护进程的配置指令,每行只能有一个指令。 RainnerScript 是最新的语法。在官网上 rsyslog 大多推荐这个语法格式来配置
老的语法格式(sysklogd & legacy rsyslog)是以行为单位。新的语法格式(RainnerScript)可以分割多行。
注释有两种语法:
- 井号 #
- C-style /* .. */
执行顺序: 指令在 rsyslog.conf 文件中是从上到下的顺序执行的。
模板是 rsyslog 一个重要的属性,它可以控制日志的格式,支持类似 template() 语句的基于 string 或 plugin 的模板,通过它我们可以自定义日志格式。
legacy 格式使用 $template 的语法,不过这个在以后要移除,所以最好使用新格式 template():,以免未来突然不工作了也不知道为什么
模板定义的形式有四种,适用于不同的输出模块,一般简单的格式,可以使用 string 的形式,复杂的格式,建议使用 list 的形式,使用 list 的形式,可以使用一些额外的属性字段(property statement)
如果不指定输出模板,rsyslog 会默认使用 RSYSLOG_DEFAULT。若想更深入的学习可以查看官方文档
了解了 rsyslog 环境的配置文件之后,我们看向 /etc/rsyslog.d/50-default.conf 这个配置文件,这个文件中主要是配置的 Filter Conditions,也就是我们在流程图中所看见的 Parser & Filter Engine,它的名字叫 Selectors 是过滤 syslog 的传统方法,他主要由两部分组成,facility 与 priority,其配置格式如下
facility.priority log_location
其中一个 priority 可以指定多个 facility,多个 facility 之间使用逗号 , 分割开
rsyslog 通过 Facility 的概念来定义日志消息的来源,以便对日志进行分类,Facility 的种类有:
类别 | 解释 |
kern | 内核消息 |
user | 用户信息 |
| 邮件系统消息 |
daemon | 系统服务消息 |
auth | 认证系统 |
authpriv | 权限系统 |
syslog | 日志系统自身消息 |
cron | 计划安排 |
news | 新闻信息 |
local0~7 | 由自定义程序使用 |
而另外一部分 priority 也称之为 serverity level,除了日志的来源以外,对统一源产生日志消息还需要进行优先级的划分,而优先级的类别有以下几种:
类别 | 解释 |
emergency | 系统已经无法使用了 |
alert | 必须立即处理的问题 |
critical | 很严重了 |
error | 错误 |
warning | 警告信息 |
notice | 系统正常,但是比较重要 |
informational | 正常 |
debug | debug的调试信息 |
panic | 很严重但是已淘汰不常用 |
none | 没有优先级,不记录任何日志消息 |
我们来看看系统中的配置
auth,authpriv.* /var/log/auth.log
这里的意思是 auth 与 authpriv 的所有优先级的信息全都输出于 /var/log/auth.log 日志中
而其中有类似于这样的配置信息意思有细微的差别
kern.* -/var/log/kern.log
- 代表异步写入,也就是日志写入时不需要等待系统缓存的同步,也就是日志还在内存中缓存也可以继续写入无需等待完全写入硬盘后再写入。通常用于写入数据比较大时使用。
到此我们对 rsyslog 的配置就有了一定的了解,若想更深入学习模板,队列的高级应用,大家可去查看官网的文档,需要注意的是 rsyslog 每个版本之间差异化比较大,学习之前先查看自己所使用的版本,再去查看相关的文档
与日志相关的还有一个还有常用的命令 logger,logger 是一个 shell 命令接口,可以通过该接口使用 Syslog 的系统日志模块,还可以从命令行直接向系统日志文件写入信息。
#首先将syslog启动起来 sudo service rsyslog start #向 syslog 写入数据 ping 127.0.0.1 | logger -it logger_test -p local3.notice & #查看是否有数据写入 sudo tail -f /var/log/syslog
从图中我们可以看到我们成功的将 ping 的信息写入了 syslog 中,格式也就是使用的 rsyslog 的默认模板
我们可以通过 man 来查看 logger 的其他用法,
参数 | 内容 |
-i | 在每行都记录进程 ID |
-t | 添加 tag 标签 |
-p | 设置日志的 facility 与 priority |
18.3 转储的日志
在本地的机器中每天都有成百上千条日志被写入文件中,更别说是我们的服务器,每天都会有数十兆甚至更多的日志信息被写入文件中,如果是这样的话,每天看着我们的日志文件不断的膨胀,那岂不是要占用许多的空间,所以有个叫 logrotate 的东西诞生了。
logrotate 程序是一个日志文件管理工具。用来把旧的日志文件删除,并创建新的日志文件。我们可以根据日志文件的大小,也可以根据其天数来切割日志、管理日志,这个过程又叫做“转储”。
大多数 Linux 发行版使用 logrotate 或 newsyslog 对日志进行管理。logrotate 程序不但可以压缩日志文件,减少存储空间,还可以将日志发送到指定 E-mail,方便管理员及时查看日志。
显而易见,logrotate 是基于 CRON 来运行的,其脚本是 /etc/cron.daily/logrotate;同时我们可以在 /etc/logrotate 中找到其配置文件
cat /etc/logrotate.conf
这其中的具体意思是什么呢?
# see "man logrotate" for details //可以查看帮助文档 # rotate log files weekly weekly //设置每周转储一次(daily、weekly、monthly当然可以使用这些参数每天、星期,月 ) # keep 4 weeks worth of backlogs rotate 4 //最多转储4次 # create new (empty) log files after rotating old ones create //当转储后文件不存在时创建它 # uncomment this if you want your log files compressed compress //通过gzip压缩方式转储(nocompress可以不压缩) # RPM packages drop log rotation information into this directory include /etc/logrotate.d //其他日志文件的转储方式配置文件,包含在该目录下 # no packages own wtmp -- we'll rotate them here /var/log/wtmp { //设置/var/log/wtmp日志文件的转储参数 monthly //每月转储 create 0664 root utmp //转储后文件不存在时创建它,文件所有者为root,所属组为utmp,对应的权限为0664 rotate 1 //最多转储一次 }
当然在 /etc/logrotate.d/ 中有各项应用的 logrotate 配置,还有更多的配置参数,大家可以使用 man 查看,如按文件大小转储,按当前时间格式命名等等参数配置。