leo_logic

liunx实验楼学习笔记(完结)
0代表假,1代表真只是针对机器语言标准输入(键盘输入) 对应文件描述符0标准输出(输出到屏幕)...
扫描右侧二维码阅读全文
25
2018/10

liunx实验楼学习笔记(完结)

0代表假,1代表真只是针对机器语言

  1. 标准输入(键盘输入) 对应文件描述符0
  2. 标准输出(输出到屏幕) 对应文件描述符1
  3. 标准错误(也是输出到屏幕) 对应文件描述符2

内建命令实际上是 shell 程序的一部分,其中包含的是一些比较简单的 Linux 系统命令,这些命令是写在bash源码的
builtins里面的,由 shell 程序识别并在 shell 程序内部完成运行,通常在 Linux 系统加载运行时 shell 就被加载并驻留> 在系统内存中。而且解析内部命令 shell 不需要创建子进程,因此其执行速度比外部命令快。比如:history、cd、exit 等。

外部命令是 Linux 系统中的实用程序部分,因为实用程序的功能通常都比较强大,所以其包含的程序量也会很大,在系统加载时> 并不随系统一起被加载到内存中,而是在需要时才将其调入内存。虽然其不包含在 shell 中,但是其命令执行过程是由 shell
程序控制的。外部命令是在 Bash 之外额外安装的,通常放在/bin,/usr/bin,/sbin,/usr/sbin等等。比如:ls、vi等。

一、学前认知
1.Linux 默认提供了 6 个纯命令行界面的 “terminal”(准确的说这里应该是 6 个 virtual consoles)来让用户登录。在物理机系统上你可以通过使用[Ctrl]+[Alt]+[F1]~[F6]进行切换
2.当切换到其中一个终端后想要切换回图形界面,可以按下[Ctrl]+[Alt]+[F7]来完成。
*本笔记来源于实验楼(非常好的编程学习平台,夏老师推荐)的课程,记录到个人博客方便后期查找复习。
二、命令学习

基本概念及操作

1、输入:输入当然就是打开终端,然后按键盘输入,然后按回车,输入格式一般就是这类的

#创建一个名为 file 的文件,touch是一个命令
touch file 
#进入一个目录,cd是一个命令
cd /etc/
#查看当前所在目录
pwd

2、输出:输出会返回你想要的结果,比如你要看什么文件,就会返回文件的内容。如果只是执行,执行失败会告诉你哪里错了,如果执行成功那么会没有输出,因为linux的哲学就是:没有结果就是最好的结果
3.相关快捷键
2) 重要的快捷键
①[Tab]
使用Tab键来进行命令补全,Tab键一般是在字母Q旁边,这个技巧给你带来的最大的好处就是当你忘记某个命令的全称时可以只输入它的开头的一部分,然后按下Tab键就可以得到提示或者帮助完成。
②[Ctrl+c]
可以使用Ctrl+c键来强行终止当前程序。
注意:虽然这个按着很方便,但不要随便按,因为有时候,当你看到终端没有任何反应或提示,也不能接受你的输入时,可能只是运行的程序需要你耐心等一下,就不要急着按Ctrl+c了
补充其他快捷键:
Ctrl+d 键盘输入结束或退出终端
Ctrl+S 暂停当前进程,按任意键恢复
Ctrl+z 将当前程序放到后台运行,恢复到前台为命令fg
Ctrl+a 将光标移至输入行头,相当于Home键
Ctrl+e 将光标移至输入行末,相当于End键
Ctrl+k 删除从光标所在位置到行末(删除光标后的部分)
Alt+Backspace 向前删除整行
Shift+PgUp 将终端显示向上滚动
Shift+PgDn 将终端显示向下滚动
2) 学会利用历史输入命令
很简单,你可以使用键盘上的方向上键↑,恢复你之前输入过的命令
3) 学会使用通配符,星号(*)和问号(?)
通配符是一种特殊语句,主要有星号(*)和问号(?),用来对字符串进行模糊匹配(比如文件名、参数名)。

touch asd.txt fgh.txt #创建两个文件为asd.txt fgh.txt的文件

忘记文件名可以使用通配符查询 ls *.txt#这里*号是提供通配符查询
4)一次性创建多个文件,比如:“love_1_linux.txt,love_2_linux.txt,... love_10_linux.txt”。

touch love_{1..10}_shiyanlou.txt

常见通配符:

  • 匹配 0 或多个字符 例如:ls *.txt
    ? 匹配任意一个字符 例如:ls leo?

[list] 匹配 list 中的任意单一字符
1 匹配 除list 中的任意单一字符以外的字符
[c1-c2] 匹配 c1-c2 中的任意单一字符 如:[0-9] [a-z]
{string1,string2,...} 匹配 string1 或 string2 (或更多)其一字符串
{c1..c2} 匹配 c1-c2 中全部字符 如{1..10}
5) 学会在命令行中获取帮助( man 手册内部就是使用的 less 来显示内容)
在 Linux 环境中,如果你遇到困难,可以使用man命令,它是Manual pages的缩写。
你可以使用如下方式来获得某个命令的说明和使用方式的详细介绍:

$ man <command_name>
区段    说明
1    一般命令
2    系统调用
3    库函数,涵盖了C标准函数库
4    特殊文件(通常是/dev中的设备)和驱动程序
5    文件格式和约定
6    游戏和屏保
7    杂项
8    系统管理命令和守护进程
man 1 ls
通常 man 手册中的内容很多,你可能不太容易找到你想要的结果,不过幸运的是你可以在 man 中使用搜索/<你要搜索的关键字>,查找完毕后你可以使用n键切换到下一个关键字所在处,shift+n为上一个关键字所在处。使用Space(空格键)翻页,Enter(回车键)向下滚动一行,或者使用j,k(vim 编辑器的移动键)进行向前向后滚动一行。按下h键为显示使用帮助(因为 man 使用 less 作为阅读器,实为less工具的帮助),按下q退出。
$ ls --help #你知道某个命令的作用,只是想快速查看一些它的某个具体参数的作用,那么你可以使用--help参数

第一节小贴士:
(以下命令)

$ sudo apt-get update
$ sudo apt-get install sysvbanner
$ banner leo   #会输出Leo对应的图像画面
$ printerbanner -w 50 A  #w可以改变宽度

6) (补充)在Ubuntu上安装MySQL,在线安装。

#安装 MySQL 服务端、核心程序
sudo apt-get install mysql-server
#安装 MySQL 客户端
sudo apt-get install mysql-client 

检查安装是否启动:

sudo netstat -tap | grep mysql 

根据自己的需求,用 gedit 修改 MySQL 的配置文件(my.cnf),使用以下命令:

sudo gedit /etc/mysql/my.cnf 

用户及文件权限管理

1)查看当前用户是?

$ who am i
或者
$ who mom likes

shiyanlou:~/ $ who am i                                              [18:20:01]
shiyanlou pts/0        2018-10-25 18:19 (:1.0)
shiyanlou:~/ $ who mom likes                                         [18:20:06]
shiyanlou pts/0        2018-10-25 18:19 (:1.0)
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)在 Linux 系统里, root 账户拥有整个系统至高无上的权利,比如 新建/添加 用户。
su,su- 与 sudo
su <user> 可以切换到用户 user,执行时需要输入目标用户的密码,sudo <cmd> 可以以特权级别运行 cmd 命令,需要当前用户属于 sudo 组,且需要输入当前用户的密码。这个sudo adduser lilei命令不但可以添加用户到系统,同时也会默认为新用户创建 home 目录:$ ls /home

shiyanlou:~/ $ sudo adduser lilei                                    [19:40:36]
\u6b63\u5728\u6dfb\u52a0\u7528\u6237"lilei"...
\u6b63\u5728\u6dfb\u52a0\u65b0\u7ec4"lilei" (1000)...
\u6b63\u5728\u6dfb\u52a0\u65b0\u7528\u6237"lilei" (1000) \u5230\u7ec4"lilei"...
\u521b\u5efa\u4e3b\u76ee\u5f55"/home/lilei"...
\u6b63\u5728\u4ece"/etc/skel"\u590d\u5236\u6587\u4ef6...
\u8f93\u5165\u65b0\u7684 UNIX \u5bc6\u7801\uff1a 
\u91cd\u65b0\u8f93\u5165\u65b0\u7684 UNIX \u5bc6\u7801\uff1a 
passwd\uff1a\u5df2\u6210\u529f\u66f4\u65b0\u5bc6\u7801
\u6b63\u5728\u6539\u53d8 lilei \u7684\u7528\u6237\u4fe1\u606f
\u8bf7\u8f93\u5165\u65b0\u503c\uff0c\u6216\u76f4\u63a5\u6572\u56de\u8f66\u952e\u4ee5\u4f7f\u7528\u9ed8\u8ba4\u503c
    \u5168\u540d []: leo
    \u623f\u95f4\u53f7\u7801 []: 1
    \u5de5\u4f5c\u7535\u8bdd []: 1
    \u5bb6\u5ead\u7535\u8bdd []: 1
    \u5176\u5b83 []: 1
\u8fd9\u4e9b\u4fe1\u606f\u662f\u5426\u6b63\u786e\uff1f [Y/n] y

可以使用你创建的用户登录了,使用如下命令切换登录用户:

$ su -l lilei

输入刚刚设置的 lilei 的密码,然后输入如下命令并查看输出:
$ who am i
$ whoami
$ pwd
3)查看所在用户组
方法一:使用 groups 命令

$ groups shiyanlou

方法二:查看 /etc/group 文件

