Linux进阶学习

跟着红岩网校学习进行记录。

v2-dde4fe300f122dd93bb24443daa19e97_1440w

一些好用的命令使用

Man pages

想要查阅一个命令的帮助手册,只需要man下就可以了。

例如: man Is

这里提供了一个将Man pages改为中文的方式

apt-get install manpages-zh

vim /etc/manpath.config

先输入一个冒号再打出以下命令

1 ,$s#/usr/share/man#/usr/share/man/zh_CN#g

Man常用的快捷:

1
2
3
4
5
6
7
8
9
10
11
12
q 退出

Enter 按行下翻Space|f 按页下

b 上翻一页

h 查看帮助文档
-f:显示与指定关键字相关的手册页面。
-k:搜索手册页中与关键字匹配的条目。
-a:显示所有匹配的手册页面。
-w:仅显示手册页的位置,而不显示其内容。
man -f grep man -k network man -a ls man -w printf

tldr

tldr(Too Long; Didn‘t Read),它简化了烦琐的man指令帮助文档,安装如下:

1
2
3
4
5
sudo apt-get install tldr   

sudo mkdir -p /root/.local/share/tldr(如果这个目录就忽略这条)

sudo tldr --update 更新完就可以开始使用啦

安装的时候可能会出现

image-20231028182325047

原因是无法找到tldr的安装包,需要对数据库进行升级,执行:

1
sudo apt-get update

然后在重复安装的过程即可。

SCP

scp是secure copy的简写,用于在Linux下进行远程拷贝文件的命令命令格式如下

1
scp FILE user@0.0.0.0:/dir

tree

尽管只是文本界面,才是更好地显示 Linux 目录树结构的工具。

1
sudo apt install tree

一旦安装好,在终端窗口运行 tree 命令:

1
tree -L 1 /

上面的指令可以翻译为“只显示以 /(根目录) 开头的目录树的第一级”。 -L(level) 选项告诉树你想看到多少层目录。

image-20231028183205968

Linux根目录

/bin 此目录存放所有用户的命令
/boo t 不要动它!Linux内核及引导系统程序所需的文件
/dev 所有设备文件的目录(如声卡、磁盘、光驱)
/etc 主要存放了系统配置方面的文件
/home 各个用户的家目录
/lib 库文件存放目录
/lost+found 当系统意外崩溃或意外关机时,用于修复的文件碎片
/mnt 用于临时挂载存储设备
/var 存放内容常变动的文件目录
/opt 自定义软件安装存放目录
/proc 进程及内核信息存放目录
/root root用户家目录
/tmp 临时文件目录
/usr 系统存放程序的目录
/sbin 给root用户使用的命令
/sys 系统内核参数

Linux 链接

在Linux系统中有一种特殊的文件类型叫链接。

链接分为两种:

  • 硬链接:硬链接和原文件必须处于同一个文件系统,删除源文件对硬链接不会有影响,硬链接和原链接实际上是对同一份数据的两个引用,同时无法建立文件夹的硬链接。
  • 软链接又叫符号链接,它类似快捷方式,指向一个路径,因此可以跨文件系统建立,当原文件被删除时,软链接也无法取得数据
1
ln [-s] 原文件路径  目标文件路径    

ls –l /

image-20231028201833460

以下对每类代表的含义进行解释

8

1
2
3
4
5
6
7
8
9
10
11
lrwxrwxrwx                                   **文件权限管理**

1 硬链接|文件数量

root root 文件所属用户与文件所属用户组

7 文件大小

Aug 21 14:50 最后修改时间

bin -> usr/bin

Linux文件权限管理

对于一个文件夹来说,如果一个用户没有文件夹的执行权限(x),则改用无法进入该文件夹

使用命令chmod改变文件权限

1
2
chmod   u+x    redrock
chmod 766 redrock

读取 r 4 二进制:100

写入 w 2 二进制:010

执行 x 1 二进制:001

•使用命令chown可以更改文件所属用户或同时修改所属用户和所属组

1
2
chown root redrock   更改文件redrock的所属用户为root
chown root:redrocker redrock 更改文件redrock的所属用户为root所属组为redrocker

•使用命令chgrp,可以单独修改文件所属组

1
chgrp redrocker redrock    更改文件redrock的所属用户组为toot

