Linux · 文章

Linux操作系统:从历史到实践全面解析

这是很早之前第一次学习 Linux 系统时候留下的笔记了,曾经有一段时间找不到,我以为失踪了,没想到最近给我翻出来, 毕竟是来时路,留着当做一份记录也好。

一、简介

L 1

而Linux 主要涉及到是系统调用和内核这两层。

2.Linux历史

操作系统始于二十世纪五十年代,当时的操作系统能运行批处理程序。批处理程序不需要用户的交互,它从文件或者穿孔卡片读取数据,然后输出到另外一个文件或者打印机。

二十世纪六十年代初,交互式操作系统开始流行。它不仅仅可以交互,还能使多个用户从不同的终端同时操作主机。这样的操作系统被称作分时操作系统,它的出现对批处理操作系统是个极大的挑战。许多人尝试开发分时操作系统, 其中包括一些大学的研究项目和商业项目。当时有个项目叫做 Multics ,它的技术在当时很具有创新性。 Multics 项目的开发并不顺利,它花费了远超过预计的资金,却没有在操作系统市场上占到多少份额。而参加该项目的一个开发团体——贝尔实验室退出了这个项目。他们在退出后开发了他们自己的一个操作系统—— UNIX 。

UNIX 最初免费发布并因此在大学里受到欢迎。后来,UNIX 实现了 TCP/IP 协议栈,成为了早期工作站的操作系统的一个流行选择。

1990 年,UNIX 在服务器市场上尤其是大学校园中成为主流操作系统,许多校园都有 UNIX 主机,当然还包括一些研究它的计算机系的学生。这些学生都渴望能在自己的电脑上运行 UNIX 。不幸的是,从那时候开始,UNIX 开始变得商业化,它的价格也变得非常昂贵。而唯一低廉的选择就是 MINIX,这是一个功能有限的类似 UNIX 的操作系统,作者 Andrew Tanenbaum 开发它的目的是用于教学。

1991 年 10 月,Linus Torvalds(Linux 之父)在赫尔辛基大学接触 UNIX,他希望能在自己的电脑上运行一个类似的操作系统。可是 UNIX 的商业版本非常昂贵,于是他从 MINIX 开始入手,计划开发一个比 MINIX 性能更好的操作系统。很快他就开始了自己的开发工作。他第一次发行的版本迅速吸引了一些黑客。尽管最初的 Linux 并没有多少用处,但由于一些黑客的加入使它很快就具有了许多吸引人的特性,甚至一些对操作系统开发不感兴趣的人也开始关注它。

Linux 本身只是操作系统的内核。内核是使其它程序能够运行的基础。它实现了多任务和硬件管理,用户或者系统管理员交互运行的所有程序实际上都运行在内核之上。其中有些程序是必需的,比如说,命令行解释器(shell),它用于用户交互和编写 shell 脚本。 Linus 没有自己去开发这些应用程序,而是使用已有的自由软件。这减少了搭建开发环境所需花费的工作量。实际上,他经常改写内核,使得那些程序能够更容易地在 Linux 上运行。许多重要的软件,包括 C 编译器,都来自于自由软件基金 GNU 项目。GNU 项目开始于 1984 年,目的是为了开发一个完全类似于 UNIX 的免费操作系统。为了表扬 GNU 对 Linux 的贡献,许多人把 Linux 称为 GNU/Linux(GNU 有自己的内核)。

1992-1993 年,Linux 内核具备了挑战 UNIX 的所有本质特性,包括 TCP/IP 网络,图形界面系统(X window ),Linux 同样也吸引了许多行业的关注。一些小的公司开始开发和发行 Linux,有几十个 Linux 用户社区成立。1994 年,Linux 杂志也开始发行。

Linux 内核 1.0 在 1994 年 3 月发布,内核的发布要经历许多开发周期,直至达到一个稳定的版本。