$ cat /etc/group | sort
这里 cat 命令用于读取指定文件的内容并打印到终端输出,后面会详细讲它的使用。 | sort 表示将读取的文本进行一个字典排序再输出
$ cat /etc/group | grep -E "shiyanlou"  #使用命令过滤掉一些你不想看到的结果
shiyanlou:x:5000:   #该代码对应格式group_name:password:GID:user_list  提示:password 字段为一个 x 并不是说密码就是它,只是表示密码不可见而已。

将其它用户加入 sudo 用户组
默认情况下新创建的用户是不具有 root 权限的,也不在 sudo 用户组,可以让其加入 sudo 用户组从而获取 root 权限:

$ su -l lilei
$ sudo ls
$ su shiyanlou # 此处需要输入shiyanlou用户密码,点击右侧工具栏“SSH直连”,可以看到密码。
$ groups lilei
$ sudo usermod -G sudo lilei
$ groups lilei

删除用户是很简单的事:

$ sudo deluser lilei --remove-home

使用较长格式列出文件(文件类型与权限、链接数、文件属主(属主就是属于主人)、文件大小、建立或最近修改时间名字:

$ ls -l

TIM截图20181025205058.png

TIM截图20181025210059.png

4)Linux 里面一切皆文件,正因为这一点才有了设备文件

/dev 目录下有各种设备文件,大都跟具体的硬件设备相关
socket:网络套接字
软链接文件:链接文件是分为两种的,另一种当然是“硬链接”(硬链接不常用,具体内容不作为本课程讨论重点,而软链接等同于 Windows 上的快捷方式,记住这一点就够了)。
**一个目录同时具有读权限和执行权限才可以打开并查看内部文件,而一个目录要有写权限才允许在其中创建其它文件**,这是因为目录文件实际保存着该目录里面的文件的列表等信息。
链接数:链接到该文件所在的 inode 结点的文件名数目(关于这个概念涉及到 Linux 文件系统的相关概念知识,不在本课程的讨论范围,感兴趣的用户可以自己去了解)。
文件大小:以 inode 结点大小为单位来表示的文件大小,你可以给 ls 加上 -lh 参数来更直观的查看文件的大小。
明白了文件权限的一些概念,我们顺带补充一下关于 ls 命令的一些其它常用的用法:①显示除了 .(当前目录)和 ..(上一级目录)之外的所有文件,包括隐藏文件(Linux 下以 . 开头的文件为隐藏文件)。`ls -A`②当然,你可以同时使用 -A 和 -l 参数:ls -Al
查看某一个目录的完整属性,而不是显示目录里面的文件属性:`$ ls -dl <目录名>`
显示所有文件大小,并以普通人类能看懂的方式呈现:`ls -AsSh`
其中小 s 为显示文件大小,大 S 为按文件大小排序,若需要知道如何按其它方式排序,请使用“man”命令查询。

5)假设目前是 lilei 用户登录,新建一个文件,命名为 “ iphone6 ”:

$ cd /home/lilei
$ sudo touch iphone6
现在,换回到 shiyanlou 用户身份,使用以下命令变更文件所有者为 shiyanlou :
$ cd /home/lilei
$ ls iphone6
$ sudo chown shiyanlou iphone6
无论在什么目录下,输入 cd 就可以回到 /home/shiyanlou(主目录) 目录

方法一:
TIM截图20181026084901.png

然后修改权限:
$ chmod 600 iphone6

方法二:加减赋值操作

完成上述相同的效果,你可以:
$ chmod go-rw iphone6

6)adduser 和 useradd 的区别是什么?

答:useradd 只创建用户,创建完了用 passwd lilei 去设置新用户的密码。adduser 会创建用户,创建目录,创建密码(提示你设置),做这一系列的操作。其实 useradd、userdel 这类操作更像是一种命令,执行完了就返回。而 **adduser 更像是一种程序**,需要你输入、确定等一系列操作。

增加一个用户:

1.新增用户:sudo adduser loutest
2.创建文件: sudo touch /opt/forloutest
3.查看当前用户权限:ll /opt/forloutest #linux下命令“ll”是“ls -l"的别名,"ll"和“ls -l”的功能是一样的
4.sudo chmod g+w /opt/forloutest (chmod 修改文件权限,g 是group,表示组权限,w 表示可写;综上所述:当前所属用户的组对这个文件不具有写的权限,+号表示增加这个权限)

增加用户作业.png

Linux 目录结构及文件基本操作

学习目标:

Linux 的文件组织目录结构。(每个目录的大体内容、文件的属性)
相对路径和绝对路径。
对文件的移动、复制、重命名、编辑等操作。(touch,file,rm,mv 等基本命令)

1)liunx的结构是树形结构

FHS(英文:Filesystem Hierarchy Standard 中文:文件系统层次结构标准),多数 Linux 版本采用这种文件组织形式,FHS 定义了系统中每个区域的用途、所需要的最小构成的文件和目录同时还给出了例外处理与矛盾处理

FHS (FHS_2.3 标准文档)定义了两层规范:

第一层是, / 下面的各个目录应该要放什么文件数据,例如 /etc 应该放置设置文件,/bin 与 /sbin 则应该放置可执行文件等等
第二层则是针对 /usr 及 /var 这两个目录的子目录来定义。例如 /var/log 放置系统日志文件,/usr/share 放置共享数据等等。

liunx系统文件结构图

可使用一下命令查看liunx结构:

 tree /  #如果提示" command not found ",就先安装:`sudo apt-get update`和`sudo apt-get install tree`

目录定义为四种交互作用的形态,如下表所示:
wm (1).png
2)路径
使用 cd 命令可以切换目录,在 Linux 里面使用 . 表示当前目录,.. 表示上一级目录(注意,我们上一节介绍过的,以 . 开头的文件都是隐藏文件,所以这两个目录必然也是隐藏的,你可以使用 ls -a 命令查看隐藏文件)
①进入上一级目录:

$ cd ..

②进入你的 home 目录:

$ cd ~ 
# 或者 cd /home/<你的用户名> 

③使用 pwd 获取当前路径:

$ pwd

绝对路径

关于绝对路径,简单地说就是以根" / "目录为起点的完整路径,以你所要到的目录为终点,表现形式如: /usr/local/bin,表示根目录下的 usr 目录中的 local 目录中的 bin 目录。

相对路径

相对路径,也就是相对于你当前的目录的路径,相对路径是以当前目录 . 为起点,以你所要到的目录为终点,表现形式如: `usr/local/bin` (这里假设你当前目录为根目录)。你可能注意到,我们表示相对路径实际并没有加上表示当前目录的那个 `.` ,而是直接以目录名开头,因为这个 usr 目录为 / 目录下的子目录,是可以省略这个 `.` 的(以后会讲到一个类似不能省略的情况);如果是当前目录的上一级目录,则需要使用 `..` ,比如你当前目录为 home 目录,根目录就应该表示为 `../../` ,表示上一级目录( home 目录)的上一级目录( / 目录)。

演示代码:

# 绝对路径
$ cd /usr/local/bin
# 相对路径
$ cd ../../usr/local/bin

提示:在进行目录切换的过程中请多使用 Tab 键自动补全,可避免输入错误,连续按两次 Tab 可以显示全部候选结果。


3)新建空白文件:
使用 touch 命令创建空白文件,关于 touch 命令,其主要作用是来更改已有文件的时间戳的(比如,最近访问时间,最近修改时间),但其在不加任何参数的情况下,只指定一个文件名,则可以创建一个指定文件名的空白文件(不会覆盖已有同名文件)
创建名为 test 的空白文件,因为在其它目录没有权限,所以需要先 cd ~ 切换回用户的 /home/shiyanlou 目录:

$ cd ~
$ touch test

①新建目录:
使用 mkdir(make directories)命令可以创建一个空目录,也可同时指定创建目录的权限属性。
②创建名为“ mydir ”的空目录:

$ mkdir mydir

使用 -p 参数,同时创建父目录(如果不存在该父目录),如下我们同时创建一个多级目录(这在安装软件、配置安装路径时非常有用):

$ mkdir -p father/son/grandson  

后面的目录路径,以绝对路径的方式表示也是可以的。
3)复制:使用 cp(copy)命令复制一个文件到指定目录。
将之前创建的“ test ”文件复制到“ /home/shiyanlou/father/son/grandson ”目录中:

$ cp test father/son/grandson  #cp 文件名 要复制到的目标路径

有时会复制不成功,要成功复制目录需要加上 -r 或者 -R 参数,表示递归复制,就是说有点“株连九族”的意思: -r 递归处理,将指定目录下的文件与子目录一并处理。若源文件或目录的形态,不属于目录或符号链接,则一律视为普通文件处理

$ cp -r father family

4)删除(删除目录):
跟复制目录一样,要删除一个目录,也需要加上 -r 或 -R 参数:

$ mkdir Documents                #创建文件
$ mv file1 Documents             #移动file1文件到Documents文件夹
$ rm -r family                   #rm删除某个文件 -r命令属强制删除

5)mv_移动文件与重命名(mv 源目录文件 目的目录)
使用 mv(move or rename files)命令移动文件(剪切)。将文件“ file1 ”移动到 Documents 目录:

$ mkdir Documents
$ mv file1 Documents  

6)mv_重命名文件(mv 旧的文件名 新的文件名)
将文件“ file1 ”重命名为“ myfile ”:

mv file1 myfile

$ cd /home/shiyanlou/