shell

什么是 shell?

•Shell 广义上泛指一切为用户提供交互界面的程序。这里狭义上的 shell 指在命令行界面和用户交互的程序。

•Linux 下常用的 shell 主要是 Bourne Again Shell (bash),另外还有 Bourne Shell (sh)、Z Shell (zsh) 等常用的 shell。

•shell 之间语法往往有细微差别,但通常语法上兼容 Bourne Shell。

数据流(data stream)

是一组有序,有起点和终点的字节的数据序列。

输入输出流

•在 Linux 中,有预设的三个输入输出流:

–标准输入流 stdin

–标准输出流 stdout

–标准错误流 stderr

•shell 中我们也可以进行流的重定向:

•使用 > 将程序的标准输出写入到某个文件

•使用 >> 将程序的标准输出追加写入到某个文件

•使用 < 将某个文件作为程序的标准输入

•使用 2> 将程序的标准错误输出写入到某个文件

•使用 2>> 将程序的标准错误输出追加写入到某个文件

管道

•有时候,我们希望将多个命令组合起来来完成某些任务。

•在 shell 中,管道是达成这个目标的理想工具。它将上一个命令的标准输出流和下一个命令的标准输入流连接起来。

•管道符号为 |,并且管道可以链式使用。

image-20231028210413343

后台任务

•在命令结尾附加“&”,即可让任务转入后台运行。

•Shell 退出时,会杀死所有后台任务。

•前台命令运行时,可以使用 Ctrl+Z 将命令挂起,然后使用 bg 命令即可使其转入后台运行。

•使用 fg 命令可以将后台命令转回前台执行。

•如果没有其它配置,后台任务仍然会向前台终端写入输出,可以使用 jobs 来查看。

•如果希望后台任务不被杀死,可以使用 nohup 运行程序,也可以对已经在后台执行的任务 disown。

变量

•Shell 也是一门编程语言,它也有变量。

•Shell 变量值的类型全部为字符串。

•设置变量的方法:

name=redrock # 注意这里等号两边不能有空格

•访问变量时,需要带着 $ 符号访问,例如:

1
echo $name

•在双引号括起来的字符串中,可以访问变量:

1
echo "Hello $name"   或  echo "Hello ${name}"

输出为:Hello redrock

字符串命令替换

•有时一串字符串的内部,我们可以使用命令的输出,使其作为字符串的一部分:

1
var=“something other words $(command to be executed)"