下面列举一些 Linux 诞生大事件:

  • 1965 年,Bell 实验室、MIT、GE(通用电气公司)准备开发 Multics 系统,为了同时支持 300 个终端访问主机,但是 1969 年失败了;

    那时候并没有鼠标、键盘,输入设备,只有卡片机。因此,如果要测试某个程序,则需要将读卡纸插入卡片机,如果有错误,还需要重新来过;Multics:Multiplexed Information and Computing Service;

  • 1969 年,Ken Thompson(C 语言之父)利用汇编语言开发了 File Server System(Unics,即 UNIX 的原型);

    因为汇编语言对于硬件的依赖性,因此只能针对特定硬件; 只是为了移植一款“太空旅游”的游戏;

  • 1973 年,Dennis Ritchie 和 Ken Thompson 发明了 C 语言,而后写出了 UNIX 的内核;

    将 B 语言改成 C 语言,由此产生了 C 语言之父;90% 的代码是 C 语言写的,10% 的代码用汇编语言写的,因此移植时只要修改那 10% 的代码即可;

  • 1977 年,Berkeley 大学的 Bill Joy 针对他的机器修改了 UNIX 源码,称为 BSD(Berkeley Software Distribution);

    Bill Joy 是 Sun 公司的创始人;

  • 1979 年,UNIX 发布 System V,用于个人计算机;

  • 1984 年,因为 UNIX 规定“不能对学生提供源码”,Tanenbaum 老师自己编写兼容于 UNIX 的 Minix,用于教学;

  • 1984 年,Stallman 开始 GNU(GNU's Not Unix)项目,创办 FSF(Free Software Foundation)基金会;

    产品:GCC、Emacs、Bash Shell、GLIBC;倡导“自由软件”;GNU 的软件缺乏一个开放的平台运行,只能在 UNIX 上运行;自由软件指用户可以对软件做任何修改,甚至再发行,但是始终要挂着 GPL 的版权;自由软件是可以卖的,但是不能只卖软件,而是卖服务、手册等;

  • 1985 年,为了避免 GNU 开发的自由软件被其他人用作专利软件,因此创建 GPL(General Public License)版权声明;

  • 1988 年,MIT 为了开发 GUI,成立了研发 XFree86 的组织;

  • 1991 年,芬兰赫尔辛基大学的研究生 Linus Torvalds 基于 gcc、bash 开发了针对 386 机器的 Linux 内核;

  • 1994 年,Torvalds 发布 Linux-v1.0;

  • 1996 年,Torvalds 发布 Linux-v2.0,确定了 Linux 的吉祥物:企鹅。

UNIX 进化史(UNIX 大家族族谱 1969-2013):

1467262784463

大致的学习路径参考

L 2

二、操作环境

  • 学习/实验环境介绍

  • 基本命令

  • 通配符的使用

  • 查看帮助文档

1.学习/实操作环境

由于我是在实验楼进行学习,因此操作环境用的官方自带的Linux在线环境

环境基于实验楼目前使用的 XFCE 桌面。

1.1、终端的概念

常我们在使用 Linux 时,并不是直接与系统打交道,而是通过一个叫做 Shell 的中间程序来完成的,在图形界面下为了实现让我们在一个窗口中完成用户输入和显示输出,Linux 系统还提供了一个叫做终端模拟器的程序(Terminal)。 下面是几个比较常见的终端模拟器: gnome-terminal,Konsole,xterm,rxvt,kvt,nxterm 和 eterm 。目前我们的实验中的终端程序是 xfce 桌面环境自带的 xfce-terminal 。不过要注意的是这里所说的终端(Terminal)和控制台(Console)是有区别的。

终端本质上是对应着 Linux 上的 /dev/tty 设备,Linux 的多用户登录就是通过不同的 /dev/tty 设备完成的,Linux 默认提供了 6 个纯命令行界面的 “terminal”(准确的说这里应该是 6 个 virtual consoles)来让用户登录。在物理机系统上你可以通过使用[Ctrl]+[Alt]+[F1]~[F6]进行切换,不过在我们的在线实验环境中可能无法切换,因为特殊功能按键会被你的主机系统劫持。当你切换到其中一个终端后想要切换回图形界面,你可以按下**[Ctrl]+[Alt]+[F7]**来完成。

3.2、 Shell

通常在图形界面中对实际体验带来差异的不是上述的不同发行版的各种终端模拟器,**而是这个 Shell(壳)。有壳就有核,这里的核就是指 UNIX/Linux 内核,**Shell 是指“提供给使用者使用界面”的软件(命令解析器),类似于 DOS 下的 command(命令行)和后来的 cmd.exe 。普通意义上的 Shell 就是可以接受用户输入命令的程序。它之所以被称作 Shell 是因为它隐藏了操作系统底层的细节。同样的 UNIX/Linux 下的图形用户界面 GNOME 和 KDE,有时也被叫做“虚拟 shell”或“图形 shell”。

UNIX/Linux 操作系统下的 Shell 既是用户交互的界面,也是控制系统的脚本语言。当然这一点也有别于 Windows 下的命令行,虽然该命令行也提供了很简单的控制语句。在 Windows 操作系统下,有些用户从来都不会直接使用 Shell,然而在 UNIX 系列操作系统下,Shell 仍然是控制系统启动、X11 启动和很多其它实用工具的脚本解释程序。

在 UNIX/Linux 中比较流行的常见的 Shell 有 bash、zsh、ksh、csh 等等,Ubuntu 终端默认使用的是 bash,默认的桌面环境是 GNOME 或者 Unity(基于 GNOME),但我们的环境中使用的分别是 zsh 和 xfce。

3.3、基本操作命令

在 linux 中,最最重要的就是命令,这就包含了 2 个过程,输入和输出

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

  • 输出:输出会返回你想要的结果,比如你要看什么文件,就会返回文件的内容。如果只是执行,执行失败会告诉你哪里错了,如果执行成功那么会没有输出,因为 linux 的哲学就是:没有结果就是最好的结果

真正学习命令行之前,你先要掌握几个十分有用、必需掌握的小技巧:

[Tab]

使用Tab键来进行命令补全,Tab键一般是在字母Q旁边,这个技巧给你带来的最大的好处就是当你忘记某个命令的全称时可以只输入它的开头的一部分,然后按下Tab键就可以得到提示或者帮助完成:

[Ctrl+c]

想想你有没有遇到过这种情况,当你在 Linux 命令行中无意输入了一个不知道的命令,或者错误地使用了一个命令,导致在终端里出现了你无法预料的情况,比如,屏幕上只有光标在闪烁却无法继续输入命令,或者不停地输出一大堆你不想要的结果。你想要立即停止并恢复到你可控的状态,那该怎么办呢?这时候你就可以使用Ctrl+c键来强行终止当前程序(你可以放心它并不会使终端退出)。

比如当你输入tail命令或者find /命令的时候就会复现上述的情况,此时你需要做的说就是输入【Ctrl+c】来结束当前的尴尬局面。当也不是任何情况下都要按这个快捷键,比如系统正在后台执行某些任务或者服务请求的时候。

其他一些常用快捷键

具体情况得根据个人使用得键盘来确定,以下快捷键基于一般情况下得通用全键盘,比如我的72键得键盘就需要配合具体情况使用。

按键

作用

Ctrl+d

键盘输入结束或退出终端

Ctrl+s

暂停当前程序,暂停后按下任意键恢复运行

Ctrl+z

将当前程序放到后台运行,恢复到前台为命令fg

Ctrl+a

将光标移至输入行头,相当于Home

Ctrl+e

将光标移至输入行末,相当于End

Ctrl+k

删除从光标所在位置到行末

Alt+Backspace

向前删除一个单词

Shift+PgUp

将终端显示向上滚动

Shift+PgDn

将终端显示向下滚动

另外,可以使用键盘的方向键上使用历史命令

学会使用通配符

通配符是一种特殊语句,主要有星号(*)和问号(?),用来对字符串进行模糊匹配(比如文件名、参数名)。当查找文件夹时,可以使用它来代替一个或多个真正字符;当不知道真正字符或者懒得输入完整名字时,常常使用通配符代替一个或多个真正字符。

终端里面输入的通配符是由 Shell 处理的,不是由所涉及的命令语句处理的,它只会出现在命令的“参数值”里(它不能出现在命令名称里, 命令不记得,那就用Tab补全)。当 Shell 在“参数值”中遇到了通配符时,Shell 会将其当作路径或文件名在磁盘上搜寻可能的匹配:若符合要求的匹配存在,则进行代换(路径扩展);否则就将该通配符作为一个普通字符传递给“命令”,然后再由命令进行处理。总之,通配符实际上就是一种 Shell 实现的路径扩展功能。在通配符被处理后, Shell 会先完成该命令的重组,然后继续处理重组后的命令,直至执行该命令。

比如我想查看后缀为.txt的文件,可以这样写:ls *.txt【注意ls和*之间的空格】

在创建文件的时候,如果需要一次性创建多个文件,比如:“love_1_linux.txt,love_2_linux.txt,... love_10_linux.txt”。在 Linux 中十分方便:

touch love_{1..10}_linux.txt

Shell常用的通配符还有很多

字符

含义

*

匹配 0 或多个字符

?

匹配任意一个字符

[list]

匹配 list 中的任意单一字符

[^list]

匹配 除 list 中的任意单一字符以外的字符

[c1-c2]

匹配 c1-c2 中的任意单一字符 如:[0-9][a-z]

{string1,string2,...}

匹配 string1 或 string2 (或更多)其一字符串

{c1..c2}

匹配 c1-c2 中全部字符 如{1..10}

举个栗子:?和[c1-c2]与{string1,string2...}的例子

shell 1shell 2shell 3

其他通配符用法差不多。

获取命令帮助

在 Linux 环境中,如果你遇到困难,可以使用man命令,它是Manual pages的缩写。

man <command_name>

比如要查看man命令的用法,可以:

man man

man 手册的内容很多.涉及了 Linux 使用过程中的方方面面。为了便于查找,man 手册被进行了分册(分区段)处理,在 Research UNIX、BSD、OS X 和 Linux 中,手册通常被分为 8 个区段,安排如下:

区段

说明

1

一般命令

2

系统调用

3

库函数,涵盖了 C 标准函数库

4

特殊文件(通常是/dev 中的设备)和驱动程序

5

文件格式和约定

6

游戏和屏保

7

杂项

8

系统管理命令和守护进程

要查看相应区段的内容,就在 man 后面加上相应区段的数字即可,如:

man 1 ls

想要获得更详细的帮助,你还可以使用info命令,不过通常使用man就足够了。如果你知道某个命令的作用,只是想快速查看一些它的某个具体参数的作用,那么你可以使用--help参数,大部分命令都会带有这个参数,如:

ls --help

三、用户及文件权限管理

用户及文件权限管理

  • Linux 用户管理

  • Linux 权限管理


打开终端,输入命令:

who am i

或者

who mom likes

输出的第一列表示打开当前伪终端的用户的用户名(要查看当前登录用户的用户名,去掉空格直接使用 whoami 即可),第二列的 pts/0pts 表示伪终端,所谓伪是相对于 /dev/tty 设备而言的,还记得上一节讲终端时的那七个使用 [Ctrl]+[Alt]+[F1]~[F7] 进行切换的 /dev/tty 设备么,这是“真终端”,伪终端就是当你在图形用户界面使用 /dev/tty7 时每打开一个终端就会产生一个伪终端,pts/0 后面那个数字就表示打开的伪终端序号,你可以尝试再打开一个终端,然后在里面输入 who am i,看第二列是不是就变成 pts/1 了,第三列则表示当前伪终端的启动时间。

请打开终端,输入命令:

who am i

或者

who mom likes

输出的第一列表示打开当前伪终端的用户的用户名(要查看当前登录用户的用户名,去掉空格直接使用 whoami 即可),第二列的 pts/0pts 表示伪终端,所谓伪是相对于 /dev/tty 设备而言的,还记得上一节讲终端时的那七个使用 [Ctrl]+[Alt]+[F1]~[F7] 进行切换的 /dev/tty 设备么,这是“真终端”,伪终端就是当你在图形用户界面使用 /dev/tty7 时每打开一个终端就会产生一个伪终端,pts/0 后面那个数字就表示打开的伪终端序号,你可以尝试再打开一个终端,然后在里面输入 who am i,看第二列是不是就变成 pts/1 了,第三列则表示当前伪终端的启动时间。

who 命令其它常用参数

参数

说明

-a

打印能打印的全部

-d

打印死掉的进程

-m

am imom likes

-q

打印当前登录用户数及用户名

-u

打印当前登录用户登录信息

-r

打印运行等级

创建用户

  • root权限

    在 Linux 系统里, root 账户拥有整个系统至高无上的权限,比如新建和添加用户。root 权限,系统权限的一种,与 SYSTEM 权限可以理解成一个概念,但高于 Administrator 权限,root 是 Linux 和 UNIX 系统中的超级管理员用户帐户,该帐户拥有整个系统至高无上的权力,所有对象他都可以操作,所以很多黑客在入侵系统的时候,都要把权限提升到 root 权限,这个操作等同于在 Windows 下就是将新建的非法帐户添加到 Administrators 用户组。更比如安卓操作系统中(基于 Linux 内核)获得 root 权限之后就意味着已经获得了手机的最高权限,这时候你可以对手机中的任何文件(包括系统文件)执行所有增、删、改、查的操作。

    • su、su-、sudo命令

    需要注意 Linux 环境下输入密码是不会显示的

    su <user> 可以切换到用户 user,执行时需要输入目标用户的密码

    sudo <cmd> 可以以特权级别运行 cmd 命令,需要当前用户属于 sudo 组,且需要输入当前用户的密码。

    su - <user> 命令也是切换用户,但是同时用户的环境变量和工作目录也会跟着改变成目标用户所对应的。

    创建用户的指令

    sudo adduser lilei
    L09

    这个命令不但可以添加用户到系统,同时也会默认为新用户在 /home 目录下创建一个工作目录:

    ls /home

    现在你已经创建好一个用户,并且你可以使用你创建的用户登录了,使用如下命令切换登录用户:

    切换账户登录

    su -l lilei

    输入刚刚设置的 lilei 的密码,然后输入如下命令并查看输出:

    who am i
    whoami
    pwd

    这就是上一小节我们讲到的 who am iwhoami 命令的区别。

    退出当前用户跟退出终端一样,可以使用 exit 命令或者使用快捷键 Ctrl+D

用户组

在 Linux 里面每个用户都有一个归属(用户组),用户组简单地理解就是一组用户的集合,它们共享一些资源和权限

  • 如何查看自己的用户组方式一

groups lilei

其中冒号之前表示用户,后面表示该用户所属的用户组。这里可以看到 shiyanlou 用户属于 shiyanlou 用户组,每次新建用户如果不指定用户组的话,默认会自动创建一个与用户名相同的用户组

  • 如何查看自己的用户组:方式二

cat /etc/group | sort

这里 cat 命令用于读取指定文件的内容并打印到终端输出,后面会详细讲它的使用。 | sort 表示将读取的文本进行一个字典排序再输出,然后你将看到如下一堆输出,你可以在最下面看到 shiyanlou 的用户组信息:

使用grep命令过滤一些不相干的数据。

cat /etc/group | grep -E "shiyanlou"

/etc/group 的内容包括用户组(Group)、用户组口令、GID(组 ID) 及该用户组所包含的用户(User),每个用户组一条记录。格式如下:

group_name:password:GID:user_list

你看到上面的 password 字段为一个 x,并不是说密码就是它,只是表示密码不可见而已。

将其它用户加入 sudo 用户组

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

使用 usermod 命令可以为用户添加用户组,同样使用该命令你必需有 root 权限,你可以直接使用 root 用户为其它用户添加用户组,或者用其它已经在 sudo 用户组的用户使用 sudo 命令获取权限来执行该命令

使用 usermod 命令可以为用户添加用户组,同样使用该命令你必需有 root 权限,你可以直接使用 root 用户为其它用户添加用户组,或者用其它已经在 sudo 用户组的用户使用 sudo 命令获取权限来执行该命令

L09 1

可以看到,此时的lilei用户已经属于sudo用户组,可以使用root权限。

补充一下,在不同的Linux分支中,不一定都叫sudo,如果你用的是centos,那么应该这样usermod -a -G root xff

修改主机名或者用户颜色

删除用户和用户组

命令

sudo deluser lilei --remove-home

使用 --remove-home 参数在删除用户时候会一并将该用户的工作目录一并删除。如果不使用那么系统会自动在 /home 目录为该用户保留工作目录。

删除用户组可以使用 groupdel 命令,倘若该群组中仍包括某些用户,则必须先删除这些用户后,才能删除群组。

Linux文件权限

文件权限就是文件的访问控制权限,即哪些用户和组群可以访问文件以及可以执行什么样的操作。

Unix/Linux 系统是一个典型的多用户系统,不同的用户处于不同的地位,对文件和目录有不同的访问权限。为了保护系统的安全性,Unix/Linux 系统除了对用户权限作了严格的界定外,还在用户身份认证、访问控制、传输安全、文件读写权限等方面作了周密的控制。

在 Unix/Linux 中的每一个文件或目录都包含有访问权限,这些访问权限决定了谁能访问和如何访问这些文件和目录。

Linux 里面一切皆文件,正因为这一点才有了设备文件( /dev 目录下有各种设备文件,大都跟具体的硬件设备相关)这一说。 socket:网络套接字,具体是什么,感兴趣的用户可以去学习实验楼的后续相关课程。pipe 管道,这个东西很重要,我们以后将会讨论到,这里你先知道有它的存在即可。软链接文件:链接文件是分为两种的,另一种当然是“硬链接”(硬链接不常用,而软链接等同于 Windows 上的快捷方式)

  • 权限的解释

读权限,表示你可以使用 cat <file name> 之类的命令来读取某个文件的内容;写权限,表示你可以编辑和修改某个文件的内容;

执行权限,通常指可以运行的二进制程序文件或者脚本文件,如同 Windows 上的 exe 后缀的文件,不过 Linux 上不是通过文件后缀名来区分文件的类型。你需要注意的一点是,一个目录同时具有读权限和执行权限才可以打开并查看内部文件,而一个目录要有写权限才允许在其中创建其它文件,这是因为目录文件实际保存着该目录里面的文件的列表等信息。

所有者权限,这一点相信你应该明白了,至于所属用户组权限,是指你所在的用户组中的所有其它用户对于该文件的权限。

关于 ls 命令的一些其它常用的用法:

  • 显示除了 .(当前目录)和 ..(上一级目录)之外的所有文件,包括隐藏文件(Linux 下以 . 开头的文件为隐藏文件)。

ls -a

当然,你可以同时使用 -a-l 参数:

ls -al

查看某一个目录的完整属性,而不是显示目录里面的文件属性:

ls -dl <目录名>
  • 显示所有文件大小,并以普通人类能看懂的方式呈现:

ls -asSh
图片描述

其中小 s 为显示文件大小,大 S 为按文件大小排序,若需要知道如何按其它方式排序,可以使用 man ls 命令查询。

变更文件所有者

切换到lilei用户,然后在home/lilei目录新建一个文件,为iphone11,查看文件所有者

# 切换用户
su - lilei

创建文件

touch iphone11

查看所有者

ls -alh iphone11

6pAFn8QIklHusEi

可以看到,文件iPhone11的所有者是lilei,拥有者权限为rw,所属用户组权限为rw,其他用户权限为r,以及文件的创建时间等信息。

现在可以切换到root用户,变更该文件的所有者

# 切换为root用户
su root

切换到/home/lilei

cd home/lilei

查看文件

ls iphone11

变更文件所有者为root

sudo chown root iphone11

查看该文件所有者信息

ls -alh iphone11

T4fr9aEwjKYdop6

修改文件权限

如果你有一个自己的文件不想被其他用户读、写、执行,那么就需要对文件的权限做修改。文件的权限有两种表示方式:

  1. 二进制数字表示

  2. 加减赋值操作

二进制数字表示

XAERZKU4tFeIGw9

每个文件有三个固定的权限,分别对应拥有者、所属用户组、其他用户。这个顺序是固定不变的。文件的读写执行对应字母rwx,以二进制表示就是111,十进制为7.

比如上面插件的文件iphone11的权限是rw-rw-rw-,十进制表示为666,这就表示这个文件的拥有者、所属用户组、其他用户具有读写权限,不具有执行权限。

解释

上面说了,Linux中文件的基本权限一共就三种,即读写执行,对应的英文简写为r,w,x。在二进制中,0代表的含义更多的是false,1代表true。那么将所有的权限用这种方式进行标表示之后,有这个权限的那一位就是1,反之就可以用0来表示。

# 二进制形式表示权限
r--:100 #可读权限
-w-:010 #可写权限
--x:001 #可执行权限

转为八进制

既然可以用二进制表示,那么转为八进制之后又是什么样的?

可以知道:

100八进制为4对应r
010八进制为2对应w
001八进制为1对应x

那么如果将所有可组合的权限利用二进制表示并转八进制之后也不难推出。

rwx=111=7
rw-=110=6
r-w=101=5
r--=100=4
-wx=011=3
-w-=010=2
--x=001=1
---=000=0

基于上述

rwx = 4+2+1=7
rw = 4+2=6
rx = 4+1=5

可以看出,每一个数字都代表一种权限,最高权限为7,最低为0。

按照这种方式,如果将所有组的权限都表示出来的话,大概这样的:

-rw------- = 600 #因为rw是对于6的
-rw-rw-rw- = 666 同理
-rwxrwxrwx = 777 # 最高权限

知道了基本原理,快速计算出权限的数值表示就不是难事了

比如对于某个文件a,要求它的拥有者能进行读写,本组用户只能执行,其他用户只读。根据上面的方式,可以快速的推出a文件的权限组合为:614(-rw-x-r)

20220315160728

如果要把iphone11的权限改为只有我自己可以用那么就可以下面用这个方法更改它的权限。

  • 操作

# 添加文件内容
echo "echo "hello root"""> iphone11

修改权限

chmod 600 iphone11

查看权限

ls -ahl iphone11

lr7zIMiBTSLwoUb

可以看到,文件权限已经变更为-rw-------

此时我们再次尝试切换到用户lilei对文件进行读写操作

# 切换用户lilei
su lilei

读写文件iphone11

echo "hello lilei" >iphone11

z5jsNfBTbx8rH1m

可以看到,此时的lilei已经不能对该文件进行读写操作,将会提示无权限。

加减赋值操作

实现上面一样的功能,我们可以使用加减赋值的方式进行操作,也可以达到同样的效果。

# 修改文件权限:加减赋值法
chmod go-rw iphone11

g,o,u:分别表示用户组(group)、其他用户(others)、用户(user)的首字母。而其中的+-表示增加和去掉对应的权限。

上面的命令中go-rw的意思就是将该文件的用户组和其他用户的读写权限去(减)掉。

有个问题

adduseruseradd的区别是什么?

useradd只会创建用户,不会创建用户密码和工作目录,创建完之后需要使用passwd<usernme>去设置新用户的密码。

adduser在创建用户时,会提示创建工作目录和密码。

所以,诸如useradduserdel这类操作更像是一种命令,。执行完就返回,而adduser更像一种程序,需要你执行输入、确定等一系列操作。

扩展

Linux还有一些关于隐藏权限和特殊权限的内容。推荐以下两篇文章

来自RedHat的好文

CSDN里面的

四、目录结构及文件基本操作

目录结构以及文件基本操作:

  1. Linux 的文件组织目录结构。

  2. 相对路径和绝对路径。

  3. 对文件的移动、复制、重命名、编辑等操作

概述

在讲 Linux 目录结构之前,你首先要清楚一点,那就是 Linux 的目录与 Windows 的目录的区别,或许对于一般操作上的感受来说没有多大不同,但从它们的实现机制来说是完全不同的。

一种不同是体现在目录与存储介质(磁盘,内存,DVD 等)的关系上,以往的 Windows 一直是以存储介质为主的,主要以盘符(C 盘,D 盘...)及分区来实现文件管理,然后之下才是目录,目录就显得不是那么重要,除系统文件之外的用户文件放在任何地方任何目录也是没有多大关系。所以通常 Windows 在使用一段时间后,磁盘上面的文件目录会显得杂乱无章(少数善于整理的用户除外吧)。然而 UNIX/Linux 恰好相反,UNIX 是以目录为主的,Linux 也继承了这一优良特性。 Linux 是以树形目录结构的形式来构建整个系统的,可以理解为树形目录是一个用户可操作系统的骨架。虽然本质上无论是目录结构还是操作系统内核都是存储在磁盘上的,但从逻辑上来说 Linux 的磁盘是“挂在”(挂载在)目录上的,每一个目录不仅能使用本地磁盘分区的文件系统,也可以使用网络上的文件系统。举例来说,可以利用网络文件系统(Network File System,NFS)服务器载入某特定目录等

FHS标准

Linux 的目录结构说复杂很复杂,说简单也很简单。复杂在于,因为系统的正常运行是以目录结构为基础的,对于初学者来说里面大部分目录都不知道其作用,重要与否,特别对于那些曾经的重度 Windows 用户,他们会纠结很长时间,关于我安装的软件在哪里这类问题。说它简单是因为,其中大部分目录结构是规定好了的(FHS 标准),是死的,当你掌握后,你在里面的一切操作都会变得井然有序

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

FHS定义了两层规范:

第一层是/下面的各个目录应该要放什么文件数据,例如/etc下应该放置设置文件,/bin/sbin则应该放置可执行文件等等。

第二层是针对/usr以及/var这两个目录的子目录来定义。例如/var/log放置系统日志文件,/usr/share放置共享数据等等。

20220308161923

你也可以执行下面的命令,查看系统的目录结构树(这个过程会稍微有一定的长)

tree /
20220308163259

关于上面提到的 FHS,这里还有个很重要的内容你一定要明白,FHS 是根据以往无数 Linux 用户和开发者的经验总结出来的,并且会维持更新,FHS 依据文件系统使用的频繁与否以及是否允许用户随意改动(注意,不是不能,学习过程中,不要怕这些),将目录定义为四种交互作用的形态,如下表所示:

ECueT564M2acB9p

目录路径

使用 cd 命令可以切换目录,在 Linux 里面使用 . 表示当前目录,.. 表示上一级目录(注意,我们上一节介绍过的,以 . 开头的文件都是隐藏文件,所以这两个目录必然也是隐藏的,你可以使用 ls -a 命令查看隐藏文件),- 表示上一次所在目录, 通常表示当前用户的 home 目录。使用 pwd 命令可以获取当前所在路径(绝对路径)。

# 进入上级目录
cd ..

进入home目录(cd /home/<你的用户名>也行)

cd ~

进入上次所在的目录

cd -

IOZCEzFxbVMkUpu

绝对路径

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

相对路径

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

下面以你的 home 目录为起点,分别以绝对路径和相对路径的方式进入 /usr/local/bin 目录

# 绝对路径
cd /usr/local/bin

相对路径

cd ../../usr/local/bin

PwmHeJIjS7zKRpA

进入一个目录,可以使用绝对路径也可以使用相对路径,那我们应该在什么时候选择正确的方式进入某个目录呢。就是凭直觉嘛,你觉得怎样方便就使用哪一个,而不用特意只使用某一种。比如假设我当前在 /usr/local/bin 目录,我想进入上一级的 local 目录你说是使用 cd .. 方便还是 cd /usr/local 方便?而如果要进入的是 usr 目录,那么 cd /usr ,就比 cd ../.. 方便一点了。

文件基本操作

新建、复制、删除、移动文件与文件重命名、查看文件、查看文件类型、以及编辑文件。

新建空白文件

使用touch命令创建空白文件,该命令主要作用是用来更改已有文件的时间戳(eg:最近访问时间、最近修改时间),但是不加任何参数的情况下,只指定一个文件名,则可以创建一个指定文件名的空白文件(不会覆盖已有同名文件)。

# 创建名为test的空白文件
touch test
wgMZOlBkWC5oy2d

新建目录

使用mkdir命令可以创建一个空目录,也可以同时指定创建目录的权限属性。

mkdir mymkdirs

使用-p参数,可以同时创建父目录(如果该目录不存在的话),如下我们可以同时创建一个多级目录,这个技巧在配置安装软件路径的时候很有用。

mkdir -p father/son/grandson
z1yvUe3LZisbcmt

这里使用的路径是相对路径,代表在当前目录下生成,当然我们直接以绝对路径的方式表示也是可以的。

若当前目录已经创建了一个 test 文件,再使用 mkdir test 新建同名的文件夹,系统会报错文件已存在。这符合 Linux 一切皆文件的理念。

若当前目录存在一个 test 文件夹,则 touch 命令,则会更改该文件夹的时间戳而不是新建文件。

HJhRj7m9XGOpCzP

复制

复制文件

使用cp命令(copy)复制一个文件到指定目录。

将前面创建的test文件复制到grandson目录中

# 复制文件
cp test father/son/grandson
etXIgcy9lNqTKGm

这操作,可比图形界面快很多。

复制目录

注意力,复制目录可不能像上面复制文件一样直接cp xxx,否则会报错cp: -r not specified; omitting directory 'father'

正确的姿势应该是加上参数复制,加上r或者R参数,表示递归复制。

# 将文件夹father复制到family文件夹下
cp -r father family
TsIp7RNyftY9Wi4

删除

删除文件

删除一个文件使用rm命令

如果遇到想删除只读权限的文件时出现rm: remove write-protected regular empty file 'test'?提示,如果你想继续删除,可以使用rm -f 强制删除。

Mqmdj74s6rI1wbo

删除目录

如你所料,在删除一个目录时也需要-r或者-R参数的加持。

rm -r family

同样,遇到权限不足无法删除的情况也可以加上-f参数。

rm -rf family

移动文件

使用mv命令移动文件(剪切)。格式:mv 源目录文件 目的目录

将file移动到document

# 创建文件夹
mkdir document

创建文件

touch file1

移动文件

mv file1 document

WbuTGBciM9q6Ur2

重命名文件

mv命令还能给文件夹重命名,格式:mv 旧文件名 新文件名

将文件file1重命名为file2

mv file1 file2
pVa4GAyQqBSeoKm

批量重命名

使用rename来实现批量重命名操作。不过它需要使用Perl正则表达式(后面会安排上)来作为参数。

另外,rename 命令并不是内置命令,若提示无该命令可以使用 sudo apt-get install rename 命令自行安装。

# 修改后缀
rename 's/.txt/.c/' *.txt

另一种方式

rename '.txt' '.c' ./*.txt

rename 是先使用第二个参数的通配符匹配所有后缀为 .txt 的文件,然后使用第一个参数提供的正则表达式将匹配的这些文件的 .txt 后缀替换为 .c

查看文件-1

使用cat,tac,nl命令来查看文件信息。

前两个都是用来打印文件内容到标准输出,其中cat为正序显示,tac为倒序显示。

查看从/etc拷贝来的passwd文件

# 拷贝文件
cp /etc/passwd passwd

默认模式查看

cat passwd

带行号查看

cat -n passwd

SZgYODlvhacQx5yihUYV9Zr1DLc4Pm

nl 命令,添加行号并打印,这是个比 cat -n 更专业的行号打印命令。

一些常用参数:

6fvSLs4Dp2E85Ga

查看文件-2

想必已经遇到过这种情况,命令窗口有点内容过多一页无法全部展示的情况下,只能通过鼠标滚轮进行翻阅,但是下面的命令将会让你抛弃这种方式。

使用moreless命令分页查看文件

moreless 就是天生用来"阅读"一个文件的内容的,比如说 man 手册内部就是使用的 less 来显示内容。其中 more 命令比较简单,只能向一个方向滚动,而 less 为基于 morevi (一个强大的编辑器,博客里有相关新的文章)开发,功能更强大。less 的使用基本和 more 一致,具体使用请查看 man 手册,这里只介绍 more 命令的使用。

使用more打开passwd文件

more passwd
xJTmMscvY4jIK2k

可以看到,在内容的尾部,会显示当前内容的阅读进度比。

接下来,你可以使用如下操作对内容进行阅读控制。

Enter键向下滚动一行

Space键向下滚动一屏,

h查看帮助

q退出

使用 headtail 命令查看文件

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

# 使用tail命令查看passwd文件
tail /etc/passwd

加上-n参数,后面跟一个行数

tail -n 2 /etc/passwd

Z8G3ClU4q7exyrh

另外,tail命令还有一个很实用的参数-f

-f循环读取,一般用来查看日志文件。

使用 file 命令查看文件的类型

file /bin/ls
bBeMkEipdm4vs3F

与 Windows 不同的是,如果你新建了一个 shiyanlou.txt 文件,Windows 会自动把它识别为文本文件,而 file 命令会识别为一个空文件。这个前面我提到过,在 Linux 中文件的类型不是根据文件后缀来判断的。当你在文件里输入内容后才会显示文件类型。

编辑文件

在 Linux 下面编辑文件通常我们会直接使用专门的命令行编辑器比如(emacs,vim,nano),由于涉及 Linux 上的编辑器的内容比较多,且非常重要,巧了,博客里有编辑器 vim 相关的文章,入门还是够了的。我想说的是vim这种东西,入门很痛苦,但是用熟悉之后简直不要太舒服,太高效!!!!

当然,如果你想更加快速地入门,可以直接使用 Linux 内部的 vim 学习教程,输入如下命令即可开始

vimtutor
ayizVqYZbpAr7WN

插播彩蛋!

如果你是跟着我的文章一步步学到这里,相信你也有些麻了吧,下面通过一些花里胡哨的的Linux小技巧来给枯燥的学习添点彩色!

跑火车

# 安装源
yum -y install epel-release

安装sl

yum -y install sl

执行sl

sl

sl

小奶牛

# 安装源(如果上面已经安装了,这里就不需要了)

安装小奶牛

yum -y install cowsay

执行小奶牛

cowsay "需要显示的内容"

20220309214038

艺术字

# 安装
yum install figlet

使用

figlet '想要输出的内容'

pQlr6yZEXTRsUdY

看天气

# curl该网站查看天气
curl http://wttr.in
20220309213605

五、环境变量与文件查找

介绍环境变量的作用与用法,以及几种搜索文件的方法

变量

要解释环境变量,得先明白变量是什么,准确的说应该是 Shell 变量,所谓变量就是计算机中用于记录一个值(不一定是数值,也可以是字符或字符串)的符号,而这些符号将用于不同的运算处理中。通常变量与值是一对一的关系,可以通过表达式读取它的值并赋值给其它变量,也可以直接指定数值赋值给任意变量。为了便于运算和处理,大部分的编程语言会区分变量的类型,用于分别记录数值、字符或者字符串等等数据类型。Shell 中的变量也基本如此,有不同类型(但不用专门指定类型名),可以参与运算,有作用域限定。

使用 declare 命令创建一个变量名为 tmp 的变量:

declare tmp

使用 = 号赋值运算符,将变量 tmp 赋值为 shiyanlou。注意,与其他语言不同的是, Shell 中的赋值操作,= 两边不可以输入空格,否则会报错

# 注意'='两边不能含有空格
tmp=Tisox

读取变量的值,使用 echo 命令和 $ 符号($ 符号用于表示引用一个变量的值,学过世界上最好的语言(PHP)的应该有点印象。

当然,bash的变量命名规则和其他语言(Java/C/C++)等类似。

echo $tmp

更多相关内容,需要参考bash脚本了

环境变量

简单理解了变量的概念,就很容易理解环境变量了。环境变量的作用域比自定义变量的要大,如 Shell 的环境变量作用于自身和它的子进程。在所有的 UNIX 和类 UNIX 系统中,每个进程都有其各自的环境变量设置,且默认情况下,当一个进程被创建时,除了创建过程中明确指定的话,它将继承其父进程的绝大部分环境设置。Shell 程序也作为一个进程运行在操作系统之上,而我们在 Shell 中运行的大部分命令都将以 Shell 的子进程的方式运行

20220309214938

通常我们会涉及到的变量类型有三种:

  • 当前 Shell 进程私有用户自定义变量,如上面我们创建的 tmp 变量,只在当前 Shell 中有效。

  • Shell 本身内建的变量。

  • 从自定义变量导出的环境变量。

也有三个与上述三种环境变量相关的命令:setenvexport。这三个命令很相似,都是用于打印环境变量信息,区别在于涉及的变量范围不同。

命 令

说 明

set

显示当前 Shell 所有变量,包括其内建环境变量(与 Shell 外观等相关),用户自定义变量及导出的环境变量。

env

显示与当前用户相关的环境变量,还可以让命令在指定环境中运行。

export

显示从 Shell 中导出成环境变量的变量,也能通过它将自定义变量导出为环境变量。

你可以更直观的使用 vimdiff 工具比较一下它们之间的差别:

temp=Tisox
export temp_env=Tisox
env|sort>env.txt
export|sort>export.txt
set|sort>set.txt

上述操作将命令输出通过管道 | 使用 sort 命令排序,再重定向到对象文本文件中。管道的概念后面我们会学到,现在你知道这是什么意思就行了。

vimdiff env.txt export.txt set.txt

使用 vimdiff 工具比较导出的几个文件的内容,退出 vimdiff 需要按下 Esc 后输入 :q 即可退出

20220310095641

关于哪些变量是环境变量,可以简单地理解成在当前进程的子进程有效则为环境变量,否则不是(有些人也将所有变量统称为环境变量,只是以全局环境变量和局部环境变量进行区分,我们只要理解它们的实质区别即可)。

export 命令来体会一下,先在 Shell 中设置一个变量 temp=shiyanlou,然后再新创建一个子 Shell 查看 temp 变量的值:

temp=Tisox
echo $tmep

创建子线程,从默认的bash切换到zsh

zsh
echo $temp

退出

exit

导出变量temp为环境变量

export temp
zsh
echo $temp

但是问题来了,当你关机后,或者关闭当前的 shell 之后,环境变量就没了啊。怎么才能让环境变量永久生效呢?

按变量的生存周期来划分,Linux 变量可分为两类:

  1. 永久的:需要修改配置文件,变量永久生效;

  2. 临时的:使用 export 命令行声明即可,变量在关闭 shell 时失效。

这里介绍两个重要文件 /etc/bashrc(有的 Linux 没有这个文件) 和 /etc/profile ,它们分别存放的是 shell 变量和环境变量。还有要注意区别的是每个用户目录下的一个隐藏文件

这个 .profile(.bash_profile) 只对当前用户永久生效。因为它保存在当前用户的 Home 目录下,当切换用户时,工作目录可能一并被切换到对应的目录中,这个文件就无法生效。而写在 /etc/profile 里面的是对所有用户永久生效,所以如果想要添加一个永久生效的环境变量,只需要打开 /etc/profile,在最后加上你想添加的环境变量就好

20220310105546

命令的查找路径与顺序

我们在 Shell 中输入一个命令,Shell 是怎么知道去哪找到这个命令然后执行的呢?这是通过环境变量 PATH 来进行搜索的,熟悉 Windows 的用户可能知道 Windows 中的也是有这么一个 PATH 环境变量。这个 PATH 里面就保存了 Shell 中执行的命令的搜索路径。

查看PATH环境变量的内容

echo $PATH

默认情况下的输出

20220310111113

如果你还记得 Linux 目录结构那一节的内容,你就应该知道上面这些目录下放的是哪一类文件了。通常这一类目录下放的都是可执行文件,当我们在 Shell 中执行一个命令时,系统就会按照 PATH 中设定的路径按照顺序依次到目录中去查找,如果存在同名的命令,则执行先找到的那个。

下面将练习创建一个最简单的可执行 Shell 脚本和一个使用 C 语言创建的“ hello world ”程序

创建一个 Shell 脚本文件,你可以使用 geditvimsublime 等工具编辑。如果你是直接复制的话,建议使用 gedit 或者 sublime,否则可能导致代码缩进混乱

# 创建一个shell脚本程序
touch hello_shell.sh

使用vim编辑脚本代码(注意!我使用的是vim,其他编辑器的操作可能略有不同,自己解决)

vim hello_shell.sh

编辑内容

for((i=0;i<10;i++));do
echo "hello shell"
done

b保存并退出编辑

:wq

20220310112445
# 授权执行
chmod 755 hello_shell.sh

执行程序

./hello_shell.sh

会打印10行"hello shell"

20220310112935

编写一个简单的C程序

vim hello_word.c

程序体内容

#include<stdio.h>
int main()
{
printf("hello_word!\n");
return 0;
}

编译:使用gcc生成可执行文件

gcc -o hello_world hello_world.c

注意,gcc 生成二进制文件默认具有可执行权限,不需要修改。

在家目录创建一个mybin 目录,并将上述 hello_shell.shhello_world 文件移动到其中:

mkdir mybin

移动

mv hello_shell.sh hello_world mybin/
cd mybin

执行

./hello_shell.sh
./hello_world

20220310114901

回到上一级目录,也就是 shiyanlou 家目录,当再想运行那两个程序时,会发现提示命令找不到,除非加上命令的完整路径,但那样很不方便,如何做到像使用系统命令一样执行自己创建的脚本文件或者程序呢?那就要将命令所在路径添加到 PATH 环境变量了。

20220310115529

添加自定义路径到“ PATH ”环境变量

在前面打印了PATH我们应该注意到 PATH 里面的路径是以 : 作为分割符的,所以我们可以这样添加自定义路径:

PATH=$PATH:/home/你的用户名/mybin
20220310214611

注意这里一定要使用绝对路径。

现在你就可以在任意目录执行那两个命令了(注意需要去掉前面的 ./)。你可能会意识到这样还并没有很好的解决问题,因为我给 PATH 环境变量追加了一个路径,它也只是在当前 Shell 有效,我一旦退出终端,再打开就会发现又失效了。有没有方法让添加的环境变量全局有效?或者每次启动 Shell 时自动执行上面添加自定义路径到 PATH 的命令?下面我们就来说说后一种方式——让它自动执行。

在每个用户的 home 目录中有一个 Shell 每次启动时会默认执行一个配置脚本,以初始化环境,包括添加一些用户自定义环境变量等等。实验楼的环境使用的 Shell 是 zsh,它的配置文件是 .zshrc,相应的如果使用的 Shell 是 Bash,则配置文件为 .bashrc。它们在 etc 下还都有一个或多个全局的配置文件,不过我们一般只修改用户目录下的配置文件。Shell 的种类有很多,可以使用 cat /etc/shells 命令查看当前系统已安装的 Shell。

20220310173450

可以简单地使用下面命令直接添加内容到 .zshrc或者.bashrc 中:

echo "PATH=$PATH:/home/xyy/mybin" >> .bashrc

上述命令中>>表示将标准输出以追加的方式重定向到一个文件中,注意前面用到的都是>,这是以覆盖的方式重定向到一个文件中,在指定文件不存在的情况下都会创建新的文件。

修改和删除已经有变量

修改变量的几种方式

变量设置方式

说明

${变量名#匹配字串}

从头向后开始匹配,删除符合匹配字串的最短数据

${变量名##匹配字串}

从头向后开始匹配,删除符合匹配字串的最长数据

${变量名%匹配字串}

从尾向前开始匹配,删除符合匹配字串的最短数据

${变量名%%匹配字串}

从尾向前开始匹配,删除符合匹配字串的最长数据

${变量名/旧的字串/新的字串}

将符合旧字串的第一个字串替换为新的字串

${变量名//旧的字串/新的字串}

将符合旧字串的全部字串替换为新的字串

比如我们可以修改前面添加到 PATH 的环境变量,将添加的 mybin 目录从环境变量里删除。为了避免操作失误导致命令找不到,我们先将 PATH 赋值给一个新的自定义变量 mypath

# 定义一个新变量
mypath=$PATH
echo $PATH
mypath=${mypath%/home/xyy/mybin}

或者使用通配符 * 表示任意字符

mypath=${mypath%*/mybin}

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

unset mypath
echo $mypath

如何让环境变量立即生效

之前在shell中修改了一个配置脚本文件之后(.bashrc),每次都要退出终端重新打开甚至重启主机之后才会生效,就很麻烦,下面了解一下source命令,它会然这个变量立即生效。

cd ~
source .bashrc

或者

source .zshrc

source还有一个别名就是.(点)下面的命令与上面等效。

cd ~
. ./.zshrc
. ./.bashrc

注意第一个点后面有一个空格,而且后面的文件必须指定完整的绝对或相对路径名,使用source 则不需要。

搜索文件

搜索相关命令:

  • whereis

  • which

  • find

  • locate

简单快捷的whereis

whereis who
whereis find
20220310223201

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

快而全的locate

locate的检索原理和whereis不同,locate会通过/var/lib/mlocate.db数据库来检索信息,而不是遍历硬盘数据。但这个数据库并不是实时更新,所以可能会出现刚刚添加的文件检索不到,不要急,要么等系统的执行完定时任务更新数据库之后再查找,要么你也可以手动更新数据库。

# 手动更新数据库mlocate.db
updatedb

注意这个命令也不是内置的命令,在部分环境中需要手动安装,然后执行更新

sudo update

安装

sudo install locate

更新数据库

sudo updayedb

它可以用来查找指定目录下的不同文件类型,如查找 /etc 下所有以 sh 开头的文件:

locate /etc/sh
20220310224644

注意,它不只是在 /etc 目录下查找,还会自动递归子目录进行查找

查找/usr/share/下所有jpg文件

locate /usr/share/.jpg
20220310224910

环境里使用 zsh,在 ~/.zshrc 文件里添加了 setopt nonomatch 配置,这样就不会自动处理和修复命令,因此可以不使用 </code> 转义。如果其他环境中执行该命令提示 zsh: no matches found: /usr/share/.jpg,则可以在 .zshrc 中添加上述配置,或者使用 </code> 转义。

  • 只需要统计数目,可以加上-c参数,

  • 忽略大小写进行查找-i

  • 此外,上面whereis的-b、-m、-s参数也是可以使用的。

小而精的which

which 本身是 Shell 内建的一个命令,我们通常使用 which 来确定是否安装了某个指定的程序,因为它只从 PATH 环境变量指定的路径中去搜索命令并且返回第一个搜索到的结果。也就是说,我们可以看到某个系统命令是否存在以及执行的到底是哪一个地方的命令

which man
which ping
20220310230121

精而细的find

find 应该是这几个命令中最强大的了,它不但可以通过文件类型、文件名进行查找而且可以根据文件的属性(如文件的时间戳,文件的权限等)进行搜索。find 命令强大到,要把它讲明白至少需要单独好几节课程才行,我们这里只介绍一些常用的内容。

sudo find /etc/ -name interfaces

这条命令表示去 /etc/ 目录下面 ,搜索名字叫做 interfaces 的文件或者目录。这是 find 命令最常见的格式,千万记住 find 的第一个参数是要搜索的地方。命令前面加上 sudo 是因为 xyy只是普通用户,对 /etc 目录下的很多文件都没有访问的权限,如果是 root 用户则不用使用。

注意 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 还要新的文件名

20220310231038

列出在home目录中,当天有改动的文件

find ~mtime 0
20220310231416

列出家目录下比/etc更新的文件

find ~ -newer /etc
20220310231730

文件打包与解压缩

介绍Linux上常用的压缩和解压工具,主要面对zip、tar的使用

  • zip命令

  • tar命令

  • 压缩与解压常用组合

在 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 程序压缩的文件

创建压缩包

使用 zip 打包文件夹,注意输入完整的参数和路径

cd ~

将目录/home/xyy打包成一个文件

zip -r -q -o xyy.zip /home/xyy

查看打包后文件的大小

du -h xyy.zip

查看打包后文件的类型

file xyy.zip

20220311102852

参数说明:

-r参数表示递归打包包含子目录的全部内容

-q参数表示为安静模式,不向屏幕输出任何信息

-o参数表示输出文件,需要在其后梗一个文件名

设置压缩级别为 9 和 1(9 最大,1 最小),重新打包

# 添加了一个压缩级别参数重新压缩:9级别
zip -r -9 -q -o xyy_9.zip /home/xyy -x ~/*.zip

1级别

zip -r -1 -q -o xyy_1.zip /home/xyy -x ~/*.zip

使用du命令分别查看默认压缩级别、最低、最高压缩级别以及未压缩的文件的大小

du -h -d 0 *.zip ~ | sort

20220311104157

参数说明:

-9参数设置一个压缩级别-[1-9],1表示压缩体积大但最快,9表示体积小但速度慢。

-x参数是为例排除前面打包创建的zip文件,否则会被打包进上一次的压缩文件中(就是说本次打包的内容排除掉前面打包的那个zip文件),

注意,这里只能使用绝对路径,否则不起作用!!!

通过 man 手册可知:

  • -h, --human-readable(顾名思义,你可以试试不加的情况)

  • -d, --max-depth(所查看文件的深度)

创建加密压缩包

使用 -e 参数可以创建加密压缩包

20220311110701

注意: 关于 zip 命令,因为 Windows 系统与 Linux/Unix 在文本文件格式上的一些兼容问题,比如换行符(为不可见字符),在 Windows 为 CR+LF(Carriage-Return+Line-Feed:回车加换行),而在 Linux/Unix 上为 LF(换行),所以如果在不加处理的情况下,在 Linux 上编辑的文本,在 Windows 系统上打开可能看起来是没有换行的。如果你想让你在 Linux 创建的 zip 压缩文件在 Windows 上解压后没有任何问题,那么你还需要对命令做一些修改

# 加上-l(是字母L的小写形式,不是数字1哦)参数将 LF 转换为 CR+LF 来达到以上目的。
zip -r -l -o shiyanlou.zip /home/xyy

使用 unzip 命令解压缩 zip 文件

将xyy.zip解压到当前目录

unzip xyy.zip

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

# 上述指定的目录如果不存在,会自动创建

unzip -q xyy.zip -d ziptest

如果不想解压,只是想查看压缩包的内容,可以加上-l参数

unzip -l xyy.xip

2022031111162820220311111816

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

使用 -O(英文字母,大写 o)参数指定编码类型:

unzip -O GBK 中文压缩文件.zip

使用unrar解压rar文件

  • 安装unrar

--------------- On 64-bit ---------------

cd /tmp

wget https://www.rarlab.com/rar/rarlinux-x64-5.6.0.tar.gz

tar -zxvf rarlinux-x64-5.6.0.tar.gz

cd rar

sudo cp -v rar unrar /usr/local/bin/

--------------- On 32-bit ---------------

cd /tmp

wget https://www.rarlab.com/rar/rarlinux-5.6.0.tar.gz

tar -zxvf rarlinux-5.6.0.tar.gz

cd rar

sudo cp -v rar unrar /usr/local/bin/

  • 简单使用

# 使用语法
unrar <command> [-<switch 1> -<switch N>] archive [files...] [path...]

基本参数

e:解压到当前文件。支持指定自定义解压得目录
l:列出环境中所有未rar格式得文件
x:提取压缩文件的文件目录

tar打包工具

在 Linux 上面更常用的是 tar 工具,tar 原本只是一个打包工具,同时还实现了对 7z、gzip、xz、bzip2 等工具的支持,这些压缩工具本身只能实现对文件或目录(单独压缩目录中的文件)的压缩,没有实现对文件的打包压缩,所以我们也无需再单独去学习其他几个工具,tar 的解压和压缩都是同一个命令,只需参数不同,使用比较方便。

掌握 tar 命令一些基本的使用方式,即不进行压缩只是进行打包(创建归档文件)和解包的操作。

cd ~
tar -Pcvf xyy.tar /home/xyy
20220311113320

参数说明:

-P保留绝对路径符

-c创建一个ta包文件

-f用于指定创建的文件名,文件名必须紧跟在其后

-v以可视化的方式输出打包的文件

解包一个文件(-x 参数)到指定路径的已存在目录(-C 参数):

mkdir tardir

解包一个文件(-x 参数)到指定路径的已存在目录(-C 参数):

tar -xf xyy_1.tar -C tardir

只查看不解包文件-t参数

tar -tf xyy.tar

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

# 这里不作演示

tar -cphf etc.tar /etc

20220311114707

出现该问题的原因是tar默认为相对路径,使用绝对路径的话就会报这个错,可以只用-P参数解决这个问题

20220311114901

对于创建不同的压缩格式的文件,对于 tar 来说是相当简单的,需要的只是换一个参数,这里以使用 gzip 工具创建 .tar.gz 文件为例来说明。

我们只需要在创建 tar 文件的基础上添加 -z 参数,使用 gzip 来压缩文件

tar -czf xyy.tar.gz /home/xyy
20220311143307

解压.tar.gz文件

tar -xzf xyy.tar.gz

如果需要使用其它的压缩工具创建或解压相应文件只需要更改一个参数即可:

压缩文件格式

参数

.tar.gz

-z

.tar.xz

-J

*tar.bz2

-j

小结

  • zip:

  • 打包 :zip something.zip something (目录请加 -r 参数)

  • 解包:unzip something.zip

  • 指定路径:-d 参数

  • tar:

  • 打包:tar -cf something.tar something

  • 解包:tar -xf something.tar

  • 指定路径:-C 参数

六、文件系统操作与磁盘管理

前面省略了关于 Linux 文件系统的一些基本知识,也因为我们是在线实验环境,所以也避开了很少一部分但又十分重要的关于硬件的内容,我们只能期待用户能够抱着提高自学能力的心态自己去补充相关的知识。

  • df、du、mount、命令的使用

  • 磁盘相关知识

查看磁盘和目录的容量

# 使用df命令查看磁盘容量
df
image-20220311152647724

但在实际的物理主机上会更像这样:

img

物理主机上的 /dev/sda2 是对应着主机硬盘的分区,后面的数字表示分区号,数字前面的字母 a 表示第几块硬盘(也可能是可移动磁盘),你如果主机上有多块硬盘则可能还会出现 /dev/sdb,/dev/sdc 这些磁盘设备都会在 /dev 目录下以文件的存在形式。

接着你还会看到"1k-块"这个陌生的东西,它表示以磁盘块大小的方式显示容量,后面为相应的以块大小表示的已用和可用容量,在你了解 Linux 的文件系统之前这个就先不管吧,我们以一种你应该看得懂的方式展示:

df -h
20220311153023

现在你就可以使用命令查看你主机磁盘的使用情况了。至于挂载点如果你还记得前面第 4 节介绍 Linux 目录树结构的内容,那么你就应该能很好的理解挂载的概念,这里就不再赘述。

du命令

# 默认同样以块的大小展示
du

加上 -h 参数,以更易读的方式展示

du -h

-d参数指定查看目录的深度

# 只查看 1 级目录的信息
du -h -d 0 ~

查看 2 级

du -h -d 1 ~

常用参数

du -h # 同 --human-readable 以 K,M,G 为单位,提高信息的可读性。
du -a # 同 --all 显示目录中所有文件的大小。
du -s # 同 --summarize 仅显示总计,只列出最后加总的值。
20220311175847

du(estimate file space usage)命令与 df(report file system disk space usage)命令只有一字之差,注意不要弄混淆,可以从 man 手册中获取命令的完整描述,记全称就不会搞混了。

简单的磁盘管理

由于实验环境的限制,环境中没有额外的磁盘可以挂载,也没有空闲空间测试分区命令,所以首先我们会先创建一个虚拟磁盘来进行后续的练习操作。

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 if=/dev/stdin of=test bs=10 count=1
dd of=test bs=10 count=1

输出到标准输出

dd if=/dev/stdin of=/de/stdout bs=10 count=1

命令结束之后,可以在终端继续打字,作为你的输入

20220311182136

上述命令从标准输入设备读入用户输入(缺省值,所以可省略)然后输出到 test 文件,bs(block size)用于指定块大小(缺省单位为 Byte,也可为其指定如 K,M,G 等单位),count 用于指定块数量。如上图所示,我指定只读取总共 10 个字节的数据,当我输入了 hello shiyanlou 之后加上空格回车总共 16 个字节(一个英文字符占一个字节)内容,显然超过了设定大小。使用 du 和 cat 10 个字节(那个黑底百分号表示这里没有换行符),而其他的多余输入将被截取并保留在标准输入。

dd 在拷贝的同时还可以实现数据转换

# 将输出的y英文字符转为大写之后再写入文件
dd if=/dev/stdin of=test bs=10 count=1 conv=ucase
20220311184058

使用 dd 命令创建虚拟镜像文件

# 从 /dev/zero 设备创建一个容量为 256M 的空文件
dd if=/dev/zero of=virtual.img bs=1M count=256
20220311205144

使用 mkfs 命令格式化磁盘(我们这里是自己创建的虚拟磁盘镜像)

你可以在命令行输入 sudo mkfs 然后按下 <Tab> 键,你可以看到很多个以 mkfs 为前缀的命令,这些不同的后缀其实就是表示着不同的文件系统,可以用 mkfs 格式化成的文件系统。

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

可以看到实际 mkfs.ext4 是使用 mke2fs 来完成格式化工作的。mke2fs 的参数很多,不过我们也不会经常格式化磁盘来玩,所以就掌握这基本用法吧,等你有特殊需求时,再查看 man 文档解决。

20220311205213

查看Linux 支持哪些文件系统 ls -l /lib/modules/$(uname -r)/kernel/fs

20220311205426

更多关于文件系统的知识,请查看 wiki: 文件系统 ext3,ext4

使用 mount 命令挂载磁盘到目录树

用户在 Linux/UNIX 的机器上打开一个文件以前,包含该文件的文件系统必须先进行挂载的动作,此时用户要对该文件系统执行 mount 的指令以进行挂载。该指令通常是使用在 USB 或其他可移除存储设备上,而根目录则需要始终保持挂载的状态。又因为 Linux/UNIX 文件系统可以对应一个文件而不一定要是硬件设备,所以可以挂载一个包含文件系统的文件到目录树。

Linux/UNIX 命令行的 mount 指令是告诉操作系统,对应的文件系统已经准备好,可以使用了,而该文件系统会对应到一个特定的点(称为挂载点)。挂载好的文件、目录、设备以及特殊文件即可提供用户使用。

# 查看主机已经挂载了哪些文件系统
sudo mount
2022031120572420220311210322

输出的结果中:

  • 每一行表示一个设备或虚拟设备,

  • 每一行最前面是设备名,

  • 然后是 on 后面是挂载点,

  • type 后面表示文件系统类型,

  • 再后面是挂载选项(比如可以在挂载时设定以只读方式挂载等等)。

# 现在直接来挂载我们创建的虚拟磁盘镜像到 /mnt 目录
mount -o loop -t ext4 virtual.img /mnt

也可以省略挂载类型,很多时候 mount 会自动识别

以只读方式挂载

mount -o loop --ro virtual.img /mnt

或者这样

mount -o loop,ro virtual.img /mnt

20220311205930
# 命令格式 sudo umount 已挂载设备名或者挂载点,如:
sudo umount /mnt

另外关于 loop 设备,你可能会有诸多疑问,那么请看来自维基百科 /dev/loop的说明

使用fdisk 为磁盘分区

(关于分区的一些概念不清楚的用户请参看 主引导记录)

# 查看硬盘分区表信息
sudo fdisk -l
image-20220312115150291

输出结果中开头显示了我主机上的磁盘的一些信息,包括容量扇区数,扇区大小,I/O 大小等信息。

由于环境限制,这部分先暂停!!!

七、Linux 下的帮助命令

man和help

  • 内建命令与外部命令之分

  • help,man,info 命令的使用以及区别

内建命令实际上是 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 命令来区分命令是内建的还是外部的。例如这两个得出的结果是不同的

type exit
type vim
type ls
20220312121601

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

ls --help
man ls

以上命令都可以打开帮助手册,但是man会详细许多。而且 man 没有内建与外部命令的区分,因为 man 工具是显示系统手册页中的内容,也就是一本电子版的字典,这些内容大多数都是对命令的解释信息,还有一些相关的描述。通过查看系统文档中的 man 也可以得到程序的更多相关信息和 Linux 的更多特性。

在尝试上面这个命令时我们会发现最左上角显示“ 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 退出当前页面

info命令

要是你觉得 man 显示的信息都还不够,满足不了你的需求,那试试 info 命令,注意,这个命令可能需要安装。

20220312122822

得到的信息是不是比 man 还要多了,info 来自自由软件基金会的 GNU 项目,是 GNU 的超文本帮助系统,能够更完整的显示出 GNU 信息。所以得到的信息当然更多

man 和 info 就像两个集合,它们有一个交集部分,但与 man 相比,info 工具可显示更完整的 GNU 工具信息。若 man 页包含的某个工具的概要信息在 info 中也有介绍,那么 man 页中会有“请参考 info 页更详细内容”的字样。

八、Linux 任务计划 crontab

我们时常会有一些定期定时的任务,如周期性的清理一下 /tmp,周期性的去备份一次数据库,周期性的分析日志等等。而且有时候因为某些因素的限制,执行该任务的时间会很尴尬。本课程将带你很好的利用 Linux 系统的计划工具。

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

通过 crontab 命令,我们可以在固定的间隔时间执行指定的系统指令或 shell 脚本。时间间隔的单位可以是分钟、小时、日、月、周的任意组合。

基本格式

20220312192224
# 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

查看是否开启了rsyslog

20220312153650
# 重启
systemctl restart rsyslog.service

开启

systemctl start rsyslog.service

状态查看

systemctl status rssylog.service

启动crontab

sudo cron -f &

启动crond

systemctl start crond.service

20220312153919

添加计划任务

crontab -e

查看具体格式

man crontab

20220312175649

一下任务会每每分钟在/home/xyy目录下新建一个以当前的年月日时分秒为名字的空白文件

*/1 * * * * touch /home/xyy/$(date+%Y%m%d%H%M%S)

添加定时任务

crontab -e

查看添加了哪些任务

crontab -l

删除全部定时任务(删除部分任务需要使用-e参数进入进行修改)

crontab -r

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

20220312155533

但是如果 cron 的守护进程并没有启动,它根本都不会监测到有任务,当然也就不会帮我们执行,我们可以通过以下 2 种方式来确定我们的 cron 是否成功的在后台启动,默默的帮我们做事,若是没有就得执行上文准备中的第二步了。

ps aux | grep cron

or

pgrep cron

20220312155744

查看已经执行过的任务,看 /var/log/cron这个文件就可以,可以用tail -f /var/log/cron观察

1、vi /var/log/cron

2、tail -f /var/log/cron

crontab深入

每个用户使用 crontab -e 添加计划任务,都会在 /var/spool/cron/crontabs 中添加一个该用户自己的任务文档,这样目的是为了隔离。

注意了 我的系统是centOS8Stream,上面的命令执行后发现没有这个文件或者文件夹,查看发现,是在/var/spool/cron中。

20220312193508

如果是系统级别的定时任务,需要 root 权限执行的任务应该怎么处理?

只需要使用 sudo 编辑 /etc/crontab 文件就可以

cron 服务监测时间最小单位是分钟,所以 cron 会每分钟去读取一次 /etc/crontab 与 /var/spool/cron 里面的內容。

在 /etc 目录下,cron 相关的目录有下面几个

20220312194137

var/spool下有

20220312194343

每个目录的作用:

  1. /etc/cron.daily,目录下的脚本会每天执行一次,在每天的 6 点 25 分时运行;

  2. /etc/cron.hourly,目录下的脚本会每个小时执行一次,在每小时的 17 分钟时运行;

  3. /etc/cron.monthly,目录下的脚本会每月执行一次,在每月 1 号的 6 点 52 分时运行;

  4. /etc/cron.weekly,目录下的脚本会每周执行一次,在每周第七天的 6 点 47 分时运行;

系统默认执行时间可以根据需求进行修改

推荐文章:例行性工作派程(crontab)

练习:备份日志

小明是一个服务器管理员,他需要每天备份自己博客数据(这里我们用 blog.log 日志替代),备份当天的日志并删除之前的日志。而且备份之后文件名是 年-月-日 的格式。blog.log 在 /var/log/ 下面。

  1. 为 xyy 用户添加计划任务

  2. 每天凌晨 3 点的时候定时备份 blog.log 到 /home/xyy/tmp/ 目录

  3. 命名格式为 年-月-日,比如今天是 2017 年 4 月 1 日,那么文件名为 2017-04-01

crontab -e 0 3 * * * sudo rm /home/xyy/tmp/* 0 3 * * * sudo cp /var/log/blog.log/ /home/xyy/tmp/$(date+%Y-%m-%d)

cron表达式详解

经典案例

"30 * * * * ?" 每半分钟触发任务 "30 10 * * * ?" 每小时的10分30秒触发任务 "30 10 1 * * ?" 每天1点10分30秒触发任务 "30 10 1 20 * ?" 每月20号1点10分30秒触发任务 "30 10 1 20 10 ? *" 每年10月20号1点10分30秒触发任务 "30 10 1 20 10 ? 2011" 2011年10月20号1点10分30秒触发任务 "30 10 1 ? 10 * 2011" 2011年10月每天1点10分30秒触发任务 "30 10 1 ? 10 SUN 2011" 2011年10月每周日1点10分30秒触发任务 "15,30,45 * * * * ?" 每15秒,30秒,45秒时触发任务 "15-45 * * * * ?" 15到45秒内,每秒都触发任务 "15/5 * * * * ?" 每分钟的每15秒开始触发,每隔5秒触发一次 "15-30/5 * * * * ?" 每分钟的15秒到30秒之间开始触发,每隔5秒触发一次 "0 0/3 * * * ?" 每小时的第0分0秒开始,每三分钟触发一次 "0 15 10 ? * MON-FRI" 星期一到星期五的10点15分0秒触发任务 "0 15 10 L * ?" 每个月最后一天的10点15分0秒触发任务 "0 15 10 LW * ?" 每个月最后一个工作日的10点15分0秒触发任务 "0 15 10 ? * 5L" 每个月最后一个星期四的10点15分0秒触发任务 "0 15 10 ? * 5#3" 每个月第三周的星期四的10点15分0秒触发任务

九、命令执行顺序控制与管道

命令执行顺序

  • 顺序执行、

  • 选择执行、

  • 管道、

  • cut 命令、

  • grep 命令、

  • wc 命令、

  • sort 命令等,

  • 高效率使用 Linux 的技巧。

当我们需要使用 apt-get (yum)安装一个软件,然后安装完成后立即运行安装的软件或命令工具,又恰巧你的主机才更换的软件源还没有更新软件列表(比如之前我们的环境中,每次重新开始实验就得 sudo apt-get update(yum update),否则可能会报错提示 404),那么你可能会有如下一系列操作。

sudo apt-get update

等待执行完毕,然后输入下面的命令

sudo apt-get install some-tool # 这里 some-tool 需要替换成具体的软件包

等待安装完毕,然后输入软件包名称执行

some-tool

注意以上命令在centOS中不同。

简单的顺序执行你可以使用 ; 来完成,比如上述操作你可以:

sudo apt-get update;sudo apt-get install some-tool;some-tool # 让它自己运行

关于上面的操作,不知你有没有思考过一个问题,如果我们在让它自动顺序执行命令时,前面的命令执行不成功,而后面的命令又依赖于上一条命令的结果,那么就会造成花了时间,最终却得到一个错误的结果,而且有时候直观的看你还无法判断结果是否正确。那么我们需要能够有选择性的来执行命令,比如上一条命令执行成功才继续下一条,或者不成功又该做出其它什么处理,比如我们使用 which 来查找是否安装某个命令,如果找到就执行该命令,否则什么也不做,虽然这个操作没有什么实际意义,但可帮你更好的理解一些概念。

which cowsay>/dev/null && cowsay -f head-in ohch~

你如果没有安装 cowsay,你可以先执行一次上述命令,你会发现什么也没发生,你再安装好之后你再执行一次上述命令,你也会发现一些惊喜。

上面的 && 就是用来实现选择性执行的,它表示如果前面的命令执行结果(不是表示终端输出的内容,而是表示命令执行状态的结果)返回 0 则执行后面的,否则不执行,你可以从 $? 环境变量获取上一次命令的返回结果:

20220313103925

学习过 C 语言的用户应该知道在 C 语言里面 && 表示逻辑与,而且还有一个 || 表示逻辑或,同样 Shell 也有一个 ||,它们的区别就在于,shell 中的这两个符号除了也可用于表示逻辑与和或之外,就是可以实现这里的命令执行顺序的简单控制。|| 在这里就是与 && 相反的控制效果,当上一条命令执行结果为 ≠0($?≠0) 时则执行它后面的命令:

20220313104035

可以结合&&和||实现一些操作。

20220313104559

管道

管道是一种通信机制,通常用于进程间的通信(也可通过 socket 进行网络通信),它表现出来的形式就是将前面每一个进程的输出(stdout)直接作为下一个进程的输入(stdin)

管道又分为匿名管道和具名管道:我们在使用一些过滤程序时经常会用到的就是匿名管道,在命令行中由 | 分隔符表示,| 在前面的内容中我们已经多次使用到了。具名管道简单的说就是有名字的管道,通常只会在源程序中用到具名管道。

普通方式查看/etc下有哪些文件和目录

ls -al /etc

通过管道的方式查看,它会将前面一个命令ls的输出作为下一个命令less的输入,然后可以一行一行的查看。

ls -al /etc | less

cut命令

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

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

如果不用cut过滤,可能是这样的。cut可以很好的筛选我们需要的数据进行查看。

20220313110750

打印 /etc/passwd 文件中每一行的前 N 个字符

# 前5个(包含)
cut /etc/passwd -c -5

前5个之后的(包含)

cut /etc/passwd -c 5-

第5个

cut /etc/passwd -c 5

2到5之间的(包含第5个)

cut /etc/passwd -c 2-5

2022031311155420220313111612

......

greap命令

这个命令虽然现在才学到,但其实在前面的操作中已经用过很多次了。

grep 命令是很强大的,也是相当常用的一个命令,它结合正则表达式可以实现很复杂却很高效的匹配和查找,不过在学习正则表达式之前,这里介绍它简单的使用,而关于正则表达式后面将会有单独一小节介绍到时会再继续学习 grep 命令和其他一些命令。

grep命令的一般格式

grep [命令选项]... 用于匹配的表达式 [文件]...

我们搜索/home/xyy目录下所有包含"xyy"的文本文件,并显示出现在文本中的行号:

grep -rnI "xyy" ~
20220313112805

-r表示递归搜索子目录中的文件。

-n打印匹配项行号

-I忽略二进制文件。

在匹配字段中使用正则表达式

# 查看环境变量中以"xyy"结尾的字符串
export | grep ".xyy$"
20220313121043

$表示一行的末尾

wc命令

用于统计并输出一个文件中行、单词、字节的数目,比如输出etc/passwd文件的统计信息。

# 行数
wc -l /etc/passwd

单词数

wc -w /etc/passwd

字节数

wc -c /etc/passwd

字符数

wc -m /etc/passwd

最长行字节数

wc -L /etc/passwd

注意:对于西文字符来说,一个字符就是一个字节,但对于中文字符一个汉字是大于 2 个字节的,具体数目是由字符编码决定的

20220313121535

再来结合管道来操作一下,下面统计 /etc 下面所有目录数:

ls -dl /etc/*/ | wc -l
20220313121910

sort命令

sort命令功能很简单。就是将输入按照一定方式排序,然后再输出,它支持的排序有按字典排序,数字排序,按月份排序,随机排序,反转排序,指定特定字段进行排序等等。

# 默认为字典排序
cat /etc/passwd | sort

反转排序

cat /etc/passwd | sort -r

按特定字段排序

cat /etc/passwd/ | sort -t ':' -k 3

202203131224182022031312250620220313122618

参数说明

-t用于指定字段的分隔符,如上面的':'

-k用于指定对哪一个字段进行排序,这里/etc/passwd文件的第三个字段为数字,默认情况下按照字典序排序。如果需要按照数字排序就加上-n。

20220313131252

uniq

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

过滤重复行

我们可以使用 history 命令查看最近执行过的命令(实际为读取 ${SHELL}_history 文件,如我们环境中的 .zsh_history 文件),不过你可能只想查看使用了哪个命令而不需要知道具体干了什么,那么你可能就会要想去掉命令后面的参数然后去掉重复的命令

history | cut -c 8- | cut -d ' ' -f 1 | uniq
2022031313491420220313135004

然后经过层层过滤,你会发现确是只输出了执行的命令那一列,不过去重效果好像不明显,仔细看你会发现它确实去重了,只是不那么明显,之所以不明显是因为 uniq 命令只能去连续重复的行,不是全文去重,所以要达到预期效果,我们先排序:

history | cut -c 8- | cut -d ' ' -f 1 | sort | uniq

or

history | cut -c 8- | cut -d ' ' -f 1 | sort -u

20220313152008

输出重复行

# 输出重复过的行(重复的只输出一个)以及重复次数
history | cut -c 8- | cut -d ' ' -f 1 | sort | uniq -dc

输出所有重复的行

history | cut -c 8- | cut -d ' ' -f 1 | sort uniq -D

20220313155727image-20220313155745203

十、简单的文本处理

将介绍这几个命令tr,col,join,paste。实际这一节是上一节关于能实现管道操作的命令的延续,所以我们依然将结合管道来熟悉这些命令的使用

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]'

image-20220313170207746image-20220313170219490

注意'[]' '[]'两个参数都需要才能正常转换,不能只写一个大写的'[A-Z]'or小写的'[a-z]'

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

2022031317132320220313171356

join命令

学过数据库的应该不陌生,在数据库中这个作为多表连接的一种,将两个文件中相同内容的一行合并在一起。

cd ~

创建两个文件

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

了解更多相关join命令可以参考一下这篇文章

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
20220313174243

十一、数据流重定向

简单重定向

你可能对重定向这个概念感到些许陌生,但你应该在前面的课程中多次见过 > 或 >> 操作了,并知道他们分别是将标准输出导向一个文件或追加到一个文件中。这其实就是重定向,将原本输出到标准输出的数据重定向到一个文件中,因为标准输出(/dev/stdout)本身也是一个文件,我们将命令输出导向另一个文件自然也是没有任何问题的。

回顾一下

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

当然前面没有用到的 < 和 << 操作也是没有问题的,如你理解的一样,它们的区别在于重定向的方向不一致而已,> 表示是从左到右,< 右到左。

前面我们已经提到过 Linux 默认提供了三个特殊设备,用于终端的显示和输出,分别为 stdin(标准输入,对应于你在终端的输入),stdout(标准输出,对应于终端的输出),stderr(标准错误输出,对应于终端的输出)。

文件描述符

设备文件

说明

0

/dev/stdin

标准输入

1

/dev/stdout

标准输出

2

/dev/stderr

标准错误

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

将 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
20220313184021

将echo命令通过管道冲过来的数据作为cat命令的输入。将标准输出作为命令的输出

将echo命令的输出从默认的标准输出重定向到一个普通文件

image-20220313184420982

【注意】管道默认是连接前一个命令的输出到下一个命令的输入,而重定向通常是需要一个文件来建立两个命令的连接,

标准错误重定向

重定向标准输出到文件,这是一个很实用的操作,另一个很实用的操作是将标准错误重定向,标准输出和标准错误都被指向伪终端的屏幕显示,所以我们经常看到的一个命令的输出通常是同时包含了标准输出和标准错误的结果的。比如下面的操作:

# 使用cat 命令同时读取两个文件,其中一个存在,另一个不存在
cat Documents/test.c hello.c

你可以看到除了正确输出了前一个文件的内容,还在末尾出现了一条错误信息

下面我们将输出重定向到一个文件

cat Documents/test.c hello.c > somefile

20220313185549

这里依然出现了那条错误信息,这正是因为如我上面说的那样,标准输出和标准错误虽然都指向终端屏幕,实际它们并不一样。那有的时候我们就是要隐藏某些错误或者警告,那又该怎么做呢。这就需要用到我们前面讲的文件描述符了:

# 将标准错误重定向到标准输出,再将标准输出重定向到文件,注意要将重定向到文件写到前面
cat Documents/test.c hello.c >somefile  2>&1

或者只用bash提供的特殊的重定向符号"&"将标准错误和标准输出同时重定向到文件

cat Documents/test.c hello.c &>somefilehell

20220313190032

注意,你应该在输出重定向文件描述符前加上&,否则shell会当作重定向到一个文件名为1的文件中

使用 tee 命令同时重定向到多个文件

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

echo 'hello Tisox' | tee hello
20220313192046

永久重定向

你应该可以看出我们前面的重定向操作都只是临时性的,即只对当前命令有效,那如何做到永久有效呢,比如在一个脚本中,你需要某一部分的命令的输出全部进行重定向,难道要让你在每个命令上面加上临时重定向的操作嘛?

当然不需要,我们可以使用 exec 命令实现永久重定向。exec 命令的作用是使用指定的命令替换当前的 Shell,即使用一个进程替换当前进程,或者指定新的重定向:

# 先开启一个子 Shell
zsh

使用exec替换当前进程的重定向,将标准输出重定向到一个文件

exec 1>somefile

后面你执行的命令的输出都将被重定向到文件中,直到你退出当前子shell,或取消exec的重定向(后面将告诉你怎么做)

ls
exit
cat somefile

20220313192604

创建输出文件描述符

在 Shell 中有 9 个文件描述符。上面我们使用了也是它默认提供的 0,1,2 号文件描述符。另外我们还可以使用 3-8 的文件描述符,只是它们默认没有打开而已。你可以使用下面命令查看当前 Shell 进程中打开的文件描述符:

cd /dev/fd/;ls -Al

同样使用 exec 命令可以创建新的文件描述符:

zsh
exec 3>somefile

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

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

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

echo "this is test" >&3
cat somefile
exit

2022031319310820220313193254

关闭文件描述符

如上面我们打开的 3 号文件描述符,可以使用如下操作将它关闭:

exec 3>&-
cd /dev/fd;ls -Al;cd -
20220313193503

完全屏蔽命令的输出

在 Linux 中有一个被称为黑洞的设备文件,所有导入它的数据都将被吞噬。

在类 UNIX 系统中,/dev/null,或称空设备,是一个特殊的设备文件,它通常被用于丢弃不需要的输出流,或作为用于输入流的空文件,这些操作通常由重定向完成。读取它则会立即得到一个 EOF。

利用/dev/null屏蔽命令的输出

cat Documents/test.c 1>/dev/null 2>&1
20220313195459

使用 xargs 分割参数列表

xargs 是一条 UNIX 和类 UNIX 操作系统的常用命令。它的作用是将参数列表转换成小块分段传递给其他命令,以避免参数列表过长的问题。

这个命令在有些时候十分有用,特别是当用来处理产生大量输出结果的命令如 find,locate 和 grep 的结果,详细用法请参看 man 文档

cut -d: -f1 < /etc/passwd | sort | xargs echo

上面这个命令用于将 /etc/passwd 文件按 : 分割取第一个字段排序后,使用 echo 命令生成一个列表。

插播彩蛋

20220313201042

燃烧的火焰!!

安装步骤

sudo yum -y install caca-utils
cacaview <pic_file> cacademo
cacafire

十二、正则表达式基础

介绍grep,sed,awk这三个命令,而正则表达式作为这三个命令的一种使用方式(命令输出中可以包含正则表达式)。正则表达式本身的内容很多,这里只是说一些比较常用的。

  • 掌握基本命令:sed,grep,awk的用法

  • 掌握正则表达式符号和语法

何为正则表达式

正则表达式,又称正规表示式、正规表示法、正规表达式、规则表达式、常规表示法(英语: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 两个字符。

基本语法

一个正则表达式通常被称为一个模式(pattern),为用来描述或者匹配一系列符合某个句法规则的字符串。

选择

|竖直分隔符表示选择,例如boy|girl可以匹配boy或者girl。

数量限定

数量限定除了上面例子中的,还有+,?(加号和问号),如果在一个模式中不加数量限定符规则表示出现一次且只出现一次。

+ 表示前面的字符必须出现至少一次(1 次或多次),例如 goo+gle 可以匹配 gooogle,goooogle 等

? 表示前面的字符最多出现一次(0 次或 1 次),例如,colou?r,可以匹配 color 或者 colour;

星号代表前面的字符可以不出现,也可以出现一次或者多次(0 次、或 1 次、或多次),例如,042 可以匹配 42、042、0042、00042 等

范围和优先级

() 圆括号可以用来定义模式字符串的范围和优先级,这可以简单的理解为是否将括号内的模式串作为一个整体。例如,gr(a|e)y 等价于 gray|grey,(这里体现了优先级,竖直分隔符用于选择 a 或者 e 而不是 gra 和 ey),(grand)?father 匹配 father 和 grandfather(这里体现了范围,? 将圆括号内容作为一个整体匹配)

语法部分

正则表达式有多种不同的风格,下面列举一些常用的作为 PCRE 子集的适用于 perl 和 python 编程语言及 grep 或 egrep 的正则表达式匹配规则:

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

由于 markdown 表格解析的问题,下面的竖直分隔符 | 用全角字符代替,实际使用时请换回半角字符

字符

描述

</code>

将下一个字符标记为一个特殊字符、或一个原义字符。 例如 n 匹配字符 n。\n 匹配一个换行符。序列 \ 匹配 </code> 而 ( 则匹配 (。

^

匹配输入字符串的开始位置。

$

匹配输入字符串的结束位置。

{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。其中特殊字符仅有反斜线 </code> 保持特殊含义,用于转义字符。其它特殊字符如星号、加号、各种括号等均作为普通字符。脱字符^如果出现在首位则表示负值字符集合;如果出现在字符串中间就仅作为普通字符。连字符 - 如果出现在字符串中间表示字符范围描述;如果出现在首位则仅作为普通字符。

[^xyz]

排除型(negate)字符集合。**匹配未列出的任意字符。**例如,[^abc] 可以匹配 plain 中的 plin。

[a-z]

字符范围。**匹配指定范围内的任意字符。**例如,[a-z] 可以匹配 a 到 z 范围内的任意小写字母字符。

[^a-z]

排除型的字符范围。匹配任何不在指定范围内的任意字符。例如,[^a-z] 可以匹配任何不在 a 到 z 范围内的任意字符。

优先级

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

运算符

说明

</code>

转义符

(),(?:),(?=),[]

括号和中括号

*,+,?,{n},{n,},{n,m}

限定符

^,$,</code> 任何元字符

定位点和序列

选择

更多正则表达式的内容可以参考以下链接:

  • 正则表达式 wiki

  • 几种正则表达式引擎的语法差异

  • 各语言各平台对正则表达式的支持

grep 模式匹配命令

如果说正则表达式是一门武功,那它也只能算得上一些口诀招式罢了,要把它真正练起来还得需要一些兵器在手才行,这里我们要介绍的 grep 命令以及后面要讲的 sed,awk 这些就该算作是这样的兵器了。

grep 命令用于打印输出文本中匹配的模式串,它使用正则表达式作为模式匹配的条件。grep 支持三种正则表达式引擎,分别用三个参数指定:

参数

说明

-E

POSIX 扩展正则表达式,ERE

-G

POSIX 基本正则表达式,BRE

-P

Perl 正则表达式,PCRE

不过在你没学过 perl 语言的大多数情况下你将只会使用到 ERE 和 BRE

在通过grep命令使用正则表达式之前,先介绍一下它的常用参数:

参数

说明

-b

将二进制文件作为文本来进行匹配

-c

统计以模式匹配的数目

-i

忽略大小写

-n

显示匹配文本所在行的行号

-v

反选,输出不匹配行的内容

-r

递归匹配查找

-A n

n 为正整数,表示 after 的意思,除了列出匹配行之外,还列出后面的 n 行

-B n

n 为正整数,表示 before 的意思,除了列出匹配行之外,还列出前面的 n 行

--color=auto

将输出中的匹配项设置为自动颜色显示

注:在大多数发行版中是默认设置了 grep 的颜色的,你可以通过参数指定或修改GREP_COLOR环境变量。

202203141115212022031411193120220314111953

使用使用基本正则表达式,BRE

位置

# 查看在/etc/group/中包含xyy的行
grep 'xyy' /etc/group

查看在/etc/group/中包含xyy开头和结尾的行

grep '^xyy' /etc/group
grep 'xyy$' /etc/group

20220314112402

数量

# 将匹配以'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*'

20220314113213

选择

# 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:]]'

2022031411363020220314113738

下面包含完整的特殊符号及说明:

特殊符号

说明

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

排除字符

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

当 ^ 放到中括号内为排除字符,否则表示行首。

使用扩展正则表达式,ERE

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

数量

# 只匹配"zo"
echo 'zero\nzo\nzoo' | grep -E 'zo{1}'

匹配以"zo"开头的所有单词

echo 'zero\nzo\nzoo' | grep -E 'zo{1,}'

推荐掌握 {n,m} 即可 +,?,* 这几个不太直观,且容易弄混淆

20220314114826

选择

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

20220314115308

注意'.'需要转义

关于正则表达式和 grep 命令的内容就介绍这么多,下面会介绍两个更强大的工具 sed 和 awk,但同样也正是因为这两个工具的强大,我们的内容无法包含它们的全部,这里将只对基本内容作介绍

十三、sed 流编辑器

sed 工具在 man 手册里面的全名为"sed - stream editor for filtering and transforming text ",意即,用于过滤和转换文本的流编辑器。

在 Linux/UNIX 的世界里敢称为编辑器的工具,大都非等闲之辈,比如前面的 vi/vim(编辑器之神),emacs(神的编辑器),gedit 这些编辑器。sed 与上述的最大不同之处在于它是一个非交互式的编辑器,下面我们就开始介绍 sed 这个编辑器。

基本格式

sed [参数]... [执行命令] [输入文件]...

形如:

$ sed -i 's/she/he/' test.txt # 表示将test.txt文件中的"sad"替换为"happy"

参数

说明

-n

安静模式,只打印受影响的行,默认打印输入数据的全部内容

-e

用于在脚本中添加多个执行命令一次执行,在命令行中执行多个命令通常不需要加该参数

-f filename

指定执行 filename 文件中的命令

-r

使用扩展正则表达式,默认为标准正则表达式

-i

将直接修改输入文件内容,而不是打印到标准输出设备

sed 执行命令格式:

[n1][,n2]command
[n1][~step]command

其中一些命令可以在后面加上作用范围,形如:

# g表示全局范围
sed -i 's/sad/happy/g' test.txt

4 表示指定行中的第四个匹配字符串

sed -i 's/sed/happy/4' test.txt

其中 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'

20220314151846image-20220314151906955

行内替换

# 将输入文本中"xyy" 全局替换为"xff",并只打印替换的那一行,注意这里不能省略最后的"p"命令
sed -n 's/xyy/xff/gp' passwd
20220314154027

删除某行

nl passwd | grep "xyy"

删除第30行

sed -i '30d' passwd

关于 sed 命令就介绍这么多,你如果希望了解更多 sed 的高级用法,你可以参看如下链接:

  • sed 简明教程

  • sed 单行脚本快速参考

  • sed 完全手册

十四、awk 文本处理语言

既然称得上一门语言,那不是草草几章就能学完的,本节的目的在于一个入门awk的学习和一些基本操作。

AWK 是一种优良的文本处理工具,Linux 及 Unix 环境中现有的功能最强大的数据处理引擎之一。其名称得自于它的创始人 Alfred Aho(阿尔佛雷德·艾侯)、Peter Jay Weinberger(彼得·温伯格)和 Brian Wilson Kernighan(布莱恩·柯林汉)姓氏的首个字母 AWK,三位创建者已将它正式定义为“样式扫描和处理语言”。它允许你创建简短的程序,这些程序读取输入文件、为数据排序、处理数据、对输入执行计算以及生成报表,还有无数其他的功能。最简单地说,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 操作体验

先用 vim 新建一个文本文档:

vim test

包含如下内容:

I like linux
www.shiyanlou.com
  • 使用 awk 将文本内容打印到终端:

# "quote>" 不用输入
awk '{
quote> print
quote> }' test

或者写到一行

awk '{print}' test

20220314160823

说明:在这个操作中我是省略了 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,它表示引用当前记录(当前行)的全部内容。

20220314161924

将 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 时应该尽量按照我那样的方式分多行按照一般程序语言的换行和缩进来输入,而不是全部写到一行(当然这在你熟练了之后是没有任何问题的)。

20220314163020

awk 常用的内置变量

变量名

说明

FILENAME

当前输入文件名,若有多个文件,则只表示第一个。如果输入是来自标准输入,则为空字符串

$0

当前记录的内容

$N

N 表示字段号,最大值为NF变量的值

FS

字段分隔符,由正则表达式表示,默认为空格

RS

输入记录分隔符,默认为 \n,即一行为一个记录

NF

当前记录字段数

NR

已经读入的记录数

FNR

当前输入文件的记录数,请注意它与 NR 的区别

OFS

输出字段分隔符,默认为空格

ORS

输出记录分隔符,默认为 \n

关于awk的内容本课程将只会包含这些内容,如果你想了解更多,请期待后续课程,或者参看一下链接内容:

  • awk 程序设计语言

  • awk 简明教程

  • awk 用户指南

十五、软件安装

在学习这种安装方式之前有一点需要说明的是,在不同的 linux 发行版上面在线安装方式会有一些差异包括使用的命令及它们的包管理工具,因为我们的开发环境是基于 ubuntu 的,所以这里我们涉及的在线安装方式将只适用于 ubuntu 发行版,或其它基于 ubuntu 的发行版如国内的 ubuntukylin(优麒麟),ubuntu 又是基于 debian 的发行版,它使用的是 debian 的包管理工具 dpkg,所以一些操作也适用于 debian。而在一些采用其它包管理工具的发行版如 redhat,centos,fedora 等将不适用(redhat 和 centos 使用 rpm)

下面的所有命令不能保证在所有linux版本上都可以使用,请自行尝试和学习。

yum包管理介绍

yum( Yellow dog Updater, Modified)是一个在 Fedora 和 RedHat 以及 SUSE 中的 Shell 前端软件包管理器。

基于 RPM 包管理,能够从指定的服务器自动下载 RPM 包并且安装,可以自动处理依赖性关系,并且一次安装所有依赖的软件包,无须繁琐地一次次下载、安装。

yum 提供了查找、安装、删除某一个、一组甚至全部软件包的命令,而且命令简洁而又好记

yum常用命令

# 列出所有可更新的软件清单命令
yum check-update

更新所有软件

yum update

仅安装指定的软件

yum install <package-name>

仅更新指定的软件命令

yum update <package_name>

列出所有可安装的软件清单

yum list

删除软件包

yum remove <package-name>

查找软件包

yum search <keyword>

清除缓存

# 清除缓存目录下的软件包
yum clean packanes
# 清除缓存目录下的 headers
yum clean headers
# 除缓存目录下旧的 header
yum clean oldheaders
# 清除缓存目录下的软件包及旧的 headers
yum clean, yum clean all (= yum clean packages; yum clean oldheaders)

文件下载

  • wget命令

这是一个用于从互联网下载文件的Linux和UNIX命令。下载单个文件、下载多个文件、恢复下载、节制下载速度和镜像远程站点等。

  1. 下载单个文件

wget 下载链接
20220410080757
  1. 下载时重命名文件

# 使用-O参数对下载的文件重命名
wget -O rename 下载链接
  1. 多文件下载

# 使用HTTP和FTP协议进行多个文件的下载
wget 下载链接1 下载链接2
20220410081412
  1. 下载指定文件夹中的多个链接

# 使用 -i 参数来完成这个功能

保证文件中存放了多个下载链接且每一条链接独立成行

cat download-linux.txt

https://releases.ubuntu.com/20.04.3/ubuntu-20.04.3-desktop-amd64.iso
https://download.rockylinux.org/pub/rocky/8/isos/x86_64/Rocky-8.5-x86_64-dvd1.iso
https://cdimage.debian.org/debian-cd/current/amd64/iso-dvd/debian-11.2.0-amd64-DVD-1.iso

使用下面的命令执行下载

wget -i download-linux.txt

  1. 静默下载

# 使用-q参数开启静默下载,下载不显示过程
wget -q 下载链接
  1. 断点续传

# 使用-c参数来开启文件下载的断点下载模式,顾名思义在文件下载失败时会再次尝试继续下载。
wget -c 下载链接

更多功能可以

参考文章-1:10 Wget (Linux File Downloader) Command Examples in Linux

参考文章-2:Linux and Unix wget command tutorial with examples

十六、Linux 进程概念

Linux 中也难免遇到某个程序无响应的情况,可以通过一些命令来帮助我们让系统能够更流畅的运行。 而在此之前,我们需要对进程的基础知识有一定的了解,才能更好、更有效率的使用 Linux 提供的工具。

  • 进程与程序

  • 进程的衍生

  • 工作管理

首先程序与进程是什么?程序与进程又有什么区别?

程序(procedure):不太精确地说,程序就是执行一系列有逻辑、有顺序结构的指令,帮我们达成某个结果。就如我们去餐馆,给服务员说我要牛肉盖浇饭,她执行了做牛肉盖浇饭这么一个程序,最后我们得到了这么一盘牛肉盖浇饭。它需要去执行,不然它就像一本武功秘籍,放在那里等人翻看。

进程(process):进程是程序在一个数据集合上的一次执行过程,在早期的 UNIX、Linux 2.4 及更早的版本中,它是系统进行资源分配和调度的独立基本单位。同上一个例子,就如我们去了餐馆,给服务员说我要牛肉盖浇饭,她执行了做牛肉盖浇饭这么一个程序,而里面做饭的是一个进程,做牛肉汤汁的是一个进程,把牛肉汤汁与饭混合在一起的是一个进程,把饭端上桌的是一个进程。它就像是我们在看武功秘籍这么一个过程,然后一个篇章一个篇章地去练。

简单来说,程序是为了完成某种任务而设计的软件,比如 vim 是程序。什么是进程呢?进程就是运行中的程序。

程序只是一些列指令的集合,是一个静止的实体,而进程不同,进程有以下的特性:

  • 动态性:进程的实质是一次程序执行的过程,有创建、撤销等状态的变化。而程序是一个静态的实体。

  • 并发性:进程可以做到在一个时间段内,有多个程序在运行中。程序只是静态的实体,所以不存在并发性。

  • 独立性:进程可以独立分配资源,独立接受调度,独立地运行。

  • 异步性:进程以不可预知的速度向前推进。

  • 结构性:进程拥有代码段、数据段、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)。

如图中第四列标题是 S,S 表示的是进程的状态,而在下属的第三行的 Z 表示的是 Zombie 的意思。( ps 命令将在后续详解)

既然子进程是通过父进程而衍生出来的,那么子进程的退出与资源的回收定然与父进程有很大的相关性。当一个子进程要正常的终止运行时,或者该进程结束时它的主函数 main() 会执行 exit(n); 或者 return n,这里的返回值 n 是一个信号,系统会把这个 SIGCHLD 信号传给其父进程,当然若是异常终止也往往是因为这个信号。

在将要结束时的子进程代码执行部分已经结束执行了,系统的资源也基本归还给系统了,但若是其进程的进程控制块(PCB)仍驻留在内存中,而它的 PCB 还在,代表这个进程还存在(因为 PCB 就是进程存在的唯一标志,里面有 PID 等消息),并没有消亡,这样的进程称之为僵尸进程(Zombie)。

如图中第四列标题是 S,S 表示的是进程的状态,而在下属的第三行的 Z 表示的是 Zombie 的意思。( ps 命令将在后续详解)

20220315101257

正常情况下,父进程会收到两个返回值: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() 来创建系统里其他的进程,所以它是所有进程的父进程或者祖先进程。同时它是一个守护程序,直到计算机关机才会停止。

通过以下的命令我们可以很明显的看到这样的结构

20220315101417

或者从此图我们可以更加形象的看清子父进程的关系

实验楼

通过以上的显示结果我们可以看的很清楚,init 为所有进程的父进程或者说是祖先进程

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

ps -fxo user,ppid,pid,pgid,command
实验楼

可以在图中看见我们执行的 ps 就是由 zsh 通过 fork-exec 创建的子进程而执行的

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

就像一个树状图,而 init 进程就是这棵树的根,其他进程由根不断的发散,开枝散叶

进程组与 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 来终止它,但是若是在后台的话就不行了。

我们可以通过 & 这个符号,让我们的命令在后台中运行:

20220315102004

图中所显示的 [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。

Linux进程管理

掌握一些 Linux 所提供的工具来进行进程的查看与控制,掌握这些工具让我们能在某些进程出现异常的时候及时查看相关的指标,从而解决问题。

  • 查看进程的运行状态

  • 进程的结束控制

  • 进程的执行顺序

不管在测试的时候、在实际的生产环境中,还是自己的使用过程中,难免会遇到一些进程异常的情况,所以 Linux 为我们提供了一些工具来查看进程的状态信息。我们可以通过 top 实时的查看进程的状态,以及系统的一些信息(如 CPU、内存信息等),我们还可以通过 ps 来静态查看当前的进程信息,同时我们还可以使用 pstree 来查看当前活跃进程的树形结构。

top工具的使用

能实时的查看我们系统的一些关键信息的变化。

top
20220315103032

top 是一个在前台执行的程序,所以执行后便进入到这样的一个交互界面,正是因为交互界面我们才可以实时的获取到系统与进程的信息。在交互界面中我们可以通过一些指令来操作和筛选。在此之前我们先来了解显示了哪些信息。

我们看到 top 显示的第一排,

内容

解释

top

表示当前程序的名称

10:30:04

表示当前的系统的时间

up 2 days,17:52

表示该机器已经启动了多长时间

1 user

表示当前系统中只有一个用户

load average: 0.00,0.02,0.00

分别对应 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 表示进程请求超过 CPU 工作能力的 2 倍。而若是这个值大于 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

20220315103714

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

ps工具的使用

ps 也是我们最常用的查看进程的工具之一,我们通过这样的一个命令来了解一下,它能给我们带来哪些信息:

ps aux
ps axjf

使用 -l 参数可以显示自己这次登录的 bash 相关的进程信息罗列出来

ps -l

若是查找其中的某个进程的话,我们还可以配合着 grep 和正则表达式一起使用:

ps aux | grep zsh
20220315105205

当然如果你觉得使用这样的此时没有把你想要的信息放在一起,我们也可以是用这样的命令,来自定义我们所需要的参数显示:

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

pstree工具使用

通过 pstree 可以很直接的看到相同的进程数量,最主要的还是我们可以看到所有进程之间的相关性。

pstree
20220315105522

参数选择

解释

-A

程序树之间以 ASCII 字符连接

-p

同时列出每个 process 的 PID

-u

同时列出每个 process 的所属账户名称

kill命令的使用

尝试用 kill 来操作下一些不属于 job 范畴的进程,直接对 pid 下手。

# 首先我们使用图形界面打开了 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
20220315105929

十七、日志系统

日志数据可以是有价值的信息宝库,也可以是毫无价值的数据泥潭。它可以记录下系统产生的所有行为,并按照某种规范表达出来。我们可以使用日志系统所记录的信息为系统进行排错,优化系统的性能,或者根据这些信息调整系统的行为。收集你想要的数据,分析出有价值的信息,可以提高系统、产品的安全性,还可以帮助开发完善代码,优化产品。日志会成为在事故发生后查明“发生了什么”的一个很好的“取证”信息来源。日志可以为审计进行审计跟踪。

  • 常见的日志

  • 配置的日志

  • 轮替的日志

可以根据服务对象粗略的将日志分为两类

  • 系统日志

  • 应用日志

系统日志主要是存放系统内置程序或系统内核之类的日志信息如 alternatives.log 、btmp 等等,应用日志主要是我们装的第三方应用所产生的日志如 tomcat7 、apache2 等等。

接下来我们来看看常见的系统日志有哪些,他们都记录了怎样的信息(根据Linux不同分支会有不同的显示)

日志名称

记录信息

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

系统信息记录

更多


关于防火墙

# 查看端口监听状态
netstat -ap | grep 端口

执行以下命令,查看防火墙状态。
systemctl status firewalld

firewall-cmd --state

如果防火墙关闭可以执行以下命令开启。
systemctl start firewalld

如果开启命令执行后提示“Failed to start firewalld.service: Unit is masked.”请执行以下命令后再重新执行开启防火墙的命令。

systemctl unmask firewalld

查看打开的端口
firewall-cmd --zone=public --list-ports

放行TCP 80端口
firewall-cmd --zone=public --add-port=80/tcp --permanent

更新防火墙规则
firewall-cmd --reload

使用ufw

# 开启
2.关闭防火墙sudo ufw disable,打开防火墙sudo ufw enable,
3.开放端口sudo ufw allow 端口号
4.关闭端口sudo ufw deny 端口号
5.重启防火墙sudo ufw reload

特别篇-踩坑宝典

记录Linux使用过程中遇到的一些问题和解决。

问题一

Linux环境:

阿里云CentOS8

在使用yum update/yum -y update命令的时候报一下错误。

2gKQReVb1GEkXnM

解决方案

阿里云解释:centos8官方源已下线,建议切换centos-vault源。

操作步骤

  • 删除AppStream源

rm -f /etc/yum.repos.d/CentOS-AppStream.repo
  • 取消并备份旧yum源(可直接删除)

mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
  • 下载vault源

curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-vault-8.5.2111.repo
  • 清除yum缓存

yum clean all
  • 生成新缓存

yum makecaceh

问题二

普通用户添加了root权限之后,在使用sudo命令时报错

20220312171326

解决方案

在sudoers文件中将用户添加进去即可。

操作步骤

# 切换到root用户
# 授予sudoers可编辑权限
chmod u+w /etc/sudoers
# 打开改文件进行编辑
vi /etc/sudoers
# 找到root ALL=(ALL)  ALL这一行,在其下添加
你的用户名  ALL=(ALL) ALL

关闭读写权限

chmod u-w /etc/sudoers

通过上面的步骤之后,不出意外的话问题得到解决。普通用户也可以使用sudo了!!

美化篇-黑屏不再压抑

安装powerlevel10k主题

安装过程

# 安装git
yum -y install git

安装zsh

yum -y install zsh

安装Oh-My-Zsh curl

sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

or wget

sh -c "$(wget -O- https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

安装P10k

git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k

or gitee镜像

git clone --depth=1 https://gitee.com/romkatv/powerlevel10k.git ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k

在 ~/.zshrc 中设置 ZSH_THEME

ZSH_THEME="powerlevel10k/powerlevel10k"

设置P10k

p10k configure

更换主题

zsh支持很多主题样式,可以在官github自寻!!

主题传送门

其实很多时候我们没必要一上来就去百度或者各种CSDN看一遍,结果还是失败,正确的操作应该是看官方文档。

20220313215012

需要注意的是,有很多主题的特殊字体需要自己下载安装,不然主题效果可能是乱码,所以这里先把字体安装教程走一遍。

字体传送门

以下图片内容来自官网github,防止进不去的小伙伴看看

  • 安装

2022031321444620220313214503
  • 卸载

20220313214550

官方已经的文档已经是很清楚的教程了,选择自己的版本安装即可,就不再赘述。下面开始安装主题。

20220313214738

安装主题(如上图我的是zh的默认主题robbyrussell),下面更换为jonathan。

2022031321530920220313221313

注意这里遇到一个坑哈,如果你使用的xshell工具连接的,再字体正确安装之后还是显示主题乱码,那么可能与你再xshell工具里使用的终端字体有关系,重新更换一个英文字体就好了。

命令总结

上面是图片文档,这里把用到的命令什么的汇总一下吧,毕竟我们都是CV工程师(滑稽脸)

  • 字体相关

注意我这里用的是CentOS8 Stream环境。其他环境还请移步官网哈,(上面有链接)

# 1.克隆
git clone https://github.com/powerline/fonts.git --depth=1

2.安装

cd fonts
./install.sh

清理

cd ..
rm -rf fonts

  • 主题相关

# 打开配置文件并编辑主题
vim ~/.zshrc

保存退出

:wq

使修改立即生效

source ~/.z

食用方法

根据官方的说明可以看出,更换主题大致分为以下几步。

  • 物色主题,选择你中意的主题,记下名称。

  • 打开~/.zshrc文件

  • 找到ZSH_THEME字段,该字段=后面的写上你主题名称即可。

实战篇-面试题

历史命令

描述

2016 年百度校招面试题

在 Linux 中,对于文本的处理和分析是极为重要的,现在有一个文件叫做 data1,可以使用下面的命令下载:

cd /home/shiyanlou
wget https://labfile.oss.aliyuncs.com/courses/1/data1

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

目标

  1. 处理文本文件 /home/shiyanlou/data1

  2. 将结果写入 /home/shiyanlou/result

  3. 结果包含三行内容,每行内容都是出现的次数和命令名称,如“100 ls”

提示

  1. cut 截取 (参数可以使用 -c 8-,使用 man cut 可以查看含义)

  2. uniq -dc 去重

  3. sort 的参数选择 -k1 -n -r

  4. 操作过程使用管道,例如:

参考答案

cat data1 |cut -c 8-|sort|uniq -dc|sort -rn -k1 |head -3 > /home/shiyanlou/result
image-20220313203117239

扩展篇

在Linux基础之上,学习并扩展。

跨服务器传输

之前将远程服务器上的文件存到本地Windows系统里面,每次都得打开xsehll来进行,同样将本地文件传输到远程也是,最近发现一个好用得命令,scp。

scp命令

Linux scp 命令用于 Linux 之间复制文件和目录。

scp 是 secure copy 的缩写, scp 是 linux 系统下基于 ssh 登陆进行安全的远程文件拷贝命令。

scp 是加密的,rcp 是不加密的,scp 是 rcp 的加强版。

语法

scp [-1246BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]
[-l limit] [-o ssh_option] [-P port] [-S program]
[[user@]host1:]file1 [...] [[user@]host2:]file2

简易写法:

scp [可选参数] file_source file_target

参数说明:

  • -1: 强制scp命令使用协议ssh1

  • -2: 强制scp命令使用协议ssh2

  • -4: 强制scp命令只使用IPv4寻址

  • -6: 强制scp命令只使用IPv6寻址

  • -B: 使用批处理模式(传输过程中不询问传输口令或短语)

  • -C: 允许压缩。(将-C标志传递给ssh,从而打开压缩功能)

  • -p:保留原文件的修改时间,访问时间和访问权限。

  • -q: 不显示传输进度条。

  • -r: 递归复制整个目录。

  • -v:详细方式显示输出。scp和ssh(1)会显示出整个过程的调试信息。这些信息用于调试连接,验证和配置问题。

  • -c cipher: 以cipher将数据传输进行加密,这个选项将直接传递给ssh。

  • -F ssh_config: 指定一个替代的ssh配置文件,此参数直接传递给ssh。

  • -i identity_file: 从指定文件中读取传输时使用的密钥文件,此参数直接传递给ssh。

  • -l limit: 限定用户所能使用的带宽,以Kbit/s为单位。

  • -o ssh_option: 如果习惯于使用ssh_config(5)中的参数传递方式,

  • -P port:注意是大写的P, port是指定数据传输用到的端口号

  • -S program: 指定加密传输时所使用的程序。此程序必须能够理解ssh(1)的选项。

使用实例

  • 传输文件:本地->远程

将本地的a.zip文件上传到远程服务器的/root目录下的test文件夹下。

scp a.zip root@121.43.101.7:/root/test

以上命令执行之后会提示输入远程服务器的密码,正确之后开始传输,速度还是可以的。

  • 传输目录本地->远程

将本地E盘下名为xuyi的目录,目录结构:/xuyi/img/test.png,传输到远程/root/test目录下。

scp -r /xuyi/ root@121.43.101.7:/root/test

同样需要输入密码。


  • 传输文件:远程->本地

将远程服务器/root/hello_word.c文件传输到本地/xuyi/目录下。

scp root@121.43.101.7:/root/hello_word.c /xuyi/

需要验证密码

  • 传输目录:远程-> 本地

将远程.root下的test目录传输到本地xuyi目录下

scp -r root@121.43.101.7:/root/test/ /xuyi/

需验证密码

特别的

如果远程服务器防火墙有为scp命令设置了指定的端口,我们需要使用 -P 参数来设置命令的端口号,命令格式如下:

#scp 命令使用端口号 4588
scp -P 4588 remote@www.runoob.com:/usr/local/sin.sh /home/administrator

版权声明

本文内容版权归作者或相关权利人所有。转载、引用或其他使用请遵循相应授权条款,并保留本文链接。

本文链接:https://xuyi.dev/2026-05-01-fxxury