要实现批量重命名,mv 命令就有点力不从心了,我们可以使用一个看起来更专业的命令 rename 来实现。不过它要用 perl 正则表达式来作为参数
# 使用通配符批量创建 5 个文件:
$ touch file{1..5}.txt
# 批量将这 5 个后缀为 .txt 的文本文件重命名为以 .c 为后缀的文件:
$ rename 's/\.txt/\.c/' *.txt
# 批量将这 5 个文件,文件名和后缀改为大写:
$ rename 'y/a-z/A-Z/' *.c
上面的命令,rename 是先使用第二个参数的通配符匹配所有后缀为 .txt 的文件,然后使用第一个参数提供的正则表达式将匹配的这些文件的 .txt 后缀替换为 .c

7)查看文件
使用 cat,tac 和 nl 命令查看文件
前两个命令都是用来打印文件内容到标准输出(终端),其中 cat 为正序显示,tac 为倒序显示。

标准输入输出:当我们执行一个 shell 命令行时通常会自动打开三个标准文件,即标准输入文件(stdin),默认对应终端的键盘、标准输出文件(stdout)和标准错误输出文件(stderr),后两个文件都对应被重定向到终端的屏幕,以便我们能直接看到输出内容。进程将从标准输入文件中得到输入数据,将正常输出数据输出到标准输出文件,而将错误信息送到标准错误文件中。

cat 文件名,加入-n可以显示行号。 cat -n passwd
  • nl 命令,添加行号并打印,这是个比 cat -n 更专业的行号打印命令,以下是它的常用的几个参数:

        -b : 指定添加行号的方式,主要有两种:
        -b a:表示无论是否为空行,同样列出行号("cat -n"就是这种方式)
        -b t:只列出非空行的编号并列出(默认为这种方式)
        -n : 设置行号的样式,主要有三种:
        -n ln:在行号字段最左端显示
        -n rn:在行号字段最右边显示,且不加 0
        -n rz:在行号字段最右边显示,且加 0
        -w : 行号字段占用的位数(默认为 6 位)
    

使用 more 和 less 命令分页查看文件(more 和 less 就是天生用来"阅读"一个文件的内容的;例子:man 手册内部就是使用的 less 来显示内容)

TIM截图20181026183523.png

打开后默认只显示一屏内容,终端底部显示当前阅读的进度。可以使用 Enter 键向下滚动一行,使用 Space 键向下滚动一屏,按下 h 显示帮助,q 退出。

  • 使用 head 和 tail 命令查看文件
    这两个命令,那些性子比较急的人应该会喜欢,因为它们一个是只查看文件的头几行(默认为 10 行,不足 10 行则显示全部)和尾几行。还是拿 passwd 文件举例,比如当我们想要查看最近新增加的用户,那么我们可以查看这个 /etc/passwd 文件,不过我们前面也看到了,这个文件里面一大堆乱糟糟的东西,看起来实在费神啊。因为系统新增加一个用户,会将用户的信息添加到 passwd 文件的最后,那么这时候我们就可以使用 tail 命令了:

    $ tail /etc/passwd

    甚至更直接的只看一行, 加上 -n 参数,后面紧跟行数:

    $ tail -n 1 /etc/passwd
    

TIM截图20181026184114.png

关于 tail 命令,不得不提的还有它一个很牛的参数 -f,这个参数可以实现不停地读取某个文件的内容并显示。这可以让我们动态查看日志,达到实时监视的目的。
8)查看文件类型
前面我提到过,在 Linux 中文件的类型不是根据文件后缀来判断的,我们通常使用 file 命令查看文件的类型:

 $file /bin/ls                                          [18:54:57]
/bin/ls: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=d0bc0fb9b3f60f72bbad3c5a1d24c9e2a1fde775, stripped
 #说明这是一个可执行文件,运行在 64 位平台,并使用了动态链接文件(共享库)

9)编辑文件

 在 Linux 下面编辑文件通常我们会直接使用专门的命令行编辑器比如(emacs,vim,nano),由于涉及 Linux 上的编辑器的内容比较多,且非常重要,故我们有一门单独的基础课专门介绍这中一个编辑器 vim 。
 实验楼的课程编辑者强烈建议我们先学习VIM编辑器

Linux 环境变量与文件查找

第一部分:环境变量
导入:全局变量图
全局变量.png
1.创建变量及赋值:使用 declare 命令创建一个变量名为 tmp 的变量:

declare tmp #不用declare预声明一个变量,直接即用即创建,这里只是告诉我们 declare 的作用,这在创建其它指定类型的变量(如数组)时会用到。
使用 = 号赋值运算符,将变量 tmp 赋值为 shiyanlou:
$ tmp=shiyanlou

2.读取变量的值:使用 echo 命令和 $ 符号($ 符号用于表示引用一个变量的值,初学者经常忘记输入):

$ echo $tmp

3.变量
变量是何物?变量是计算机中用于记录一个值(不一定是数值,也可以是字符或字符串)的符号,而这些符号将用于不同的运算处理中。
变量的命名规范是?和Java一样:变量名只能是英文字母、数字或者下划线,且不能以数字作为开头。
4.liunx变量分为三种:

①当前 Shell 进程私有用户自定义变量,如上面我们创建的 tmp 变量,只在当前 Shell 中有效。(set)
②Shell 本身内建的变量。(env)
③从自定义变量导出的环境变量。(export)

![wm.png][12]

①set:显示当前 Shell 所有变量,包括其内建环境变量(与 Shell 外观等相关),用户自定义变量及导出的环境变量。
 env:显示与当前用户相关的环境变量,还可以让命令在指定环境中运行。
 expor:显示从 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

全局和局部变量(为了与普通变量区分,通常我们习惯将环境变量名设为大写。)对比代码示例图:

![wm (1).png][13]

按变量的生存周期来划分,Linux 变量可分为两类:
①永久的:需要修改配置文件,变量永久生效;
②临时的:使用 export 命令行声明即可,变量在关闭 shell 时失效。
这里介绍两个重要文件 /etc/bashrc(有的 Linux 没有这个文件) 和 /etc/profile ,它们分别存放的是 shell 变量和环境变量。还有要注意区别的是每个用户目录下的一个隐藏文件:

.profile 可以用 ls -a 查看
cd /home/shiyanlou
ls -a 

这个 .profile 只对当前用户永久生效。而写在 /etc/profile 里面的是对所有用户永久生效,所以如果想要添加一个永久生效的环境变量,只需要打开 /etc/profile,在最后添加环境变量就好
Shell 是通过环境变量 PATH 来进行搜索的,熟悉 Windows 的用户可能知道 Windows 中的也是有这么一个 PATH 环境变量。

查看 PATH 环境变量的内容:

$ echo $PATH

简单创建创建一个打印程序:

1.以下创建一个 Shell 脚本文件:
$ gedit hello_shell.sh #①会进入编辑器

#!/bin/bash    #②本行前面的代码并非注释需要粘贴在编辑器中
for ((i=0; i<10; i++));do
    echo "hello shell"
done
exit 0

$ chmod 755 hello_shell.sh  #为文件添加可执行权限

$ ./hello_shell.sh  #执行脚本,就会看到10行hello shell

2.创建一个 C 语言“ hello world ”程序:
$ gedit hello_world.c ①同样会进入编辑器

#include <stdio.h>    ②将本段C语言代码粘贴到编辑器
int main(void)
{
    printf("hello world!\n");
    return 0;
}

gcc -o hello_world hello_world.c   #gcc 生成二进制文件默认具有可执行权限,不需要修改

添加自定义路径到path环境变量:

$ PATH=$PATH:/home/shiyanlou/mybin  #零时改变
echo "PATH=$PATH:/home/shiyanlou/mybin" >> .zshrc #永久改变 注意>>和>的区别,同为重定向到一个文件中,但>>不会覆盖原文件;>会覆盖目标文件

变量的修改有以下几种方式:

   变量设置方式                                   说明