•也可以使用反引号 `:

1
var="`command to be executed`"

注意,当前shell环境下定义的变量为局部变量,只对当前shell有效。

环境变量

•为了方便程序获知外部状态,系统提供了环境变量。

•可以使用 env 查看系统的环境变量。

•在 shell 中,PATH 环境变量用于 shell 检索命令,它们之间使用冒号分隔。

•可以使用export来在当前会话设定环境变量,也可以在语句前方加上 VARNAME=“CONTENT” 的格式允许程序获取

•使用 whereis 命令可以查看一个命令的所有可能路径。

部分常用特殊变量

变量 作用
$0,$1,…,$n 参数列表的第 1-n 个参数
$@/$* 参数列表(引号括起来表现为多个/一个参数)
$# 参数个数
$? 上一条命令的返回值
$! 上一条后台运行的程序的 PID
$$ Shell 的 PID

列表

•Shell 中存在列表,它输出时表现往往和用空格分隔的一串字符串一致。

•定义一个列表:

1
2
3
list=(1 2 3) | list=([0]=1 [1]=2 [2] =3)

list[0]=1;list[1]=2;list[2]=3

• 增加元素:

1
list+=(4 5 6)

• 删除元素:

1
unset list[1]

• 列表长度:

1
${#list[@]} || ${#list[*]}

模式扩展

Shell 接收到用户输入的命令以后,会根据空格将用户的输入,拆分成一个个词元。然后,Shell 会扩展词元里面的特殊字符,扩展完成后才会调用相应的命令。

Bash 是先进行扩展,再执行命令。因此,扩展的结果是由 Bash 负责的,与所要执行的命令无关。命令本身并不存在参数扩展,收到什么参数就原样执行。这一点务必需要记住。

字符样例 作用
? 匹配文件路径里任意确定的一个字符
* 匹配文件路径里面任意多个字符
[abc] 匹配一个字符,其必为 abc 中的一种
[a-z] 匹配一个字符,其必在 a-z 之间
[^abc] 匹配一个字符,其必不为 abc 中的一种

大括号扩展

Shell 可以通过简单的表达式生成列表。

基本语法:{begin..end}

例:L{a..k}

结果:La Lb Lc Ld Le Lf Lg Lh Li Lj Lk

当我们将多个花括号放在一起:{a..f}{1..4}

结果: a1 a2 a3 a4 b1 b2 b3 b4 c1 c2 c3 c4 d1 d2 d3 d4 e1 e2 e3 e4 f1 f2 f3 f4

Shell 将其所有排列的可能性进行了枚举。

数值计算

Shell 虽然都是字符串,但满足字符串表示整数的规则下可以进行数值计算,数值计算需要使用 $(( )) 括起来。

image-20231028212626664

转义与引用

•由于 shell 中许多字符有特殊的语法含义,在直接表示这些字符时就需要转义以防产生歧义。

•通常使用反斜杠 \ 进行转义,例如参数本身含有空格时,空格应当被转义为 \ 。反斜杠 \ 本身使用 \ 进行转义。

•使用双引号 “ 括住的字符串称为弱引用,能消除空格的歧义,但变量仍然会被展开,里面包括双引号时需要转义为 "。同时如果想直接输出 $,也需要转义为 $。

•使用单引号 ‘ 括住的字符串称为强引用,它内部会忽略任何转义,因此强引用中不能包含单引号。

内部命令

并非所有 shell 命令都是外部的可执行文件,还有许多命令是 shell 内部存在的。例如 cd、fg、bg 等。

在 Bash 中,可以使用 help 命令查看所有内部命令。

配置文件

大部分 shell 都在用户目录下存在配置文件,用于用户自定义启动 shell 时需要加载的配置。

对于 Bash,其配置文件为 ~/.bashrc 或者 ~/.bash_profile。

对于 Zsh,其配置文件为 ~/.zshrc。

/etc/profile 会在登录时被当前帐户登录 shell 执行。

/etc/bash.bashrc 会在 shell打开时执行。

在ctf中常用到的Linux命令

Linux pwd命令

Linux pwd(英文全拼:print work directory) 命令用于显示工作目录。(持续收集整理中)

开始编写第一个 Shell 脚本

脚本的第一行通常是 Unix shebang,其结构为:

#!/path/to/interpreter

Unix shebang 指示了脚本应当被哪个解释器解释,通常写 Shell 脚本常用的 shebang 有:

#!/bin/sh 使用 POSIX shell

#!/bin/bash 使用 Bash

写完脚本后,赋予脚本执行权限,即可直接运行。

判断

•我们规定,程序 main 函数返回 0 代表执行成功,因此 Shell 中 0 为真值。

•如果需要进行逻辑判断,可以使用中括号构造判断表达式,中括号其实也是一个命令,它的返回值即为逻辑判断真假。

•命令结尾使用 “&&” 连接下一个命令则下一个命令只在当前命令成功执行时执行。

•命令结尾使用 “||” 连接下一个命令则下一个命令只在当前命令执行失败时执行。

•使用上述两种符号连接的命令被视为一条命令,最终返回值为最后执行的命令的返回值。

运算符 作用
-e filename 判断 filename 存在
-d/-f/-L filename 判断 filename 是文件夹/文件/符号链接
-r/-w/-x filename 判断 filename 可读/可写/可执行
f1 -nt/-ot f2 判断 f1 比 f2 新/旧
-z/-n string 判断 string 为空/非空
str1 =/!= str2 判断 str1 和 str2 相等/不相等
n1 -eq/-ne n2 判断数字 n1 和 n2 相等/不相等
n1 -lt/-le n2 判断数字 n1 小于/小于等于 n2
n1 -gt/-ge n2 判断数字 n1 大于/大于等于 n2

If 语句

Shell 的 if 语句结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
if condition; then           if condition     (省略分号写法)

do_something then

elif condition; then do_something

do_something fi

else

do_something

fi

其中 condition 可为一条语句的返回值,中括号判断表达式也十分常用。

Case 语句

1
2
3
4
5
6
7
8
9
10
11
12
Shell 的 switch-case 语句结构如下:
case $var in
"case1")
do_something1
;;
"case2")
do_something2
;;
*)
do_default_thing
;;
esac

While 语句

Shell 的 while 语句和 if 写法类似,结构如下:

1
2
3
4
5
6
7
while condition; do              while condition   (省略分号写法,与if同理)

do_something do

done do_something

done

break 和 continue 在 shell 中也存在。

For语句

Shell 的 for 语句有多种形态,最普遍的一种结构如下:

1
2
3
4
5
for x in $list; do

do_something $x

done

变量 x 是从列表 list 中迭代的,这与你们学的 Python 类似

Bash 中提供了一种类似 C 风格的 for:

1
2
3
4
5
for ((i=0;i<100;i++)); do

do_something $i

done

这是 Bash 的语法扩展。在 POSIX sh 中,这种写法是不合法的

在这种 C 风格的 for 循环判定条件中,访问变量不需要加“$”前缀。

函数

Shell 的函数可以认为是用户定义的命令,它有独立的参数列表:

要定义一个函数,只需

1
2
3
4
5
funcName() {

do_something…

}

在函数内部任意位置可以通过 return 立即终止函数,shell 函数不通过返回值来传递执行结果,而是通过输出。

函数的执行是在当前shell环境下执行,而不是在子shell环境下调用一个函数和调用一条命令类似:

1
funcName arg1 arg2

在函数内部,可以使用 $1、$2 甚至 $@ 访问参数列表。

有时我们可能希望跳过当前参数,只需要使用 shift 命令,$1 后的参数就会依次向前进 1,当前的 $1 会被丢弃。

Debug

Shell 脚本从编程语言设计的角度来看并不出众,甚至可以说它的设计十分糟糕,再加上不同 shell 变体实现各不相同,写完脚本后我们往往难以发现其中的问题。

Shellcheck 是一个 shell 脚本静态检查器。它检查脚本的代码并给出修改反馈。

https://shellcheck.net

如果需要动态地查看脚本的执行流程,可以对 shell 附加 -x 参数。

启动与服务

启动方式

将储存在硬盘内部的系统启动程序,读取进计算机内存,将启动管理权交予计算机系统

1.BIOS 启动方式

•BIOS(Basic Input Output System)即基本输入/输出系统。它是被固化在计算机ROM芯片上的一组程序。它是微机系统软、硬件之间的一个可编程接口。

•BIOS的主要功能概括来说包括如下几部分:

•1)POST(Power On Self Test)加电自检,检测机器的状态

•2)Setup进行系统设置,存于CMOS中。一般开机时按Del或者F2进入到BIOS的设置界面。

•3)启动自举程序,自举程序将读取引导记录,装载操作系统。

•按下电源键

•Bios被读入内存

•POST自检

•Bios常驻程序读入内存

•根据boot启动顺序,依次尝试启动系统

•读取硬盘的位于第0柱面、0磁头、1扇区MBR(主扇区引导记录 Main/Master Boot Record),将其读入内存地址0x7C00

•执行MBR,启动系统OS引导程序,Bios启动阶段完成。

2.UEFI 启动方式

统一可扩展固件接口(英语:Unified Extensible Firmware Interface,缩写UEFI)是一种个人电脑系统规格,用来定义操作系统与系统固件之间的软件界面,作为BIOS的替代方案[1]。可扩展固件接口负责加电自检(POST)、联系操作系统以及提供连接操作系统与硬件的接口。· 硬盘划分专门的,EFI系统分区(ESP),ESP分区内部存有xxx.efi格式的启动二进制文件。使用哪些文件由UEFI管理器选择。

UEFI启动管理器支持的启动方式:

1.兼容bios的启动方式,硬盘仍采取

原始bios启动方式,不存在ESP分区,UEFI管理器会自动为这类硬盘自动生成一个efi文件,采用类似bios的启动方式启动

\2. “回退路径”(Fallback Path)UEFI原生启动项。如图中倒数两项,UEFI会扫描整个磁盘找到ESP分区,

然后在分区内寻找 boot[架构].efi文件

(x64架构计算机,寻找bootx64.efi),

选择其作为加载项。具体启动的是哪个OS,就看bootx64.efi文件实际

是启动windows还是Linux了

3.UEFI原生启动项,如图1、2、3项。此项由OS或人为使用efibootmgr等工具,写入UEFI管理器,它告诉UEFI其具体指向哪个efi文件,不必每次

启动都扫描硬盘。如windows路径常为efi\microsoft\boot\bootmgfw.efi

内核启动

•Linux内核启动后,将会由init进程接管整个系统,init进程负责进一步的系统启动

•Init的进程ID(PID)为1

•补充知识:进程是正在运行的程序实例,当你启动一个程序时,它将获得一个唯一ID,由操作系统分配,作为系统标识。

•传统Linux发行版init进程主要使用System V。然而现代大多数Linux发行版主要使用systemd,所以本次课程讲述systemd的相关细节。

image-20231028213913762

systemd

我们使用systemctl来操作服务。

•启动服务: systemctl start ServiceName.service

•停止服务: systemctl stop ServiceName.service

•重启服务: systemctl restart ServiceName. Service

•查看服务的状态: systemctl status ServiceName.service

•使服务可以开机自启: systemctl enable ServiceName.service

•使服务不能开机自启: systemctl disable ServiceName.service

•重新加载配置: systemctl reload ServiceName.service

Systemd-service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[Unit]

Description=Hello World

After=network-online.target

Requires=network-online.target

[Service]

TimeoutStartSec=0

ExecStartPre=/usr/bin/docker pull busybox

ExecStart=/usr/bin/docker run busybox /bin/ sh

ExecStop="/usr/bin/docker stop busybox1"

ExecStopPost="/usr/bin/docker rm busybox1"

[Install]

WantedBy=multi-user.target

Unit 段 所有unit类型服务都有该字段

•Description:描述这个 Unit 文件的信息

Requires:依赖的其它 Unit 列表,列在其中的 Unit 模板会在这个服务启动时的同时被启动。并且,如果其中任意一个服务启动失败,这个服务也会被终止

After:与 Requires 相似,但是在后面列出的所有模块全部启动完成以后,才会启动当前的服务

Service .service类型unit****特有

•ExecStart:启动当前服务的命令

•ExecStartPre:启动当前服务之前执行的命令

•ExecStop:停止当前服务时执行的命令

•ExecStopPost:停止当前服务之后执行的命令

•RestartSec:自动重启当前服务间隔的秒数

•Restart:定义何种情况 Systemd 会自动重启当前服务,可能的值包括 always(总是重启)、on-success、on-failure、on-abnormal、on-abort、on-watchdog

•TimeoutStartSec:启动服务时等待的秒数超过视为失败。

•WantedBy:和 Unit 段的 Wants 作用相似,只有后面列出的不是服务所依赖的模块,而是依赖当前服务的模块。它的值是一个或多个 Target

Systemd自定义target文件

image-20231028214121788

Systemd timer

•Timer单元是以 “.timer” 为后缀的单元文件, 封装了一个由 systemd 管理的定时器, 以支持基于定时器的启动。

•注意,如果在启动时间点到来的时候,匹配的单元已经被启动, 那么将不执行任何动作,也不会启动任何新的服务实例。 因此,那些设置了RemainAfterExit=yes(当该服务的所有进程全部退出之后,依然将此服务视为处于活动状态)的服务单元一般不适合使用基于定时器的启动。 因为这样的单元仅会被启动一次,然后就永远处于活动(active)状态了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[Unit]

Description=Hello World

After=docker.service

Requires=docker.service

[Timer]

OnBootSec=5hour 30min

OnUnitStartSec =5w 3d 6h 30m 10s 110ms 1us

OnCalendar =Wed,Thu 2022-6..7~04 *:2/1:0

OnCalendar =minutely

[Install]

WantedBy=multi-user.target

Timer .timer类型unit特有

•OnActiveSec: 相对于这个timer单元自身启动的时间

•OnBootSec:相对于机器被启动的时间点,也就是内核开始运行的时间点

•OnStartupSec:相对于 systemd 被首次启动的时间点,也就是内核启动init进程的时间点

•OnUnitActiveSec:相对于匹配单元最后一次被启动的时间点;

•OnUnitInactiveSec:相对于匹配单元 最后一次被停止的时间点;

•OnCalendar:定义基于挂钟时间(wallclock)的日历定时器,值是一个日历事件表达式