${变量名#匹配字串}                   从头向后开始匹配,删除符合匹配字串的最短数据
${变量名##匹配字串}                   从头向后开始匹配,删除符合匹配字串的最长数据
${变量名%匹配字串}                   从尾向前开始匹配,删除符合匹配字串的最短数据
${变量名%%匹配字串}                   从尾向前开始匹配,删除符合匹配字串的最长数据
${变量名/旧的字串/新的字串}           将符合旧字串的第一个字串替换为新的字串
${变量名//旧的字串/新的字串}           将符合旧字串的全部字串替换为新的字串

比如要修改我们前面添加到 PATH 的环境变量。为了避免操作失误导致命令找不到,我们先将 PATH 赋值给一个新的自定义变量 path:

$ path=$PATH
$ echo $path
$ path=${path%/home/shiyanlou/mybin}
# 或使用通配符,*表示任意多个任意字符
$ path=${path%*/mybin}

变量删除:

可以使用 unset 命令删除一个环境变量
$ unset temp

第二部分:环境变量
搜索命令与搜索相关的命令常用的有 whereis,which,find 和 locate

  • whereis 简单快速

    $whereis who

    whereis 只能搜索二进制文件(-b),man帮助文件(-m)和源代码文件(-s)。如果想要获得更全面的搜索结果可以使用 locate 命令。

  • locate 快而全(注意,它不只是在 /bin 目录下查找,还会自动递归子目录进行查找。)
    通过“ /var/lib/mlocate/mlocate.db ”数据库查找,不过这个数据库也不是实时更新的,系统会使用定时任务每天自动执行 updatedb 命令更新一次,所以有时候你刚添加的文件,它可能会找不到,需要手动执行一次 updatedb 命令(在我们的环境中必须先执行一次该命令)。它可以用来查找指定目录下的不同文件类型,如查找 /etc 下所有以 sh 开头的文件:注意,它不只是在 /bin 目录下查找,还会自动递归子目录进行查找。
  • 查找 /usr/share/ 下所有 jpg 文件:

    $ locate /usr/share/\*.jpg  

    如果想只统计数目可以加上 -c 参数,-i 参数可以忽略大小写进行查找,whereis 的 -b、-m、-s 同样可以使用。

  • which 小而精
  1. 本身是 Shell 内建的一个命令,我们通常使用 which 来确定是否安装了某个指定的软件,因为它只从 PATH 环境变量指定的路径中去搜索命令:

    $ which man
    
  • find 精而细
  1. 应该是这几个命令中最强大的了,它不但可以通过文件类型、文件名进行查找而且可以根据文件的属性(如文件的时间戳,文件的权限等)进行搜索,这儿只能简单记录一下啦。

这条命令表示去 /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 还要新的文件名
wm.png

列出 home 目录中,当天(24 小时之内)有改动的文件:

$ find ~ -mtime 0

列出用户家目录下比 Code 文件夹新的文件:

$ find ~ -newer /home/shiyanlou/Code

Linux 文件打包与解压缩

文件打包与解压缩

文件后缀名     说明
*.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 程序压缩的文件

①zip压缩示例:

使用 zip 打包文件夹:
$ cd /home/shiyanlou
$ zip -r -q -o shiyanlou.zip /home/shiyanlou/Desktop
$ du -h shiyanlou.zip
$ file shiyanlou.zip

上面命令将目录 /home/shiyanlou/Desktop 打包成一个文件,并查看了打包后文件的大小和类型。第一行命令中,-r 参数表示递归打包包含子目录的全部内容-q 参数表示为安静模式,即不向屏幕输出信息-o,表示输出文件,需在其后紧跟打包输出文件名。后面使用 du 命令查看打包后文件的大小,Linux du命令是查看当前指定文件或目录(会递归显示子目录)占用磁盘空间大小,还是和df命令有一些区别的.
设置压缩级别为 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

这里添加了一个参数用于设置压缩级别 -[1-9],1 表示最快压缩但体积大,9 表示体积最小但耗时最久。最后那个 -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 命令,因为 Windows 系统与 Linux/Unix 在文本文件格式上的一些兼容问题,比如换行符(为不可见字符),在 Windows 为 CR+LF(Carriage-Return+Line-Feed:回车加换行),而在 Linux/Unix 上为 LF(换行),所以如果在不加处理的情况下,在 Linux 上编辑的文本,在 Windows 系统上打开可能看起来是没有换行的。如果你想让你在 Linux 创建的 zip 压缩文件在 Windows 上解压后没有任何问题,那么你还需要对命令做一些修改:

$ zip -r -l -o shiyanlou.zip /home/shiyanlou/Desktop

需要加上 -l 参数将 LF 转换为 CR+LF 来达到以上目的。
将 shiyanlou.zip 解压到当前目录:

$ unzip shiyanlou.zip

使用安静模式,将文件解压到指定目录:

$ unzip -q shiyanlou.zip -d ziptest

只查看压缩包文件

$ unzip -l shiyanlou.zip

注意: 使用 unzip 解压文件时我们同样应该注意兼容问题,不过这里我们关心的不再是上面的问题,而是中文编码的问题,通常 Windows 系统上面创建的压缩文件,如果有有包含中文的文档或以中文作为文件名的文件时默认会采用 GBK 或其它编码,而 Linux 上面默认使用的是 UTF-8 编码,如果不加任何处理,直接解压的话可能会出现中文乱码的问题(有时候它会自动帮你处理),为了解决这个问题,我们可以在解压时指定编码类型。
使用 -O(英文字母,大写 o)参数指定编码类型:

$ unzip -O GBK 中文压缩文件.zip
$ tar -cf shiyanlou.tar /home/shiyanlou/Desktop #创建一个tar
       # -c 表示创建一个 tar 包文件
       # -f 用于指定创建的文件名,注意文件名必须紧跟在 -f 参数之后,比如不能写成 tar -fc shiyanlou.tar
       # tar -f shiyanlou.tar -c ~
       # -v 参数以可视的的方式输出打包的文件
       # 上面这段tar代码会自动去掉表示绝对路径的 /,你也可以使用 -P 保留绝对路径符。 
$ mkdir leo
$ tar -xf leo.tar -C leo   # x是解压
$ tar -tf shiyanlou.tar    #只查看不解包文件 -t 参数

保留文件属性和跟随链接(符号链接或软链接),有时候我们使用 tar 备份文件当你在其他主机还原时希望保留文件的属性(-p 参数)和备份链接指向的源文件而不是链接本身(-h 参数):

$ tar -cphf etc.tar /etc

我们只需要在创建 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

Linux 文件系统操作与磁盘管理

1)查看磁盘和目录的容量
①使用 df 命令查看磁盘的容量

$ df #或df -h(推荐)

将一个文件系统的顶层目录挂到另一个文件系统的子目录上,使它们成为一个整体,称为挂载。把该子目录称为挂载点。
/dev/sda2 是对应着主机硬盘的分区,后面的数字表示分区号,数字前面的字母 a 表示第几块硬盘(也可能是可移动磁盘),你如果主机上有多块硬盘则可能还会出现 /dev/sdb,/dev/sdc 这些磁盘设备都会在 /dev 目录下以文件的存在形式。
接着你还会看到"1k-块"这个陌生的东西,它表示以磁盘块大小的方式显示容量,后面为相应的以块大小表示的已用和可用容量
将一个文件系统的顶层目录挂到另一个文件系统的子目录上,使它们成为一个整体,称为挂载,把该子目录称为挂载点
②使用 du 命令查看目录的容量

# 默认同样以 块 的大小展示
$ du    #$ du -h(推荐-h,加入-h方式更易读)

-d参数指定查看目录的深度
# 只查看1级目录的信息
$ du -h -d 0 ~
# 查看2级
$ du -h -d 1 ~
# 加上`-h`参数,以更易读的方式展示

dd也可以用在备份硬件的引导扇区、获取一定数量的随机数据或者空数据等任务中。dd程序也可以在复制时处理数据,例如转换字节序、或在 ASCII 与 EBCDIC 编码间互换。
dd的命令行语句与其他的 Linux 程序不同,因为它的命令行选项格式为选项=值,而不是更标准的--选项 值或-选项=值。dd默认从标准输入中读取,并写入到标准输出中,但可以用选项if(input file,输入文件)和of(output file,输出文件)改变。

# 输出到文件
$ 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
# 注
在打完了这个命令后,继续在终端打字,作为你的输入

将输出的英文字符转换为大写再写入文件:

$ dd if=/dev/stdin of=test bs=10 count=1 conv=ucase

从/dev/zero设备创建一个容量为 256M 的空文件:

$ dd if=/dev/zero of=virtual.img bs=1M count=256
$ du -h virtual.img

使用下面的命令来将我们的虚拟磁盘镜像格式化为ext4文件系统

$ sudo mkfs.ext4 virtual.img

Linux 支持哪些文件系统你可以输入

ls -l /lib/modules/$(uname -r)/kernel/fs
# 进入磁盘分区模式
$ sudo fdisk virtual.img
# 输入`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

Linux 的帮助命令

type 命令来区分命令是内建的还是外部的。

shiyanlou:~/ $ type exit                                            
exit is a shell builtin   #得到这样的结果说明是内建命令,正如上文所说内建命令都是在 bash 源码中的 builtins 的.def中
shiyanlou:~/ $ type vim   #得到这样的结果说明是外部命令,正如上文所说,外部命令在/usr/bin or /usr/sbin等等中

xxx is /usr/bin/xxx

vim is /usr/bin/vim

xxx is an alias for xx --xxx  #补充:若是得到alias的结果,说明该指令为命令别名所设定的名称;

1)help命令
help 命令是用于显示 shell 内建命令的简要帮助信息,help 命令只能用于显示内建命令的帮助信息
那外部命令怎么办呢?
外部命令可以设置参数为--help来显示帮助信息
2)man命令
比help详细,man没有内建与外部命令的区分
左上角显示“ LS (1)”,在这里,“ LS ”表示手册名称,而“(1)”表示该手册位于第一章节。
打开手册之后我们可以通过 pgup 与 pgdn 或者上下键来上下翻看,可以按 q 退出当前页面

章节数    说明
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特定的), 用来存放内核例行程序的文档。

2)info命令
man 和 info 就像两个集合,它们有一个交集部分,但与 man 相比,info 工具可显示更完整的 GNU 工具信息。

Linux 任务计划

1)crontab 命令从输入设备读取指令,并将其存放于 crontab 文件中,以供之后读取和执行。通常,crontab 储存的指令被守护进程激活,crond 为其守护进程,crond 常常在后台运行,每一分钟会检查一次是否有预定的作业需要执行。
①启动 rsyslog(特殊情况)

sudo apt-get install -y rsyslog
sudo service rsyslog start

②crontab 不被默认启动时(特殊情况),同时不能在后台由 upstart 来管理,所以需要我们来启动它

sudo cron -f &

2)crontab完整使用过程:
①添加命令:

crontab -e

第一次启动会出现这样一个画面,这是让我们选择编辑的工具,选择第二个基本的 vim 就可以了

img.png

而选择后我们会进入这样一个画面,这就是添加计划的地方了,与一般的配置文档相同,以#号开头的都是注释(具体用法:man crontab
下面的代码是每分钟我们会在/home/shiyanlou目录下创建一个以当前的年月日时分秒为名字的空白文件:

*/1 * * * * touch /home/shiyanlou/$(date +\%Y\%m\%d\%H\%M\%S) #添加成功后我们会得到最后一排 installing new crontab 的一个提示

注意 “ % ” 在 crontab 文件中,有结束命令行、换行、重定向的作用,前面加 ” ” 符号转义,否则,“ % ” 符号将执行其> 结束命令行或者换行的作用,并且其后的内容会被做为标准输入发送给前面的命令。

1135081468203483143-wm.png
可以通过这样的一个指令来查看我们添加了哪些任务

crontab -l 

360截图18430709108137150.png
通过以下2种方式来确定我们的 cron 是否成功的在后台启动,默默的帮我们做事

ps aux | grep cron
or
pgrep cron

360截图18430709363477.png
再使用ll命令查看创建的文件详情,会发现每隔1s系统就会创建一个空白文件
我们通过这样一个命令可以查看到执行任务命令之后在日志中的信息反馈

sudo tail -f /var/log/syslog

当我们并不需要这个任务的时候我们可以使用这么一个命令去删除任务

crontab -r

3)crontab的深入:
每个用户使用 crontab -e 添加计划任务,都会在 /var/spool/cron/crontabs 中添加一个该用户自己的任务文档,这样目的是为了隔离。
①系统级别的定时任务需要使用sudo命令,以 sudo 权限编辑 /etc/crontab 文件
cron 服务监测时间最小单位是分钟,所以 cron 会每分钟去读取一次 /etc/crontab(系统定时任务) 与 /var/spool/cron/crontabs(用户定时任务) 里面的內容。

每个目录的作用:

/etc/cron.daily,目录下的脚本会**每天执行一次**,在**每天的6点25分时运行**;
/etc/cron.hourly,目录下的脚本会**每个小时执行一次**,在每小时的17分钟时运行;
/etc/cron.monthly,目录下的脚本会每月执行一次,在每月1号的6点52分时运行;
/etc/cron.weekly,目录下的脚本会每周执行一次,在每周第七天的6点47分时运行;
系统默认执行时间可以根据需求进行修改。

liunx定时.png

命令执行顺序控制与管道

多条命令如何输入呢?难道要一条一条执行吗?
1)顺序执行多条命令

关键符号;
 sudo apt-get update;sudo apt-get install some-tool;some-tool
 which cowsay>/dev/null && cowsay -f head-in ohch~
 没有安装cowsay,你可以先执行一次上述命令,你会发现什么也没发生.再安装好之后你再执行一次上述命令,你也会发现一些惊喜。&&就是用来实现选择性执行的,它表示如果前面的命令执行结果.是表示终端输出的内容,而是表示命令执行状态的结果)返回0则执行后面的,否则不执行,你可以从$?环境变量获取上一次命令的返回结果

360截图17290430667884.png

JAVA语言里面&&表示逻辑与,而且还有一个||表示逻辑或,同样 Shell 也有一个||,它们的区别就在于,shell中的这两个符号除了也可用于表示逻辑与和或之外,就是可以实现这里的命令执行顺序的简单控制。

which cowsay>/dev/null || echo "cowsay has not been install, please run 'sudo apt-get install cowsay' to install"#本段代码使用||表示当cowsay不存在时,会输出||的所有部分。&&与||的功能刚好相反!

做一个流程控制图:
wm.png

2.1)管道预体验
管道是什么?管道是一种通信机制,通常用于进程间的通信(也可通过socket进行网络通信),它表现出来的形式就是将前面每一个进程的输出(stdout)直接作为下一个进程的输入(stdin)。
我们在使用一些过滤程序时经常会用到的就是匿名管道,在命令行中由|分隔符表示,|在前面的内容中我们已经多次使用到了。具名管道简单的说就是有名字的管道,通常只会在源程序中用到具名管道。

$ ls -al /etc | less #(| less命令加入后就可以在输出中任意位置查看)

2.2)cut命令打印每一段的某一字段
打印/etc/passwd文件中以:为分隔符的第1个字段和第6个字段分别表示用户名和其家目录:

$ cut /etc/passwd -d ':' -f 1,6

360截图1738040698141145.png
打印/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

2.3)grep命令是很强大的,也是相当常用的一个命令,它结合正则表达式可以实现很复杂却很高效的匹配和查找
grep命令的一般形式为:

grep [命令选项]... 用于匹配的表达式 [文件]...
$ grep -rnI "shiyanlou" ~ #-r 参数表示递归搜索子目录中的文件,-n表示打印匹配项行号,-I表示忽略二进制文件。这个命令显示出grep的强大
查看环境变量中以"yanlou"结尾的字符串
$ export | grep ".*yanlou$"

2.4)wc 命令用于统计并输出一个文件中单词字节的数目

# 行数
$ 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

默认为字典排序:

$ 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

2.5)uniq命令可以用于过滤或者输出重复行。
uniq命令可以用于过滤或者输出重复行。

-c 在输出行前面加上每行在输入文件中出现的次数。
-d 仅显示重复行。
-u 仅显示不重复的行。

我们可以使用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

简单文本处理

1)tr命令

tr [option]...SET1 [SET2]
选项    说明
-d            删除和set1匹配的字符,注意不是全词匹配也不是按字符顺序匹配
-s            去除set1指定的在输入文本中连续并重复的字符

echo 'input some text here' | tr '[:lower:]' '[:upper:]'  #将小写转换为大写
echo 'input some text here' | tr '[a-z]' '[A-Z]'  #将小写转换为大写

echo 'input some text here' | tr '[:upper:]' '[:lower:]'  #将大写转换为小写
echo 'input some text here' | tr '[A-Z]' '[a-z]'  #将大写转换为小写

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

3)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

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

数据流重定向

1)简单重定向

> >>

重定向,将原本输出到标准输出的数据重定向到一个文件中,因为标准输出(/dev/stdout)本身也是一个文件,我们将命令输出导向另一个文件自然也是没有任何问题的。

$ echo 'hello shiyanlou' > redirect 
$ echo 'www.shiyanlou.com' >> redirect
$ cat redirect

当然前面没有用到的<和<<操作也是没有问题的,如你理解的一样,它们的区别在于重定向的方向不一致而已,>表示是从左到右,<右到左。
用于终端的显示和输出,分别为:
stdin(标准输入,对应于你在终端的输入)
stdout(标准输出,对应于终端的输出)
stderr(标准错误输出,对应于终端的输出)

文件描述符 设备文件 说明
0 /dev/stdin 标准输入
1 /dev/stdout 标准输出
2 /dev/stderr 标准错误

文件描述符:文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开
文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉
及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于 UNIX、Linux 这样的操作系统。

默认使用终端的标准输入作为命令的输入和标准输出作为命令的输出

$ 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 'hello shiyanlou' > redirect
$ cat redirect

初学者这里要注意不要将管道和重定向混淆,管道默认是连接前一个命令的输出到下一个命令的输入,而重定向通常是需要一个文件来建立两个命令的连接,你可以仔细体会一下上述第三个操作和最后两个操作的异同点。

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的文件中

3)使用 tee 同时重定向到多个文件夹
你可能还有这样的需求,除了需要将输出重定向到文件,也需要将信息打印在终端。那么你可以使用tee命令来实现:

$ echo 'hello shiyanlou' | tee hello

4)永久重定向
我们可以使用exec命令实现“永久”重定向。

# 先开启一个子 Shell
$ zsh
# 使用exec替换当前进程的重定向,将标准输出重定向到一个文件
$ exec 1>somefile
# 后面你执行的命令的输出都将被重定向到文件中,直到你退出当前子shell,或取消exec的重定向(后面将告诉你怎么做)
$ ls
$ exit
$ cat somefile

5)创建输出文件描述符:
在 Shell 中有9个文件描述符。
重新复习下前三个:

0代表假,1代表真只是针对机器语言

  1. 标准输入(键盘输入) 对应文件描述符0
  2. 标准输出(输出到屏幕) 对应文件描述符1
  3. 标准错误(也是输出到屏幕) 对应文件描述符2
    cd /dev/fd/;ls -Al #这条命令可以查看当前 Shell 进程中打开的文件描述符
    同样使用exec命令可以创建新的文件描述符:
    $ zsh
    $ exec 3>somefile

    先进入目录,再查看,否则你可能不能得到正确的结果,然后再回到上一次的目录

    $ cd /dev/fd/;ls -Al;cd -

    注意下面的命令>与&之间不应该有空格,如果有空格则会出错

    $ echo "this is test" >&3
    $ cat somefile
    $ exit
    如上面我们打开的3号文件描述符,可以使用如下操作将它关闭:

$ exec 3>&-
$ cd /dev/fd;ls -Al;cd -
6)完全的屏蔽命令的输出
在类 UNIX 系统中,/dev/null,或称空设备,是一个特殊的设备文件,它通常被用于丢弃不需要的输出流,或作为用于输入流的空文件,这些操作通常由重定向完成。读取它则会立即得到一个EOF。
我们可以利用设个/dev/null屏蔽命令的输出:

$ cat Documents/test.c nefile 1>/dev/null 2>&1 #这个操作我们将不会得到任何结果。

7)使用xargs分割参数列表
xargs 是一条 UNIX 和类 UNIX 操作系统的常用命令。它的作用是将参数列表转换成小块分段传递给其他命令,以避免参数列表过长的问题。这个命令在有些时候十分有用,特别是当用来处理产生大量输出结果的命令如 find,locate 和 grep 的结果,详细用法请参看 man 文档。

$ cut -d: -f1 < /etc/passwd | sort | xargs echo #-d(指定字段的分隔符) -f1(f代表显示指定字段的内容)

挑战:

wget http://labfile.oss.aliyuncs.com/courses/1/data1

里面记录是一些命令的记录,现在需要你从里面找出出现频率次数前3的命令并保存在/home/shiyanlou/result。

cat data1 | cut -c 8- | sort | uniq -dc | head -n -3  >  /home/shiyanlou/result
#cut -c:以字符为单位进行分割 
#uniq -c或--count 在每列旁边显示该行重复出现的次数
#uniq -d或--repeated 仅显示重复出现的行列。
#head -n -k /etc/passwd 除最后k行外,显示剩余所有内容。

正则表达式

正则表达式,又称正规表示式、正规表示法、正规表达式、规则表达式、常规表示法(英语:Regular Expression,在代
码中常简写为 regex、regexp 或 RE),计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列符
合某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些符合某个模式的文本。

许多程序设计语言都支持利用正则表达式进行字符串操作。例如,在 Perl 中就内建了一个功能强大的正则表达式引擎。正> 则表达式这个概念最初是由 UNIX 中的工具软件(例如sed和grep)普及开的。正则表达式通常缩写成“regex”,单数有
regexp、regex,复数有 regexps、regexes、regexen。

简单的说形式和功能上正则表达式和我们前面讲的通配符很像,不过它们之间又有很大差别,特别在于一些特殊的匹配字符的含义上

假设我们有这样一个文本文件,包含"shiyanlou",和"shilouyan"这两个字符串,同样一个表达式:

shi*

如果这作为一个正则表达式,它将只能匹配 shi,而如果不是作为正则表达式作为一个通配符,则将同时匹配这两个字符串。这是为什么呢?因为在正则表达式中表示匹配前面的子表达式(这里就是它前面一个字符)零次或多次,比如它可以匹配"sh","shii","shish","shiishi"等等,而作为通配符表示匹配通配符后面任意多个任意字符,所以它可以匹配"shiyanlou",和"shilouyan"两个字符。

1)基本语法:一个正则表达式通常被称为一个模式(pattern),为用来描述或者匹配一系列符合某个句法规则的字符串。
①选择
|竖直分隔符表示选择,例如"boy|girl"可以匹配"boy"或者"girl"
②数量限定
数量限定除了我们举例用的*,还有+加号,?问号,如果在一个模式中不加数量限定符则表示出现一次且仅出现一次:

+表示前面的字符必须出现至少一次(1次或多次),例如,"goo+gle",可以匹配"gooogle","goooogle"等;
?表示前面的字符最多出现一次(0次或1次),例如,"colou?r",可以匹配"color"或者"colour";
*星号代表前面的字符可以不出现,也可以出现一次或者多次(0次、或1次、或多次),例如,“0*42”可以匹配42、042、0042、00042等。

③范围和优先级
()圆括号可以用来定义模式字符串的范围和优先级,这可以简单的理解为是否将括号内的模式串作为一个整体。例如,"gr(a|e)y"等价于"gray|grey",(这里体现了优先级,竖直分隔符用于选择a或者e而不是gra和ey),"(grand)?father"匹配father和grandfather(这里体验了范围,?将圆括号内容作为一个整体匹配)。
⑤语法(部分)
正则表达式有多种不同的风格,下面列举一些常用的作为 PCRE 子集的适用于perl和python编程语言及grep或egrep的正则表达式匹配规则:(由于markdown表格解析的问题,下面的竖直分隔符用全角字符代替,实际使用时请换回半角字符)

PCRE(Perl Compatible Regular Expressions中文含义:perl语言兼容正则表达式)是一个用 C 语言编写的正则表达
式函数库,由菲利普.海泽(Philip Hazel)编写。PCRE是一个轻量级的函数库,比Boost 之类的正则表达式库小得多。
PCRE 十分易用,同时功能也很强大,性能超过了 POSIX 正则表达式库和一些经典的正则表达式库。

字符    描述
\    将下一个字符标记为一个特殊字符、或一个原义字符。例如,“n”匹配字符“n”。“\n”匹配一个换行符。序列“\\”匹配“\”而“\(”则匹配“(”。
^    匹配输入字符串的开始位置。
$    匹配输入字符串的结束位置。
{n}    n是一个非负整数。匹配确定的n次。例如,“o{2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o。
{n,}    n是一个非负整数。至少匹配n次。例如,“o{2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。“o{1,}”等价于“o+”。“o{0,}”则等价于“o*”。
{n,m}    m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{1,3}”将匹配“fooooood”中的前三个o。“o{0,1}”等价于“o?”。请注意在逗号和两个数之间不能有空格。
*    匹配前面的子表达式零次或多次。例如,zo*能匹配“z”、“zo”以及“zoo”。*等价于{0,}。
+    匹配前面的子表达式一次或多次。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{1,}。
?    匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“do”或“does”中的“do”。?等价于{0,1}。
?    当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串“oooo”,“o+?”将匹配单个“o”,而“o+”将匹配所有“o”。
.    匹配除“\n”之外的任何单个字符。要匹配包括“\n”在内的任何字符,请使用像“(.|\n)”的模式。
(pattern)    匹配pattern并获取这一匹配的子字符串。该子字符串用于向后引用。要匹配圆括号字符,请使用“\(”或“\)”。
x|y    匹配x或y。例如,“z|food”能匹配“z”或“food”。“(z|f)ood”则匹配“zood”或“food”。
[xyz]    字符集合(character class)。匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”。其中特殊字符仅有反斜线\保持特殊含义,用于转义字符。其它特殊字符如星号、加号、各种括号等均作为普通字符。脱字符^如果出现在首位则表示负值字符集合;如果出现在字符串中间就仅作为普通字符。连字符 - 如果出现在字符串中间表示字符范围描述;如果如果出现在首位则仅作为普通字符。
[^xyz]    排除型(negate)字符集合。匹配未列出的任意字符。例如,“[^abc]”可以匹配“plain”中的“plin”。
[a-z]    字符范围。匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”范围内的任意小写字母字符。
[^a-z]    排除型的字符范围。匹配任何不在指定范围内的任意字符。例如,“[^a-z]”可以匹配任何不在“a”到“z”范围内的任意字符。

优先级
优先级为从上到下从左到右,依次降低:

运算符    说明
\    转义符
(), (?:), (?=), []    括号和中括号
*、+、?、{n}、{n,}、{n,m}    限定符
^、$、\任何元字符    定位点和序列
|    选择

2)使用正则表达式:
2.1)使用基本正则表达式,BRE

  • 位置
    查找/etc/group文件中以"shiyanlou"为开头的行

    $ grep 'shiyanlou' /etc/group
    $ grep '^shiyanlou' /etc/group
  • 数量

    # 将匹配以'z'开头以'o'结尾的所有字符串
    $ echo 'zero\nzo\nzoo' | grep 'z.*o'
    # 将匹配以'z'开头以'o'结尾,中间包含一个任意字符的字符串
    $ echo 'zero\nzo\nzoo' | grep 'z.o'
    # 将匹配以'z'开头,以任意多个'o'结尾的字符串
    $ echo 'zero\nzo\nzoo' | grep 'zo*'

    选择

    # 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:]则不会有这个问题。

# 排除字符
$ $ echo 'geek\ngood' | grep '[^o]'

2.2)使用扩展正则表达式,ERE
要通过grep使用扩展正则表达式需要加上-E参数,或使用egrep。
2.2.1数量

# 只匹配"zo"
$ echo 'zero\nzo\nzoo' | grep -E 'zo{1}'
# 匹配以"zo"开头的所有单词
$ echo 'zero\nzo\nzoo' | grep -E 'zo{1,}'

注意:推荐掌握{n,m}即可,+,?,*,这几个不太直观,且容易弄混淆。
2.2.2选择

# 匹配"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'

注意:因为.号有特殊含义,所以需要转义。
关于正则表达式和grep命令的内容就介绍这么多,下面会介绍两个更强大的工具sed和awk,但同样也正是因为这两个工具的强大,我们的内容无法包含它们的全部,这里将只对基本内容作介绍。

  • 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

360截图17571113365661.png

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所有的操作都是基于pattern(模式)—action(动作)对来完成的,如下面的形式:

    $ pattern {action}

    你可以看到就如同很多编程语言一样,它将所有的动作操作用一对{}花括号包围起来。其中pattern通常是表示用于匹配输入的文本的“关系式”或“正则表达式”,action则是表示匹配后将执行的动作。在一个完整awk操作中,这两者可以只有其中一个,如果没有pattern则默认匹配输入的全部文本,如果没有action则默认为打印匹配内容到屏幕。

awk处理文本的方式,是将文本分割成一些“字段”,然后再对这些字段进行处理,默认情况下,awk以空格作为一个字段的分割符,不过这不是固定的,你可以任意指定分隔符,下面将告诉你如何做到这一点。

  • awk命令基本特点:awk [-F fs] [-v var=value] [-f prog-file | 'program text'] [file...]
    其中-F参数用于预先指定前面提到的字段分隔符(还有其他指定字段的方式) ,-v用于预先为awk程序指定变量,-f参数用于指定awk命令要执行的程序文件,或者在不加-f参数的情况下直接将程序语句放在这里,最后为awk需要处理的文本输入,且可以同时输入多个文本文件。现在我们还是直接来具体体验一下吧。
  • 使用awk将文本内容打印到终端

    # "quote>" 不用输入
    $ awk '{
    > print
    > }' test
    # 或者写到一行
    $ 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

    将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的时候应该尽可能将其作为一门程序语言来理解,这样将会使你学习起来更容易,所以初学阶段在练习awk时应该尽量按照我那样的方式分多行按照一般程序语言的换行和缩进来输入,而不是全部写到一行(当然这在你熟练了之后是没有任何问题的)。

  • awk常用内置变量

变量名 说明
FILENAME 当前输入文件名,若有多个文件,则只表示第一个。如果输入是来自标准输入,则为空字符串
$0 当前记录的内容
$N N表示字段号,最大值为NF变量的值
FS 字段分隔符,由正则表达式表示,默认为" "空格
RS 输入记录分隔符,默认为"n",即一行为一个记录
NF 当前记录字段数
NR 已经读入的记录数
FNR 当前输入文件的记录数,请注意它与NR的区别
OFS 输出字段分隔符,默认为" "空格
ORS 输出记录分隔符,默认为"n"

liunx下软件的安装

1.软件安装 sudo apt-get install w3m
1)四种安装方式:

在线安装
从磁盘安装deb软件包
从二进制软件包安装
从源代码编译安装

试想一下,平时我们在使用 Windows 的时候,想要安装一个软件,我们需要在网上去下载对应软件的安装包,接着安装的时候就是不断的去点击下一步,这些流程想必大家已经经历无数回了,但是在 Linux 下,一个命令加回车,等待一下,软件就安装好了,这就是方便的在线安装软件的方式。在学习这种安装方式之前有一点需要说明的是,在不同的linux发行版上面在线安装方式会有一些差异包括使用的命令及它们的包管理工具,因为我们的开发环境是基于ubuntu的,所以这里我们涉及的在线安装方式将只适用于ubuntu发行版,或其它基于ubuntu的发行版如国内的ubuntukylin(优麒麟),ubuntu又是基于debian的发行版,它使用的是debian的包管理工具dpkg,所以一些操作也适用与debian。而在一些采用其它包管理工具的发行版如redhat,centos,fedora等将不适用(redhat和centos使用rpm)。

$ sudo apt-get install w3m #安装一个名字叫w3m的软件 (w3m是一个建议的命令行浏览器)

如果在安装一个软件之后,无法立即使用Tab键补全这个命令,你可以尝试先执行source ~/.zshrc,然后你就可以使用补全操作。
2)apt包管理工具介绍:

APT是Advance Packaging Tool(高级包装工具)的缩写,是Debian及其派生发行版的软件包管理器,APT可以自动下载,配
置,安装二进制或者源代码格式的软件包,因此简化了Unix系统上管理软件的过程。APT最早被设计成dpkg的前端,用来处理deb
格式的软件包。现在经过APT-RPM组织修改,APT已经可以安装在支持RPM的系统管理RPM包。这个包管理器包含以 apt- 开头的多> 个工具,如 apt-get apt-cache apt-cdrom 等,在Debian系列的发行版中使用。

当你在执行安装操作时,首先apt-get 工具会在本地的一个数据库中搜索关于 w3m 软件的相关信息,并根据这些信息在相关的服务器上下载软件安装,这里大家可能会一个疑问:既然是在线安装软件,为啥会在本地的数据库中搜索?要解释这个问题就得提到几个名词了:

软件源镜像服务器
软件源

我们需要定期从服务器上下载一个软件包列表,使用 sudo apt-get update 命令来保持本地的软件包列表是最新的(有时你也需要手动执行这个操作,比如更换了软件源),而这个表里会有软件依赖信息的记录,对于软件依赖,我举个例子:我们安装 w3m 软件的时候,而这个软件需要 libgc1c2 这个软件包才能正常工作,这个时候 apt-get 在安装软件的时候会一并替我们安装了,以保证 w3m 能正常的工作。
3)apt-get常用参数
apt-get 是用于处理 apt包的公用程序集,我们可以用它来在线安装、卸载和升级软件包等,下面列出一些apt-get包含的常用的一些工具:
工具 说明
install 其后加上软件包名,用于安装一个软件包
update 从软件源镜像服务器上下载/更新用于更新本地软件源的软件包列表
upgrade 升级本地可更新的全部软件包,但存在依赖问题时将不会升级,通常会在更新之前执行一次update
dist-upgrade 解决依赖关系并升级(存在一定危险性)
remove 移除已安装的软件包,包括与被移除软件包有依赖关系的软件包,但不包含软件包的配置文件
autoremove 移除之前被其他软件包依赖,但现在不再被使用的软件包
purge 与remove相同,但会完全移除软件包,包含其配置文件
clean 移除下载到本地的已经安装的软件包,默认保存在/var/cache/apt/archives/
autoclean 移除已安装的软件的旧版本软件包
常用参数
参数 说明
-y 自动回应是否安装软件包的选项,在一些自动化安装脚本中使用这个参数将十分有用
-s 模拟安装
-q 静默安装方式,指定多个q或者-q=#,#表示数字,用于设定静默级别,这在你不想要在安装软件包时屏幕输出过多时很有用
-f 修复损坏的依赖关系
-d 只下载不安装
--reinstall 重新安装已经安装但可能存在问题的软件包
--install-suggests 同时安装APT给出的建议安装的软件包
关于安装,如前面演示的一样你只需要执行apt-get install <软件包名>即可,除了这一点,你还应该掌握的是如何重新安装软件包。 很多时候我们需要重新安装一个软件包,比如你的系统被破坏,或者一些错误的配置导致软件无法正常工作。

$ sudo apt-get --reinstall install w3m

2.软件升级

# 更新软件源
$ sudo apt-get update
# 升级没有依赖问题的软件包
$ sudo apt-get upgrade
# 升级并解决依赖关系
$ sudo apt-get dist-upgrade
  • 进程的概念:

    程序(procedure):不太精确地说,程序就是执行一系列有逻辑、有顺序结构的指令,帮我们达成某个结果。就如我们去餐馆,给服务员说我要牛肉盖浇饭,她执行了做牛肉盖浇饭这么一个程序,最后我们得到了这么一盘牛肉盖浇饭。它需要去执行,不然它就像一本武功秘籍,放在那里等人翻看。
    进程(process):进程是程序在一个数据集合上的一次执行过程,在早期的UNIX、Linux 2.4及更早的版本中,它是系统进行资源分配和调度的独立基本单位。同上一个例子,就如我们去了餐馆,给服务员说我要牛肉盖浇饭,她执行了做牛肉盖浇饭这么一个程序,而里面做饭的是一个进程,做牛肉汤汁的是一个进程,把牛肉汤汁与饭混合在一起的是一个进程,把饭端上桌的是一个进程。它就像是我们在看武功秘籍这么一个过程,然后一个篇章一个篇章地去练。
  • 进程有以下的特性:
    ·动态性:进程的实质是一次程序执行的过程,有创建、撤销等状态的变化。而程序是一个静态的实体。

·并发性:进程可以做到在一个时间段内,有多个程序在运行中。程序只是静态的实体,所以不存在并发性。
·独立性:进程可以独立分配资源,独立接受调度,独立地运行。
·异步性:进程以不可预知的速度向前推进。
·结构性:进程拥有代码段、数据段、PCB(进程控制块,进程存在的唯一标志)。也正是因为有结构性,进程才可以做到独立地运行。

并发:在一个时间段内,宏观来看有多个程序都在活动,有条不紊的执行(每一瞬间只有一个在执行,只是在一段时间有多个程序都执行过)
并行:在每一个瞬间,都有多个程序都在同时执行,这个必须有多个 CPU 才行

引入进程是因为传统意义上的程序已经不足以描述 OS 中各种活动之间的动态性、并发性、独立性还有相互制约性。程序就像一个公司,只是一些证书,文件的堆积(静态实体)。而当公司运作起来就有各个部门的区分,财务部,技术部,销售部等等,就像各个进程,各个部门之间可以独立运做,也可以有交互(独立性、并发性)。
而随着程序的发展越做越大,又会继续细分,从而引入了线程的概念,当代多数操作系统、Linux 2.6及更新的版本中,进程本身不是基本运行单位,而是线程的容器。就像上述所说的,每个部门又会细分为各个工作小组(线程),而工作小组需要的资源需要向上级(进程)申请。

线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。因为线程中几乎不包含系统资源,所以执行更快、更有效率。
简而言之,一个程序至少有一个进程,一个进程至少有一个线程。线程的划分尺度小于进程,使得多线程程序的并发性高。另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
  • 进程可以从两个角度来分:
    ①以进程的功能与服务的对象来分;

②以应用程序的服务类型来分;

  • 可以分为用户进程与系统进程:
    ①用户进程:通过执行用户程序、应用程序或称之为内核之外的系统程序而产生的进程,此类进程可以在用户的控制下运行或关闭。

②系统进程:通过执行系统内核程序而产生的进程,比如可以执行内存资源分配和进程切换等相对底层的工作;而且该进程的运行不受用户的干预,即使是 root 用户也不能干预系统进程的运行。

  • 第二角度来看,我们可以将进程分为交互进程、批处理进程、守护进程
    ①交互进程:由一个 shell 终端启动的进程,在执行过程中,需要与用户进行交互操作,可以运行于前台,也可以运行在后台。

②批处理进程:该进程是一个进程集合,负责按顺序启动其他的进程。
③守护进程:守护进程是一直运行的一种进程,在 Linux 系统启动时启动,在系统关闭时终止。它们独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件。例如 httpd 进程,一直处于运行状态,等待用户的访问。还有经常用的 cron(在 centOS 系列为 crond)进程,这个进程为 crontab 的守护进程,可以周期性的执行用户设定的某些任务。
进程有这么多的种类,那么进程之间定是有相关性的,而这些有关联性的进程又是如何产生的,如何衍生的?
就比如我们启动了终端,就是启动了一个 bash 进程,我们可以在 bash 中再输入 bash 则会再启动一个 bash 的进程,此时第二个 bash 进程就是由第一个 bash 进程创建出来的,他们之间又是个什么关系?
我们一般称呼第一个 bash 进程是第二 bash 进程的父进程,第二 bash 进程是第一个 bash 进程的子进程,这层关系是如何得来的呢?
关于父进程与子进程便会提及这两个系统调用 fork()exec()

①fork-exec是由 Dennis M. Ritchie 创造的
②fork() 是一个系统调用(system call),它的主要作用就是为当前的进程创建一个新的进程,这个新的进程就是它的子进程,这个子进程除了父进程的返回值和 PID 以外其他的都一模一样,如进程的执行代码段,内存信息,文件描述,寄存器状态等等
③exec() 也是系统调用,作用是切换子进程中的执行程序也就是替换其从父进程复制过来的代码段与数据段

子进程就是父进程通过系统调用 fork() 而产生的复制品,fork() 就是把父进程的 PCB 等进程的数据结构信息直接复制过来,只是修改了 PID,所以一模一样,只有在执行 exec() 之后才会不同,而早先的 fork() 比较消耗资源后来进化成 vfork(),效率高了不少,感兴趣的同学可以查查为什么。

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 还在,代表这个进程还存在(因为 PCB 就是进程存在的唯一标志,里面有 PID 等消息),并没有消亡,这样的进程称之为僵尸进程(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
  1. 为所有进程的父进程或者说是祖先进程

我们还可以使用这样一个命令来看,其中 pid 就是该进程的一个唯一编号,ppid 就是该进程的父进程的 pid,command 表示的是该进程通过执行什么样的命令或者脚本而产生的

 ps -fxo user,ppid,pid,pgid,command   

可以在图中看见我们执行的 ps 就是由 zsh 通过 fork-exec 创建的子进程而执行的
使用这样的一个命令我们也能清楚的看见 init 如上文所说是由进程 0 这个初始化进程来创建出来的子进程,而其他的进程基本是由 init 创建的子进程,或者是由它的子进程创建出来的子进程。所以 init 是用户进程的第一个进程也是所有用户进程的父进程或者祖先进程。(ps 命令将在后续课程详解)

  • 进程组和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)就是在终端中运行,但是你并不能与其任何的交互,也不会显示其执行的过程
  • 工具管理:
    bash(Bourne-Again shell)支持工作控制(job control),而 sh(Bourne shell)并不支持。
  • 并且每个终端或者说 bash 只能管理当前终端中的 job,不能管理其他终端中的 job。比如我当前存在两个 bash 分别为
  • bash1、bash2,bash1 只能管理其自己里面的 job 并不能管理 bash2 里面的 job
    我们都知道当一个进程在前台运作时我们可以用 ctrl + c 来终止它,但是若是在后台的话就不行了。
  • 通过 & 这个符号,让我们的命令在后台中运行

    ls &

图中所显示的 [1] 236分别是该 job 的 job number 与该进程的 PID,而最后一行的 Done 表示该命令已经在后台执行完毕。
我们还可以通过 ctrl + z 使我们的当前工作停止并丢到后台中去。
被停止并放置在后台的工作(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
  • liunx进程管理
    不管在测试的时候、在实际的生产环境中,还是自己的使用过程中,难免会遇到一些进程异常的情况,所以 Linux 为我们提供了一些工具来查看进程的状态信息。我们可以通过 top 实时的查看进程的状态,以及系统的一些信息(如 CPU、内存信息等),我们还可以通过 ps 来静态查看当前的进程信息,同时我们还可以使用 pstree 来查看当前活跃进程的树形结构。
  • top工具的使用:
  1. 工具是我们常用的一个查看工具,能实时的查看我们系统的一些关键信息的变化:

    top
    

liunx.png

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 中读取的。

  • 查看 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 (5HZ) / 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 能够很有效的帮助我们观察到系统的瓶颈所在,或者是系统的问题所在。

  • PStree工具的使用:
    通过 pstree 可以很直接的看到相同的进程数量,最主要的还是我们可以看到所有进程之间的相关性。
    pstree #无参数
    pstree -up 含-u -p参数

    参数选择:

    -A :各程序树之间以 ASCII 字元來連接;

    -p :同时列出每个 process 的 PID;

    -u :同时列出每个 process 的所屬账户名称。

  • Kill命令掌握:

首先我们使用图形界面打开了 gedit、gvim,用 ps 可以查看到

ps aux

使用9这个信号强制结束 gedit 进程

kill -9 1608

我们再查找这个进程的时候就找不到了

ps aux | grep gedit 
  • 进程的执行顺序:
    我们在使用 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

  • liunx日志系统
    日志是一个系统管理员,一个运维人员,甚至是开发人员不可或缺的东西,系统用久了偶尔也会出现一些错误,我们需要日志来给系统排错,在一些网络应用服务不能正常工作的时候,我们需要用日志来做问题定位,日志还是过往时间的记录本,我们可以通过它知道我们是否被不明用户登录过等等。

在 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 #系统信息记录

    ①alternatives.log 中的信息:

    update-alternatives 2016-07-02 13:36:16: run with --install /usr/bin/x-www-browser x-www-browser /usr/bin/google-chrome-stable 200
    update-alternatives 2016-07-02 13:36:16: run with --install /usr/bin/gnome-www-browser gnome-www-browser /usr/bin/google-chrome-stable 200
    update-alternatives 2016-07-02 13:36:16: run with --install /usr/bin/google-chrome google-chrome /usr/bin/google-chrome-stable 200

    我们可以从中得到的信息有程序作用,日期,命令,成功与否的返回码

    ②我们用这样的命令来看看 auth.log 中的信息

    less auth.log

    我们可以从中得到的信息有日期与 ip 地址的来源以及的用户与工具

    在 apt 文件夹中的日志信息,其中有两个日志文件 history.log 与 term.log,两个日志文件的区别在于 history.log 主要记录了进行了哪个操作,相关的依赖有哪些,而 term.log 则是较为具体的一些操作,主要就是下载包,打开包,安装包等等的细节操作。

    然后我们来安装 git 这个程序,因为本实验环境中本有预装 git 所以这里真正执行的操作是一个更新的操作,但这并不影响
    sudo apt-get install git
    其他的日志格式也都类似于之前我们所查看的日志,主要便是时间,操作。而这其中有两个比较特殊的日志,其查看的方式比较与众不同,因为这两个日志并不是 ASCII 文件而是被编码成了二进制文件,所以我们并不能直接使用 less、cat、more 这样的工具来查看,这两个日志文件是 wtmp,lastlog;我们查看的方法是使用 last 与 lastlog 工具来提取其中的信息

  • 配置日志:
    这些日志是如何产生的?通过上面的例子我们可以看出大部分的日志信息似乎格式都很类似,并且都出现在这个文件夹中。

实现可以通过两种方式:
·一种是由软件开发商自己来自定义日志格式然后指定输出日志位置;
·一种方式就是 Linux 提供的日志服务程序,而我们这里系统日志是通过 syslog 来实现,提供日志管理服务。
syslog 是一个系统日志记录程序,在早期的大部分 Linux 发行版都是内置 syslog,让其作为系统的默认日志收集工具,虽然时代的进步与发展,syslog 已经年老体衰跟不上时代的需求,所以他被 rsyslog 所代替了,较新的 Ubuntu、Fedora 等等都是默认使用 rsyslog 作为系统的日志收集工具
rsyslog的全称是 rocket-fast system for log,它提供了高性能,高安全功能和模块化设计。rsyslog 能够接受各种各样的来源,将其输入,输出的结果到不同的目的地。rsyslog 可以提供超过每秒一百万条消息给目标文件。
这样能实时收集日志信息的程序是有其守护进程的,如 rsyslog 的守护进程便是 rsyslogd
既然它是一个服务,那么它便是可以配置,为我们提供一些我们自定义的服务:
首先我们来看 rsyslog 的配置文件是什么样子的,而 rsyslog 的配置文件有两个,

一个是 /etc/rsyslog.conf
一个是 /etc/rsyslog.d/50-default.conf。

第一个主要是配置的环境,也就是 rsyslog 加载什么模块,文件的所属者等;而第二个主要是配置的 Filter Conditions
通过这个简单的流程图我们可以知道 rsyslog 主要是由 Input、Output、Parser 这样三个模块构成的,并且了解到数据的简单走向,首先通过 Input module 来收集消息,然后将得到的消息传给 Parser module,通过分析模块的层层处理,将真正需要的消息传给 Output module,然后便输出至日志文件中。

上文提到过 rsyslog 号称可以提供超过每秒一百万条消息给目标文件,怎么只是这样简单的结构。我们可以通过下图来做更深入的了解
第一个模块便是 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 #用户信息
    mail #邮件系统消息
    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

  • 代表异步写入,也就是日志写入时不需要等待系统缓存的同步,也就是日志还在内存中缓存也可以继续写入无需等待完全写入硬盘后再写入。通常用于写入数据比较大时使用。
  • 与日志相关的还有一个还有常用的命令 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
    我们可以通过 man 来查看 logger 的其他用法,
    参数 内容
    -i 在每行都记录进程 ID
    -t 添加 tag 标签
    -p 设置日志的 facility 与 priority
    在本地的机器中每天都有成百上千条日志被写入文件中,更别说是我们的服务器,每天都会有数十兆甚至更多的日志信息被写入文件中,如果是这样的话,每天看着我们的日志文件不断的膨胀,那岂不是要占用许多的空间,所以有个叫 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 查看,如按文件大小转储,按当前时间格式命名等等参数配置。


  1. list
Last modification:November 26th, 2018 at 07:40 am
If you think my article is useful to you, please feel free to appreciate

Leave a Comment