Linux Shell/Bash 常用命令和适用场景

本文旨在分类汇总Linux下常用命令及适用场景

什么是Shell?

当谈到命令行时,我们实际上指的是shell。shell是一个接收由键盘输入的命令,并将其传递给操作系统来执行的程序。几乎所有的Linux发行版都提供shell程序,该程序来自于称之为bash的GNU项目。bash是Bourne Again Shell的首字母缩写,Bourne Again Shell基于这样一个事实,即bash是sh的增强版本,而sh是最初的UNIX shell程序,由Steve Bourne编写。

当然市面上的shell种类远不止这两个,还有其它的shell,比如,ash, korn, tcsh,还有就是同事经常使用的 zsh,配合oh-my-zsh使用起来比较爽(感兴趣可以研究)。

以下均以bash为例子,所有涉及到的命令,均可以在 man 手册中详细查看。
man 的级别:(man 也可以 man 可以详细查看级别)
一般情况下,man手册会根据传入的参数,判断属于什么级别。
man的级别如下:

      The table below shows the section numbers of the manual followed by the types of pages they contain.

       1   Executable programs or shell commands
       2   System calls (functions provided by the kernel)
       3   Library calls (functions within program libraries)
       4   Special files (usually found in /dev)
       5   File formats and conventions, e.g. /etc/passwd
       6   Games
       7   Miscellaneous (including macro packages and conventions), e.g. man(7), groff(7), man-pages(7)
       8   System administration commands (usually only for root)
       9   Kernel routines [Non standard]

如果我们明确知道要man的内容数据以上某个级别,可以直接 man [1,2,3,4...] 内容。特别适合使用在同名命令的场景。比如,man read,默认走到了 1 这个级别,即 shell commands,read是一个bash内建的命令。但是万一想查的是系统调用read,则man 2 read

READ(2)                                                                         Linux Programmer's Manual                                                                         READ(2)

NAME
       read - read from a file descriptor

SYNOPSIS
       #include <unistd.h>

       ssize_t read(int fd, void *buf, size_t count);

DESCRIPTION
       read() attempts to read up to count bytes from file descriptor fd into the buffer starting at buf.

       On  files that support seeking, the read operation commences at the file offset, and the file offset is incremented by the number of bytes read.  If the file offset is at or past
       the end of file, no bytes are read, and read() returns zero.

       If count is zero, read() may detect the errors described below.  In the absence of any errors, or if read() does not check for errors, a read() with a count of 0 returns zero and
       has no other effects.

       According to POSIX.1, if count is greater than SSIZE_MAX, the result is implementation-defined; see NOTES for the upper limit on Linux.

RETURN VALUE ... ...

Bash 自身也是一个可执行程序,在没有GUI之前,大家均使用的是命令行的模式。在Bash下去执行命令时,Bash会解析命令和参数,并且也是启动(fork)一个子进程去执行该命令。

文件和目录操作

本章节涉及到的命令:
ls cd file less cp mv mkdir rm ln

Linux整个目录结构是一颗倒立的树状结构:


Linux目录结构

文件系统管理着其下的文件和目录结构。常见的文件系统如下:

  • ext系列,ext2,3,4 扩展文件系统,支持高级日志功能
  • xfs 高性能64位日志文件系统
  • nfs 网络文件系统
  • ntfs 支持Microsoft NT文件系统
  • proc 可以访问系统信息的文件系统
  • tmpfs 临时文件系统,可以使用内存或者磁盘
  • ramfs 内存文件系统

文件系统之上,linux抽象出VFS层,将所有操作文件的接口统一起来。比如,可以使用read或者write系统调用读写底层各种不同种类的文件系统上的文件,包括socket。


vfs

常见的文件操作

本章节涉及到的命令:ls cd file less cp mkdir mv rm ln touch

ls 显示当前目录下的文件

-l 显示文件细节
-a 显示所有文件包括隐藏文件,在linux下以 . 开头的文件称为移仓文件
-h 文件大小以human readable的方式显示
-r 逆序排序
-t 按照时间逆序排序
常用 ls -lrth 按照时间逆序排序,很有用的参数,很方便找到最新修改的文件,尤其是看log的时候,如:

wind@bogon log]$ ls -rlth
total 0
-rw-r--r--. 1 wind wind 0 Aug 14 16:56 log1.txt
-rw-r--r--. 1 wind wind 0 Aug 14 16:56 log2.txt
-rw-r--r--. 1 wind wind 0 Aug 14 16:56 log3.txt
[wind@bogon log]$ 

如果不按时间排序,单凭肉眼,看不出来哪个文件是最新修改的,按时间排序之后,就可以放心地直接打开log3.txt查看最新的日志。

ls 可以列出多个目录下的内容,只需要传递多个参数即可。如

[wind@bogon ~]$ ls Videos/ Desktop/
Desktop/:
node_modules

Videos/:
[wind@bogon ~]$ 

cd 进入某个目录

注意,进入某个目录必须要有该目录的r+x权限,要在目录下创建文件,修改文件等,必须有该目录的写权限。

cd - 回到上一次的工作目录。

可以在终端上敲env,查看当前所有的环境变量,可以看到有一个 OLDPWD的环境变量,这里就记录的是上一次进入的目录。cd - 直接会切到该目录。(注意,如果你刚打开终端,什么目录还未进入,不会有这个环境变量。)
也是非常实用的命令之一。帮助我们在就近的两个目录间切换。

cd 或者 cd ~ 直接回到家目录

当想快速切换到家目录的时候,可以使用。同样的,可以在终端中敲env,可以看到HOME环境变量设置的就是你自己的家目录的绝对路径。

file 查看文件类型

file 也是十分有用的命令,当不知道文件是什么类型,file 文件名即可知道,尤其是那种不带类型后缀的文件就更实用了。比如:

[wind@bogon tmp]$ file tmpi2kh4wcu.png 
tmpi2kh4wcu.png: PNG image data, 48 x 48, 8-bit/color RGBA, non-interlaced
[wind@bogon tmp]$ file output.log 
output.log: ASCII text
[wind@bogon tmp]$ file /usr/bin/ls
/usr/bin/ls: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=db22e6c4e6b570619fcdfc0e693f5ff827666411, for GNU/Linux 3.2.0, stripped
[wind@bogon ~]$ file /usr/lib64/libGLX_mesa.so.0.0.0 
/usr/lib64/libGLX_mesa.so.0.0.0: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=1012f52900c5308af9e23edd2e8dc81ca51a1f7f, stripped
wind@bogon ~]$ cat a
#!/bin/bash

echo "hello world"
[wind@bogon ~]$ file a
a: Bourne-Again shell script, ASCII text executable
[wind@bogon ~]$ 

less 分段查看文件

当我们要看很大的文件时,直接用vim打开是非常慢的,直接使用less可以很快打开,并且仍旧可以使用vim的快捷键,比如上下翻页,跳转文件头尾部,当然也可以 / 去查找字符串。

less的出现替代了more,"less is more" ,more是不支持内部使用vim的命令去操作的,并且只能向后翻页。所以,less起来吧。

cp 复制文件或目录

几个常用参数
-r 拷贝目录
-i 当目标位置已有相同文件时,进行提示

cp file1 file2
cp file1 file2 directory/
cp -r directory directory/

mkdir 创建目录

几个常用参数
-p 按照传入参数建目录,当目录不存在时依次创建。

如:

[wind@bogon ~]$ mkdir hello/world
mkdir: cannot create directory ‘hello/world’: No such file or directory
[wind@bogon ~]$
 mkdir -p hello/world
# 这里的tree是用来显示目录树状结构的命令
[wind@bogon ~]$ tree hello/
hello/
└── world

2 directories, 0 files
[wind@bogon ~]$ 

mv 移除和重命名文件

mv 的参数和cp的参数大致相同,只不过mv不区分文件还是目录。

mv file1 file2 file3 directory/
mv file1 file2

Question:mv or cp?

rm 删除文件和目录

当心啊!!! 删除数据一般需要小心的,因为恢复起来不容易。
几个常用参数:
-r 删除目录,包括目录中的子目录
-f 强制删除,忽略不存在的文件,不会有交互式提示,会覆盖掉-i参数
-i 删除文件时与用户交互提示是否删除

ln 创建链接

什么是链接?用过windows的朋友都知道快捷方式,链接类似于这个概念。但在Linux中存在软连接和硬链接的概念。其中软连接就类似于快捷方式。

创建软连接又称符号链接
ln -s 源文件 目的软连接位置
创建硬链接
ln 源文件 目的连接位置

软链接是因为硬链接的不足创造的。
硬链接限制:
硬链接不能跨磁盘创建,由于硬链接底层是引用计数,引用计数只能在当前文件系统内生效,即在当前磁盘生效。
硬链接无法引用目录。

当然,硬链接可以用来备份文件而不需要两倍的空间大小。这点,软连接做不到。

touch 修改文件最后访问和修改时间

touch,摸一下,就会修改文件最后的访问时间。如果摸了一个不存在的文件,还会新建出来。
这在依据文件时间变化来进行增量编译的构建系统中是有些用途的。可以让其强制编译某个文件。

寻找命令的位置

本章节会涉及到的命令:which alias

which xx 寻找命令安装位置

which 用来获取当前传入的命令安装位置,如:

[wind@bogon ~]$ which ls
alias ls='ls --color=auto'
    /usr/bin/ls
[wind@bogon ~]$ 

经常会遇到当系统中存在多个相同命令是,不确定当前执行的是哪一个,就可以which xx来确定当前执行的是哪一个。

alias 给命令起别名

命令别名,可以给命令起另外一个名字,比如:

alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias ll='ls -l --color=auto'
alias ls='ls --color=auto'

可以把一些命令较长,较为难记的命令alias一下,构建自己的命令体系。

重定向

本章节涉及的命令如下:cat sort uniq wc grep awk sed head tail tee

标准输入、标准输出、标准错误

Linux为每个进程默认创建了3个文件流,即标准输入、标准输出、标准错误,对应 0 1 2 三个句柄。
当需要将进程打印的信息写入到文件时,就可以使用重定向。可以选择需要重定向的流,比如:

[wind@bogon ~]$ ls > ls_output.txt
[wind@bogon ~]$ cat ls_output.txt 
Desktop
Documents
Downloads
Music
Pictures
Public
shellscript
Templates
Videos
[wind@bogon ~]$ 

> 表示重定向,就是一个大于号,大于号前可以选择重定向的流编号,如,1或者2。默认是1。

[wind@bogon ~]$ ls /tmp/wahahah > ls_output.txt
ls: cannot access '/tmp/wahahah': No such file or directory
wind@bogon ~]$ ls /tmp/wahahah 2> ls_output.txt
[wind@bogon ~]$ cat ls_output.txt 
ls: cannot access '/tmp/wahahah': No such file or directory
[wind@bogon ~]$ 

如果既想要将标准输出重定向又想标准错误重定向可以如下:

[wind@bogon ~]$ ls /tmp/wahahah > ls_output.txt 2>&1

可以了解为,将标准错误的内如也给到标准输出,一起重定向出去了。只不过在 1 之前要加有一个&作为转义。不然就会把1当做一个文件名了。

如果说不想要标准输出的内容,或者标准错误的内容,可以直接将对应的内容给到 /dev/null 设备。比如:

[wind@bogon ~]$ ls /tmp/wahaha 2>/dev/null
[wind@bogon ~]$ 

在 /dev/下面有很多有趣的设备 /dev/null 就像一个垃圾箱,可以收各种输出然后扔掉。
/dev/zero 产生0的字符设备。
/dev/random 可以用来产生随机数的字符设备。比如产生8字节的浮点数(年会抽奖可以用用,现场码抽奖代码):

[root@bogon ~]# head -c 8 /dev/random | od -A n -t f8
    5.41717595263032e-186

cat 显示文件内容,连接文件内容

cat 可以将传入的所有参数文件内容打印到终端上。因此,使用cat可以将多个文件拼接起来。比如:

[wind@bogon shellscript]$ echo "hello" >a 
[wind@bogon shellscript]$ echo "world" >a1
[wind@bogon shellscript]$ cat a a1 > b
[wind@bogon shellscript]$ cat b
hello
world
# 也可以使用通配符
[wind@bogon shellscript]$ cat a* > b
[wind@bogon shellscript]$ cat b
hello
world

管道

管道,顾名思义,抽象起来就是一个管子,有输入有输出。比如:

[wind@bogon shellscript]$ ls /usr/bin | grep -w cd
cd
cd-create-profile
cd-drive
cd-fix-profile
cd-iccdump
cd-info
cd-it8
cd-paranoia
cd-read
[wind@bogon shellscript]$ 

比如上面的命令就过滤出来了/usr/bin下面的文件中,有哪些包含了 cd 这个单词的。
管道作为内容的传递媒介,实际上做的是将上个命令的输出直接给到了下个命令的输入。
可以利用管道的这一点,进行更复杂的文本处理。比如对输出的内容进行排序,去重,统计行数,再过滤等等。

uniq 报告或者忽略文本中重复的行

uniq会将临近行相同的合并只显示一行,一般会与sort配合使用。

sort 排序字符串

sort 有多个参数可以可以指定分隔符,可以指定按照第几个分隔符的内容作为主键来排序整行。
-t 分隔符
-k 使用字段
-n 按照数字大小排序
-r 逆序
举例如下:

[wind@bogon shellscript]$ cat text.log 
5:a
2:c
8:f
4:p
2:c
1:e
[wind@bogon shellscript]$  sort -t':' -k1 text.log
1:e
2:c
2:c
4:p
5:a
8:f
[wind@bogon shellscript]$  sort -t':' -k2 text.log
5:a
2:c
2:c
1:e
8:f
4:p
[wind@bogon shellscript]$  sort -t':' -k2 text.log | uniq 
5:a
2:c # 去重了
1:e
8:f
4:p

wc 统计行数、字节数、字数等

常用参数:
-l 统计行数
-c 统计字节数量
-w 统计单词数量

grep 打印匹配行

linux bash 三剑客之一,与sed、awk齐名。都是文本处理利器。
常用参数:
-w 整个单词严格匹配
-v 反向过滤
-n 显示匹配的字符串在文件中的行号
-E 开启正则表达式(正则表达式是另外一个话题)
-i 忽略大小写
-A 匹配到关键字行之后再打印几行
-B 匹配到关键字所在行之前先打印几行
太强大了,暂时只用到了这几个。

我们在查找代码中的函数的时候,经常会用到grep去找符号,比如:

[wind@bogon mesa]$ grep -wn "llvmpipe_draw_vbo" ./* -R
./src/gallium/drivers/llvmpipe/lp_draw_arrays.c:54:llvmpipe_draw_vbo(struct pipe_context *pipe, const struct pipe_draw_info *info,
./src/gallium/drivers/llvmpipe/lp_draw_arrays.c:191:   llvmpipe->pipe.draw_vbo = llvmpipe_draw_vbo;
./tags:334214:llvmpipe_draw_vbo src/gallium/drivers/llvmpipe/lp_draw_arrays.c   /^llvmpipe_draw_vbo(struct pipe_context *pipe, const struct pipe_draw_info *info,$/;"   f       typeref:typename:void file:
[wind@bogon mesa]$ 

这样麻烦的话,直接使用ag,就替代了这么一长串参数:

[wind@bogon mesa]$ ag "llvmpipe_draw_vbo"
tags
334214:llvmpipe_draw_vbo        src/gallium/drivers/llvmpipe/lp_draw_arrays.c   /^llvmpipe_draw_vbo(struct pipe_context *pipe, const struct pipe_draw_info *info,$/;"   f       typeref:typename:void file:

src/gallium/drivers/llvmpipe/lp_draw_arrays.c
54:llvmpipe_draw_vbo(struct pipe_context *pipe, const struct pipe_draw_info *info,
191:   llvmpipe->pipe.draw_vbo = llvmpipe_draw_vbo;
[wind@bogon mesa]$ 

只是显示格式的区别,但省了很多参数。

awk

awk, linux bash 三剑客之一,与sed、grep齐名。都是文本处理利器。
过于强大,只说常用的,详细的可以参考man手册,或者网上的使用方式。
awk的语法类似于C的语法,awk可以支持流程控制,函数,定义数组、变量。

  1. 列处理字符串、数字
  2. 统计总数、平均数等
  3. 文本内容匹配、替换、格式化等。

举例,统计分数:

[wind@bogon shellscript]$ cat c
name age score
li   20   100
wang 22   90
zhang 23  98
[wind@bogon shellscript]$ awk 'BEGIN{sum=0;} {if(NR>1) sum+=$3;} END{print sum}' c
288
[wind@bogon shellscript]$ 

举例,获取用户的登录shell名称:

[wind@bogon shellscript]$ cat /etc/passwd | grep wind | awk -F':' '{print $7}'
/bin/bash
[wind@bogon shellscript]$ 

-F 为指定分隔符

awk 有很多内置变量可以直接引用,常用的,比如:
NR: 表示当前行号。
NF: 表示当前行的字段总数。
FS: 表示当前使用的分隔符
FILENAME: 表示当前正在处理的文件名称,awk可以一次处理多个文件。

awk 引用外部变量:

[wind@bogon shellscript]$ awk '{printf("%s\n", "'"$HOME"'");}' b
/home/wind
/home/wind
/home/wind
/home/wind
[wind@bogon shellscript]$ awk -v value=$HOME '{printf("%s\n", value);}' b
/home/wind
/home/wind
/home/wind
/home/wind
[wind@bogon shellscript]$ 

awk 过于强大,其他内容,有待大家自己发掘。

sed

sed, linux bash 三剑客之一,与awk、grep齐名。都是文本处理利器。
过于强大,只说常用的。

  1. 打印,删除指定行
[wind@bogon shellscript]$ cat b
hello
world
wahaha
[wind@bogon shellscript]$ sed -n '1,2p' b
hello
world
[wind@bogon shellscript]$ sed '1,2d' -i b
[wind@bogon shellscript]$ cat b
wahaha
[wind@bogon shellscript]$ 

-n 表示只显示匹配的行
-i 表示写入文件

  1. 字符串替换,修改,打印
    格式:sed 's/pattern/replacement/flags' file
    flags 可以有多个常用的:
    p: 只打印,
    d: 删除匹配行
    g: 全局替换,即如果一行有多个,则全部替换
[wind@bogon shellscript]$ cat b
hello hello
world
hello
wahaha
[wind@bogon shellscript]$ sed -n '/hello/p' b
hello hello
hello
[wind@bogon shellscript]$ sed '/hello/d' b
world
wahaha
[wind@bogon shellscript]$ sed  's/hello/2333/' b
2333 hello
world
2333
wahaha
[wind@bogon shellscript]$ sed  's/hello/2333/g' b
2333 2333
world
2333
wahaha
[wind@bogon shellscript]$ 

如果想把结果写入到文件中,需要在文件前,添加 -i 。

  1. 指定位置插入文本内容
    在指定行尾添加
[wind@bogon shellscript]$ cat b | sed  "1,2 s/$/233/"
hello hello233
world233
hello
wahaha
[wind@bogon shellscript]$ 

在匹配的字符串后追加:

[wind@bogon shellscript]$ cat b | sed -r "s/(hello)/\1 fvck/"
hello fvck hello
world
hello fvck
wahaha
[wind@bogon shellscript]$ cat b | sed -r "s/(hello)/\1 fvck/g"
hello fvck hello fvck
world
hello fvck
wahaha
[wind@bogon shellscript]$ 

在匹配的字符串后添加行:

[wind@bogon shellscript]$ cat b | sed  "/hello/a fvck"
hello hello
fvck
world
hello
fvck
wahaha
[wind@bogon shellscript]$ 
  1. 引用外部变量
    使用bash的双引号扩展,直接将匹配字符串部分用双引号括起来,内部引用变量即可。
[wind@bogon shellscript]$ export A=hello
[wind@bogon shellscript]$ cat b
hello hello
world
hello
wahaha
[wind@bogon shellscript]$ cat b | sed -n "/$A/p"
hello hello
hello
[wind@bogon shellscript]$ 

sed过于强大,其他内容有待大家自己发掘。

tr 字符转换

比如大小写转换:

[wind@bogon shellscript]$ cat b
hello hello
world
hello
wahaha
[wind@bogon shellscript]$ cat b | tr a-z A-Z
HELLO HELLO
WORLD
HELLO
WAHAHA
[wind@bogon shellscript]$ 

head/tail 打印文件行首或者行尾

常用参数:
-n 行数,这在文件很大或者只想知道文件内部内容的格式的情况下很有用的。

tail -f 实时监控文件的改变

[wind@bogon shellscript]$ tail -f a1                                                           │[wind@bogon ~]$ echo "wahaha" >> shellscript/a1
world                                                                                          │[wind@bogon ~]$ 
wahaha                                                                                         │

相当实用的命令,能够帮助我们随时看到文件的最新变化。其底层实现也是使用了文件系统提供的watch/notify机制。感兴趣可以查看 inotify 。

tee 从stdin读取,并同时输出到stdout和文件中

问题来了,如果在管道中间还想看到输出的内容而不是全部被定向到下一个,可以使用tee。比如:

[wind@bogon tmp]$ ls /usr/bin | tee command.txt | grep -w ls
ls
[wind@bogon tmp]$ head -n 3 command.txt 
[
2to3
2to3-3.11
[wind@bogon tmp]$ 

命令行扩展

花括号扩展

当我们需要批量创建文件、目录等需求时,可以使用花括号扩展。
比如,我们要创建已 command为前缀,以数字编号为后缀的0-100个文件时,可以做如下操作:

[wind@bogon tmp]$ touch command_{1..100}.txt
[wind@bogon tmp]$ ls 
command_100.txt  command_19.txt  command_28.txt  command_37.txt  command_46.txt  command_55.txt  command_64.txt  command_73.txt  command_82.txt  command_91.txt
command_10.txt   command_1.txt   command_29.txt  command_38.txt  command_47.txt  command_56.txt  command_65.txt  command_74.txt  command_83.txt  command_92.txt
command_11.txt   command_20.txt  command_2.txt   command_39.txt  command_48.txt  command_57.txt  command_66.txt  command_75.txt  command_84.txt  command_93.txt
#... ... 这里省略剩余文件。
[wind@bogon tmp]$ 

Bash 在执行这条命令时,会优先展开{},相当于touch后面跟了100个参数。
当然花括号扩展还支持嵌套,比如:

[wind@bogon tmp]$ touch wahaha-{x{1..10},y{11..20}}.txt
[wind@bogon tmp]$ ls
wahaha-x10.txt  wahaha-x2.txt  wahaha-x4.txt  wahaha-x6.txt  wahaha-x8.txt  wahaha-y11.txt  wahaha-y13.txt  wahaha-y15.txt  wahaha-y17.txt  wahaha-y19.txt
wahaha-x1.txt   wahaha-x3.txt  wahaha-x5.txt  wahaha-x7.txt  wahaha-x9.txt  wahaha-y12.txt  wahaha-y14.txt  wahaha-y16.txt  wahaha-y18.txt  wahaha-y20.txt

参数扩展

可以在命令行中直接引用其它变量,比如:
echo $USER
echo $OLDPWD

命令扩展

如果在执行命令过程中,需要依赖其它命令的输出,可以使用参数扩展。如下:

[wind@bogon ~]$ ls -l $(which cp)
-rwxr-xr-x. 1 root root 145480 May  5 05:57 /usr/bin/cp
[wind@bogon ~]$ 

上述中,bash在执行时会优先执行 $() 中的内容,之后将结果,替换到对应位置,再行执行剩下的命令。
当然,$() 也可以使用 `` 两个反引号括起来,

算数扩展

可以拿 $(()) 来计算 加减乘除取余次幂,不过只是对整数而已。比如:
echo $((2**32)) 即 2 的 32 次方。

单引号和双引号

使用单引号括起来的字符串,不支持所有扩展。
使用双引号括起来的字符串,支持所有扩展,支持字符的转义。
比如:

[wind@bogon ~]$ A=10
[wind@bogon ~]$ echo '$A'
$A
[wind@bogon ~]$ echo "$A"
10
[wind@bogon ~]$ echo "\$A"
$A
[wind@bogon ~]$ echo "$(ls)"
build_package
Desktop
Documents
Downloads
Music
Pictures
Public
shellscript
Templates
Videos
[wind@bogon ~]$ echo '$(ls)'
$(ls)

用户和权限

本章节设计的命令:id useradd deluser usermod chmod su sudo chown passwd
在UNIX安全模型中,一个用户可以对文件或者目录有控制权,即owner。用户可以划归到某一个组中,如果改组对文件或者目录有控制权,那么其组内的用户均继承该控制权。那么除了用户自己和用户所在的组,剩余的均称为其它用户和其它用户组,拥有文件控制权的用户可以控制对应的文件或者目录是否对可以被其它用户访问。
注:超级用户(root)不受权限约束,除非 ... ...

id 查看当前用户id,所属组id等

[wind@bogon ~]$ id
uid=1000(wind) gid=1000(wind) groups=1000(wind),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[wind@bogon ~]$

同样的信息可以在/etc/passwd中查看到。

useradd 添加用户

常用参数:
-d 家目录路径
-m 家目录不存在时创建
-s 指定登录shell类型

wind@bogon ~]$ sudo useradd test -d /home/test -m -s /bin/bash 
[sudo] password for wind: 
[wind@bogon ~]$ sudo passwd test
Changing password for user test.
New password: 
Retype new password: 
passwd: all authentication tokens updated successfully.
[wind@bogon ~]$ 

userdel 删除用户

userdel user_name
常用参数:
-r 删除用户时同时清理掉家目录及相关目录。

[wind@bogon home]$ ls -rlth
total 12K
drwx------.  3 test test 4.0K Aug 24 16:23 sudo
drwx------.  3 test test 4.0K Aug 24 16:42 test
drwx------. 29 wind wind 4.0K Aug 24 16:45 wind
[wind@bogon home]$ sudo userdel test # 默认不会清理家目录
[wind@bogon home]$ ls -rlth
total 12K
drwx------.  3 1001 1001 4.0K Aug 24 16:23 sudo
drwx------.  3 1001 1001 4.0K Aug 24 16:42 test
drwx------. 29 wind wind 4.0K Aug 24 16:45 wind
[wind@bogon home]$ 

usermod 修改用户账户信息

usermod可以修改很多内容,包括:

  1. 用户家目录
  2. 用户组信息,添加删除修改
  3. 修改用户可使用时间等
    以添加用户组为例:
[wind@bogon ~]$ sudo usermod -a -G test wind 

给wind用户添加test组。注意这里需要用户重新登录才能生效。

读写可执行权限

文件或目录的权限情况可以通过 ls -l 来查看,如:

[wind@bogon ~]$ ls -l
total 392
drwxr-xr-x.  3 wind wind   4096 Jul 19 21:51 build_package
drwxr-xr-x.  3 wind wind   4096 Jul 12 14:36 Desktop
drwxr-xr-x.  2 wind wind   4096 Jan 10  2023 Documents
drwxr-xr-x.  4 wind wind   4096 Mar  8 16:40 Downloads
drwxr-xr-x.  2 wind wind   4096 Jul  3 12:12 genbu_service
drwxr-xr-x. 17 wind wind   4096 Aug  4 15:29 git
-rwxr-x---.  1 wind wind  82627 Feb  6  2023 gl33-gtf-master.txt
-rwxr-x---.  1 wind wind 253199 Feb  6  2023 gl33-master.txt
-rw-r--r--.  1 wind wind     60 Aug 15 11:50 ls_output.txt
[wind@bogon ~]$ 
文件类型和权限

各个权限对应的权值:

  • r 可读权限,对应 4
  • w 可写权限,对应 2
  • x 可执行权限,对应 1
    加和等于 7

Question:为什么是 1 2 4,不是1 2 3

常见的文件类型:

  • - 普通文件
  • d 目录文件
  • l 符号链接文件,注意对于符号链接文件,其文件属性始终是rwxrwxrwx,是个伪属性,其指向的文件的属性才是真的文件属性
  • c 字符设备文件,该文件类型表示以字节流形式处理数据的设备,比如终端(tty),比如我们的显卡设备/dev/dri/card0 ,比如我们的fb设备 /dev/fb0
[wind@bogon dev]$ ls -l fb0 dri/card0 
crw-rw----+ 1 root video 226, 0 Aug 15 08:57 dri/card0
crw-rw----. 1 root video  29, 0 Aug 14 15:47 fb0
[wind@bogon dev]$ 
  • b 块设备文件,该文件类型表示以数据块方式处理数据的设备,比如,磁盘设备,光盘设备
[wind@bogon dev]$ ls -l /dev/sda{1,2,3}
brw-rw----. 1 root disk 8, 1 Aug 14 15:47 /dev/sda1
brw-rw----. 1 root disk 8, 2 Aug 14 15:47 /dev/sda2
brw-rw----. 1 root disk 8, 3 Aug 14 15:47 /dev/sda3
[wind@bogon dev]$ 

chmod 更改文件或者目录权限

chmod 有很多种方式去修改文件权限

  1. 全量修改,即一次性指定所有的权限,包括user、group、other的权限,比如:
[wind@bogon shellscript]$ ls -rlth
-rw-r--r--. 1 wind wind    6 Aug 15 12:06 a
[wind@bogon shellscript]$ chmod 666 a 
[wind@bogon shellscript]$ ls -l a
-rw-rw-rw-. 1 wind wind 6 Aug 15 12:06 a

自己需要事前计算好所有权值和

  1. 部分修改
    如果只需要部分修改权值,则可以通过指定给 user 、group、other 添加或者减少什么权值,比如:
[wind@bogon shellscript]$ ls -l a
-rw-rw-rw-. 1 wind wind 6 Aug 15 12:06 a
[wind@bogon shellscript]$ chmod u+x a
[wind@bogon shellscript]$ chmod o+x a
[wind@bogon shellscript]$ chmod g-w a
[wind@bogon shellscript]$ ls -l a
-rwxr--rwx. 1 wind wind 6 Aug 15 12:06 a
[wind@bogon shellscript]$ 
  • u 表示user
  • g 表示 group
  • o 表示 other
  • a 表示 all
    • 表示 添加 权限
    • 表示 去除 权限
  • = 表示指定权限
    适用于只需要部分修改文件权限的场景。这里+或者-后面可以跟不止一个权限,比如 +rwx 也是可行的。
    并且,可以同时给 ug,go,等组合添加或者去除权限。

更改身份

su 以其它用户和组ID的身份来运行shell

su 可以切换到指定用户去执行命令
su [-] username
su执行后会进入对应用户的登录shell界面,这里 - 的意思代表是否要切环境变量,如果指定了 - 表示使用要切换的用户的所有环境变量,而不是当前用户的所有环境变量。不指定 - 时,表示不切换环境变量,只是用户身份的切换。比如:

[wind@bogon shellscript]$ export A=10
[wind@bogon shellscript]$ su 
Password: 
[root@bogon shellscript]# echo $A
10
[root@bogon shellscript]# 
exit
[wind@bogon shellscript]$ export B=20
[wind@bogon shellscript]$ su -
Password: 
[root@bogon ~]# echo $B

[root@bogon ~]# 
logout
[wind@bogon shellscript]$ su 
Password: 
[root@bogon shellscript]# echo $B
20
[root@bogon shellscript]# 

注意,su 不指定用户名称时,默认会切换到超级用户,即root。
如果不进入用户的登录shell交互式执行命令,还可以直接 -c “command”的方式执行命令,即:

[wind@bogon shellscript]$ su - -c "ls"
Password: 
anaconda-ks.cfg  initial-setup-ks.cfg
[wind@bogon shellscript]$ 

退出shell的方式,exit或者ctrl-d均可,ctrl-d 的含义我们在信号章节继续讲。

sudo 以另一个用户的身份执行命令

sudo命令在很多方面都类似于su命令,不过管理员可以通过配置sudo命令,使系统以一种可控的方式,允许一个普通用户以一个不同的用户身份(通常是超级用户)执行命令。在特定情况下,用户可能被限制为只能执行一条或者几条特定的命令,而对其他命令没有执行权限。另一个重要的区别在于,使用sudo命令并不需要输入超级用户的密码。使用sudo命令时,用户只需要输入自己的密码来进行认证。(浅显的理解,sudo即super user do xxx)。
配置sudo可以使用visudo,或者直接修改/etc/sudoers 文件。推荐使用visudo,因为其可以帮助检查语法错误。我们可以精确配置某个用户使用sudo可以执行哪些命令,不能执行哪些命令。
用 sudo -l 可以详细查看当前用户可以执行的哪些命令。

[wind@bogon shellscript]$ ls -l a
-r-xr-x---. 1 root root 26 Aug 17 13:07 a
[wind@bogon shellscript]$ ./a
bash: ./a: Permission denied
[wind@bogon shellscript]$ sudo ./a
hello
[wind@bogon shellscript]$ sudo -l
Matching Defaults entries for wind on bogon:
    !visiblepw, always_set_home, match_group_by_gid, always_query_group_plugin, env_reset, env_keep="COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS", env_keep+="MAIL QTDIR USERNAME LANG
    LC_ADDRESS LC_CTYPE", env_keep+="LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES", env_keep+="LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE", env_keep+="LC_TIME LC_ALL
    LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY", secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/var/lib/snapd/snap/bin

User wind may run the following commands on bogon:
    (ALL) ALL
[wind@bogon shellscript]$ 

chown 更改文件所有者和所属群组

可以使用chown来修改文件或者目录所属于的用户或者组。
chown user[:[group]] file
这里的group如果和user相同,可以缺省(但是冒号得留着)。
也可以只修改user或者只修改group。只修改group时,冒号需要带上。

passwd 更改用户密码

passwd username 这个命令没有什么特别的。改用户密码。

高级键盘技巧

在Bash下敲命令,有很多快捷键,十分方便,给我们提升效率,以下介绍常用的快捷键,感兴趣的话可以详细查看。

clear 清理屏幕显示内容

clear会清理当前终端显示的所有内容,如果敲clear太麻烦,可以直接 Ctrl-l。

reset 清理当前及历史的所有输出

reset 不仅会清理当前终端显示内容,历史显示内容仍旧会清理,保证不会干扰后续的程序输出。

编辑命令行

当我们在敲命令时候,敲错了,或者漏敲了,怎么能快速完成这些操作呢?

Ctrl-a

将光标移动到行首,很好记,a是字母表的开始字母,表示开始的意思。

Ctrl-e

将光标移动到行尾,很好记,e是end的缩写。

Ctrl-f

将光标向前移动一个字符,f 是front的意思。

Ctrl-b

将光标向后移动一个字符,b是back的意思。

Alt-f

将光标向前移动一个单词。(可能会和终端快捷键起冲突)

Alt-b

将光标向后移动一个单词。

Ctrl-w

从后向前删除一个单词,w是word的意思。

Ctrl-d

删除光标处的字符。d->delete

Ctrl-k

剪切从光标到行尾的文本。

Ctrl-u

剪切从光标到行首的文本。

复制粘贴

和寻常我们使用的Ctrl-c Ctrl-v不同,Ctrl-c 已经用来代表其它含义了(发送信号,在信号章节介绍),再加一个shift。

复制粘贴选中的文本行

Ctrl-Shift-c
Ctrl-Shift-v
别再鼠标右键点来点去了。键盘操作起来吧。
PS: 在vim或者tmux中复制内容,可以按住shift再用鼠标选中和复制。

搜索命令行

history 显示历史执行的命令列表

每个历史命令都有一个编号,当我们知道这个编号以后,就可以直接使用 !num 来运行。

[wind@bogon mesa]$ history | tail 
 1082  sudo dnf upgrade vsftpd
 1083  rpm -q vsftpd
 1084  rpm -qa vsftpd
 1085  rpm -q vsftpd
 1086  man 
 1087  man rpm
 1088  vmstat 
 1089  free 
 1090  man free 
 1091  history | tail 
[wind@bogon mesa]$ !1089
free 
               total        used        free      shared  buff/cache   available
Mem:         2000904      623772      200980       18032     1176152     1171288
Swap:        2000892      132352     1868540
[wind@bogon mesa]$ 

Ctrl-r 搜索历史命令

键入 Ctrl-r 之后,会进入搜索历史命令的模式,这时候只需要敲一些关键字,就会匹配到历史的命令,进而可以执行历史的命令。如果显示的命令不是你想要的那个,可以连续按 Ctrl-r 找到自己想要的命令。
找到之后可以 Ctrl-j 将命令或者Ctrl-a(光标自动到头b部),Ctrl-e(光标自动到尾部) 将命令先显示到提示符。
或者直接 Enter 执行。

(reverse-i-search)`free': free 
[wind@bogon mesa]$ free 
               total        used        free      shared  buff/cache   available
Mem:         2000904      624820      143484       18044     1232600     1170236
Swap:        2000892      131840     1869052
[wind@bogon mesa]$ 

上翻下翻历史命令

可以使用方向键 上下 来显示上一条或者下一条命令
也可以使用 Ctrl-p 和 Ctrl-n (prev,next)来上下翻执行过的命令。

进程相关

本章节涉及到的命令:pstree ps top jobs bg fg kill killall
所有的现代操作系统都能够同事运行若干进程,至少用户错觉上是这样的。如果系统只有一个处理器,那么给定时刻只有一个程序可以运行。在多处理器系统中,可以真正并行运行的进程数目,取决于物理CPU的数目。
内核和处理器之间建立了多任务的错觉,即可以并行做几种操作,这是通过以很短的时间间隔在系统运行的应用程序之间不停切换而做到的。由于切换间隔如此之短,使得用户无法注意到短时间内的停滞,从而在感官上觉得计算机能够同时做几件事情。 --- 来自《深入Linux内核架构》


进程的执行

这是一种复用机制的体现,对整个机器硬件的复用。早起的操作系统是单任务的,比如MSDOS,一次只能加载一个程序运行。受限于当时处理器机能限制,运行单个程序已经有些费劲,主频较低。随着Intel 80x86系列的崛起,以及后面的奔腾系列。单任务已经阻碍了充分发挥硬件的能力,随后微软推出了Windows。产生了多任务切换机制。
在整个操作系统中,我们可以随处可见复用机制:

  1. 端口机制
  2. IO 复用,select/poll/epoll
    以及我们常听说的,波分复用,频分复用,分时复用。我们的网卡中就使用了频分复用。

内核会保存每个进程的信息以便确保任务有序进行。如果对内核都为进程保存了哪些信息感兴趣,可以深入学习 《深入Linux内核架构》中,进程管理和调度章节。或者详细分析task_struct结构体内容,在include/sched.h中。

在运行的Linux中,每个进程都会有一个唯一的ID来标识它,ID在一段时间内是递增的。所有用户进程的祖先均为1号进程,也就是init进程(或者是systemd进程)。
一个进程可以自行创建其它进程,习惯性称为父进程创建子进程。在Linux下可以使用fork系统调用进行创建。由此可知,进程之间是存在一个树状结构的。可以使用 pstree 来显示出这种树状结构。

进程的状态

进程的状态

Linux 下通过 ps 或者 top 显示的进程的状态:

D 表示不可中断的睡眠状态,也就是说,进程正在等待某些资源(例如磁盘I/O),不能被信号打断。
I 表示空闲状态,也就是说,进程没有运行,也没有被调度。
R 表示运行或可运行状态,也就是说,进程正在运行或者在运行队列中等待运行。
S 表示可中断的睡眠状态,也就是说,进程正在等待某些事件(例如信号)发生,可以被信号打断。
T 表示停止状态,也就是说,进程收到了停止信号(例如SIGSTOP),暂时停止运行。
Z 表示僵尸状态,也就是说,进程已经终止,但是父进程还没有回收它的资源。
僵尸进程没法杀死,只能等父进程回收资源。

[root@bogon ~]# pstree                                                                                                                                                                 [63/63]
systemd─┬─ModemManager───3*[{ModemManager}]                                                                                                                                                   
        ├─NetworkManager───2*[{NetworkManager}]                                                                                                                                               
        ├─2*[VBoxClient───VBoxClient───3*[{VBoxClient}]]                                                                                                                                      
        ├─VBoxClient───VBoxClient───4*[{VBoxClient}]                                                                                                                                          
        ├─VBoxDRMClient───3*[{VBoxDRMClient}]                                                                                                                                                 
        ├─VBoxService───8*[{VBoxService}]                                                                                                                                                     
        ├─abrt-dbus───2*[{abrt-dbus}]                                                                                                                                                         
        ├─3*[abrt-dump-journ]                                                                                                                                                                                                                                                                                                                  
        ├─lightdm─┬─Xorg───{Xorg}                                                                                                                                                             
        │         ├─lightdm─┬─xfce4-session─┬─Thunar───2*[{Thunar}]                                                                                                                           
        │         │         │               ├─abrt-applet───3*[{abrt-applet}]                                                                                                                 
        │         │         │               ├─agent───2*[{agent}]                                                                                                                             
        │         │         │               ├─blueman-applet───3*[{blueman-applet}]                                                                                                           
        │         │         │               ├─dnfdragora-upda───3*[{dnfdragora-upda}]                                                                                                         
        │         │         │               ├─nm-applet───3*[{nm-applet}]                                                                                                                     
        │         │         │               ├─seapplet───2*[{seapplet}]                                                                                                                       
        │         │         │               ├─ssh-agent                                                                                                                                       
        │         │         │               ├─tracker-miner-f───5*[{tracker-miner-f}]                                                                                                         
        │         │         │               ├─tracker-miner-r───4*[{tracker-miner-r}]                                                                                                         
        │         │         │               ├─xfce-polkit───2*[{xfce-polkit}]                                                                                                                 
        │         │         │               ├─xfce4-notifyd───2*[{xfce4-notifyd}]                                                                                                             

这里只截取了一部分。
如果对1号进程是怎么来的,0号进程怎么创建的1号进程,以及当前系统正在运行的进程都是怎么来的感兴趣的话,可以去查找Linux开机启动过程。这部分内容还是十分有趣的,毕竟操作系统是怎么将一堆会电人的硬件变成了我们的生产工具的过程还是很迷人的。

进程组和session

由此,自然而然地就产生了进程组的概念,即一个进程创建的多个进程会被归到一个进程组里面,我们可以通过命令来查看。多个进程组就组成了一个session,一般情况下,session中的第一个进程既是session的leader也是当前进程组的组长。

进程组和session

关于进程组和session 的更多信息,可以参考 Unix环境高级编程,第9章进程关系中进程组和会话章节。

ps 查看进程信息

ps 可以显示进程/线程状态,包括内存占用,运行时间。
可以 man ps,其中列举了多种常用的使用方式。详细可以查看man ps中EXAMPLES节。
比如:

EXAMPLES
       To see every process on the system using standard syntax:
          ps -e
          ps -ef
          ps -eF
          ps -ely

       To see every process on the system using BSD syntax:
          ps ax
          ps axu

       To print a process tree:
          ps -ejH
          ps axjf

       To get info about threads:
          ps -eLf
          ps axms

       To see every process running as root (real & effective ID) in user format:
          ps -U root -u root u

       To see every process with a user-defined format:
          ps -eo pid,tid,class,rtprio,ni,pri,psr,pcpu,stat,wchan:14,comm
          ps axo stat,euid,ruid,tty,tpgid,sess,pgrp,ppid,pid,pcpu,comm
          ps -Ao pid,tt,user,fname,tmout,f,wchan

       Print only the process IDs of syslogd:
          ps -C syslogd -o pid=

       Print only the name of PID 42:
          ps -q 42 -o comm=

应用一下:
如何查看进程属于某个组或者session:

[wind@bogon ~]$ ps
    PID TTY          TIME CMD
 735131 pts/0    00:00:00 bash
 735270 pts/0    00:00:00 ps
[wind@bogon ~]$ ps -q 735131 -o comm=,pid=,pgid=,sid=
bash             735131  735131  735131
# 或者
[wind@bogon ~]$ ps -C bash -o comm=,pid=,pgid=,sid=
bash              22160   22160   22160
bash              22404   22404   22404
bash             735131  735131  735131
[wind@bogon ~]$ 

使用PID查询较为精准,使用名称来查可能遇到多个相同名称的进程,接下来需要自己去确定哪个是你要找的进程。

引入组和session的概念是为了作业管理和信号传递。比如,可以给进程组的leader进程发送信号,可以让其传递给所有的组内的进程。我们在信号章节再做实验。

top 动态查看进程信息

top程序将按照进程活动的顺序,以列表的形式持续更新显示系统进程的当前信息(默认每3秒更新一次)。它主要用于查看系统“最高(top)”进程的运行情况,其名字也来源于此。top命令显示的内容包含两个部分,顶部显示的是系统总体状态信息,下面显示的是一张按CPU活动时间排序的进程情况表。
常用参数:
-p pid 显示指定pid的进程
-H 显示进程下的所有线程
-c 显示完整的进程命令
-b 不使用交互式运行,只是显示指定次数
-n 次数 和 -b 配合,明确指出top运行的次数

如果不想要动态刷新,可以指定运行几次:

[wind@bogon ~]$ top -c -b -n 1
top - 18:26:35 up  2:04,  1 user,  load average: 0.00, 0.00, 0.00
Tasks: 163 total,   1 running, 162 sleeping,   0 stopped,   0 zombie
%Cpu(s):  7.7 us,  0.0 sy,  0.0 ni, 92.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :   1954.0 total,    231.8 free,    643.2 used,   1079.0 buff/cache
MiB Swap:   1954.0 total,   1953.5 free,      0.5 used.   1157.3 avail Mem 

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
   3922 root      20   0  335380 126528  46660 S  13.3   6.3   0:31.46 /usr/libexec/Xorg -core -noreset :0 -seat seat0 -auth /run/lightdm/root/:0 -nolisten tcp vt1 -novtswitch
   6530 wind      20   0  690884  42836  33408 S   6.7   2.1   0:00.35 /usr/bin/xfce4-terminal
      1 root      20   0  105772  16540  10752 S   0.0   0.8   0:01.09 /usr/lib/systemd/systemd rhgb --switched-root --system --deserialize=31
      2 root      20   0       0      0      0 S   0.0   0.0   0:00.00 [kthreadd]
      3 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 [rcu_gp]
# ... ... 省略若干行
   6361 root      20   0       0      0      0 I   0.0   0.0   0:00.01 [kworker/u2:2-writeback]
   6507 root      20   0   16284   6784   5888 S   0.0   0.3   0:00.00 systemd-userwork: waiting...
   6508 root      20   0   16284   6784   5888 S   0.0   0.3   0:00.00 systemd-userwork: waiting...
   6509 root      20   0   16284   6784   5888 S   0.0   0.3   0:00.00 systemd-userwork: waiting...
   6556 wind      20   0  224264   5248   3584 S   0.0   0.3   0:00.01 bash
   6624 wind      20   0  224804   3712   3072 R   0.0   0.2   0:00.00 top -c -b -n 1
[wind@bogon ~]$ 

在交互式情况下,按 h 可以进入help界面,q 退出。
比如 e 可以控制显示的进程内存占用的单位,E 可以指定概览区域显示的内存单位。等等
top 十分强大,可以参考man手册,这里给一个链接参考 https://zhuanlan.zhihu.com/p/623777553

前台进程

前台进程,顾名思义,在前台运行的进程,可以接受键盘输入,前台运行的所有进程构成了一个前台进程组。当终端上产生任何信号,将会递送给前台进程组。比如:

[wind@bogon ~]$ geany 

geany并没有返回 因为Bash在等待geany执行完成,这里的geany即前台进程。

后台进程

在后台运行的进程,其不能接受键盘的输入交互式输入。bash中可以在命令后添加一个 & ,即可以让其在后台运行。比如:

[wind@bogon ~]$ sleep 10 &
[1] 735186

这里的输出有两个,[1] 和 735186 分别代表作业ID和进程ID。
Bash的作业管理会给当前后台运行的每个进程创建一个 Job ID。当后台进程执行完成之后,会在终端上打印Done。

[wind@bogon ~]$ 
[1]+  Done                    sleep 10
[wind@bogon ~]$ 

jobs 查看当前终端启动的所有作业。比如:

pi@raspberrypi:~ $ for((i=0;i<5;i++))do sleep 30 & done
[1] 1609
[2] 1610
[3] 1611
[4] 1612
[5] 1613
pi@raspberrypi:~ $
pi@raspberrypi:~ $ jobs
[1]   Running                 sleep 30 &
[2]   Running                 sleep 30 &
[3]   Running                 sleep 30 &
[4]-  Running                 sleep 30 &
[5]+  Running                 sleep 30 &

这里的 + 表示最后一个放入后台的进程,也是 fg 默认回放置到前台的进程。
这里的 - 表示倒数第二个放入后台的进程。当倒数第一个到前台之后,倒数第二个会变成+。

fg 将某个作业放置到前台执行。

fg jobid
比如正在写代码,突然同事来了,要然你帮忙看个log,看完之后,再继续写代码。

pi@raspberrypi:~/shell_script $ vim a.c

[1]+  Stopped                 vim a.c
pi@raspberrypi:~/shell_script $ vim b.log
pi@raspberrypi:~/shell_script $ fg
vim a.c
pi@raspberrypi:~/shell_script $

中断进程运行

Ctrl-C 会向进程发送一个中断信号,从而来中断进程。

停止进程运行

Ctrl-Z 回向进程发送一个停止信号,从而来停止当前进程。

pi@raspberrypi:~/shell_script $ geany

[1]+  Stopped                 geany
pi@raspberrypi:~/shell_script $ jobs
[1]+  Stopped                 geany
pi@raspberrypi:~/shell_script $

bg 让停止的进程重新回到后台运行的状态。

pi@raspberrypi:~/shell_script $ geany

[1]+  Stopped                 geany
pi@raspberrypi:~/shell_script $ jobs
[1]+  Stopped                 geany
pi@raspberrypi:~/shell_script $ bg 1
[1]+ geany &
pi@raspberrypi:~/shell_script $ jobs
[1]+  Running                 geany &
pi@raspberrypi:~/shell_script $ fg
geany

进程组和session2

bash会将所有前台运行和后台运行的进程放在一个session中。bash自身作为session的leader。这样有个好处,就是当图示的调制解调器断开链接(挂断信号 SIGHUP)发送给bash进程时(或者Bash窗口关闭时),bash可以直接将信号都发送给session下的所有进程,用以session下管辖的进程。

如果我们想在当前中断启动一个长期运行的后台进程,不会在我们退出终端之后也退出了,可以使用nohup来启动程序。

nohup 启动长久的后台进程

pi@raspberrypi:~/shell_script $ nohup sleep 120 &
[1] 2074
pi@raspberrypi:~/shell_script $ nohup: ignoring input and appending output to 'nohup.out'

pi@raspberrypi:~/shell_script $ ls
a.c  b.log  nohup.out
pi@raspberrypi:~/shell_script $ jobs
[1]+  Running                 nohup sleep 120 &
pi@raspberrypi:~/shell_script $ ps
    PID TTY          TIME CMD
   1526 pts/0    00:00:00 bash
   2074 pts/0    00:00:00 sleep
   2077 pts/0    00:00:00 ps
pi@raspberrypi:~/shell_script $
logout
Connection to 192.168.3.102 closed.


  22/08/2023   21:20.36   /home/mobaxterm  ssh -XY pi@192.168.3.102
pi@raspberrypi:~ $ ps aux |grep sleep
pi          2074  0.0  0.0   4936   492 ?        S    14:20   0:00 sleep 120
pi          2111  0.0  0.0   5912   656 pts/0    S+   14:20   0:00 grep --color=auto sleep
pi@raspberrypi:~ $

这是非常有用的一个命令,非常适合启动一个监控进程。比如定期监控磁盘使用,网络占用,内存占用等等。

这几个命令主要的实现原来均是读取/proc文件系统下面的进程信息,如果ps、top、htop不能满足你的需求,比如你想详细查看某个进程的内存映射布局、限制信息、文件描述符信息等,可以直接去/proc下查找具体的进程ID。/proc下面的内容较为丰富,足以单独提出来另起一篇再叙。感兴趣的话,大家可以自行分析/proc文件系统下面都保存了什么东西。

信号

关于信号的详细使用,编程以及实现方式,可以参考另一篇内容:Linux下信号的使用
在Bash下面,信号可以使用kill 或者 killall 来发送给指定进程或者进程组。

kill 对指定进程或者进程组发送信号

kill [-SIGNUM] PID
kill 只能通过指定PID的方式来发送信号,如果不加参数,默认发送SIGTERM信号。
关于信号编号及其默认对进程的影响行为,参考上述链接文档的内容。

kill 还可以给进程组发送信号,即:
kill -SIGNUM -PGID

killall 通过进程名称来发送信号

最简单的使用:

killall  -SIGNUM ProcessName 

当然,killall还支持正则表达式,方便我们匹配更多的进程名称。详细man killall

环境

本章节涉及到的命令:printenv set export alias

环境中存储的是什么

环境中存储的是环境变量和shell 变量,shell变量是Bash存放的少量数据。除了变量之外,shell还存储了一些编程数据,比如function和别名。

printenv 打印环境变量

printenv [variable name]
默认会打印全部的环境变量。

pi@raspberrypi:~ $ printenv
SHELL=/bin/bash
PWD=/home/pi
LOGNAME=pi
HOME=/home/pi
LANG=en_GB.UTF-8
TERM=xterm
USER=pi
DISPLAY=localhost:10.0
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
SSH_TTY=/dev/pts/0
TEXTDOMAIN=Linux-PAM
_=/usr/bin/printenv
pi@raspberrypi:~ $

set 同时显示shell变量和环境变量

会比printenv多shell自身的一些函数。

pi@raspberrypi:~ $ printenv
SHELL=/bin/bash
NO_AT_BRIDGE=1
PWD=/home/pi
LOGNAME=pi
XDG_SESSION_TYPE=tty
MOTD_SHOWN=pam
HOME=/home/pi
LANG=en_GB.UTF-8
# 省略大部分内容
quote ()
{
    local quoted=${1//\'/\'\\\'\'};
    printf "'%s'" "$quoted"
}
quote_readline ()
{
    local ret;
    _quote_readline_by_ref "$1" ret;
    printf %s "$ret"
}
pi@raspberrypi:~ $

环境是如何建立的

换件在登录shell登录之后,会按照顺序读取指定位置的配置文件,设置一批环境变量。

文件路径 加载时机及作用
/etc/profile 适用于所有用户的全局配置脚本
/etc/bash.bashrc 适用于所有用户的全局配置脚本
~/.bashrc 用户的个人启动文件,可以扩展或者重写全局配置脚本中的设置
~/.bash_profile 用户的个人启动文件,可以扩展或者重写全局配置脚本中的设置
~/.bash_login 如果~/.bash_profile缺失,bash尝试读取此脚本
~/.profile 如果/.bash_profile和/.bash_login均缺失,则bash尝试读取此文件

加载顺序是先系统后个人。
如你想配置自己的 alias,配置自己的PATH,配置自己的网络代理等等,就可以在个人启动文件中配置。
bash会自动加载。

修改环境变量

export 临时修改环境变量

export ENV_NAME=VALUE
仅在当前终端生效,包括终端后续要执行的命令中可以读取到。但是当新开启一个终端后,该设置即不生效了。

写入指定配置文件

几个重要的环境变量:
PATH:以冒号分割的一个目录列表,当用户输入一个可执行程序的名称时,会查找该目录列表。
DISPLAY:运行图形界面时界面的名称。通常是:0,表示由X服务器生成的第一个界面。
LANG:定义了语言的字符集和排序规则
PS1:定义了提示字符串的内容,有趣的定制。

PATH 的修改常用在当我们需要添加自己的命令,并且想让bash自动寻找和补全的时候。可以在PATH中添加。

[wind@fedora shellscript]$ ls
a  a1  a.c  b  data.dat  group.sh  nohup.out  text.log  tmp
[wind@fedora shellscript]$ pwd
/home/wind/shellscript
[wind@fedora shellscript]$ export PATH=$PATH:/home/wind/shellscript
[wind@fedora shellscript]$ group
groupadd   groupdel   groupmems  groupmod   groups     group.sh   
[wind@fedora shellscript]$ group.sh 

想让bash优先查找使用自己定义的命令时,可以将新路径添加到原始PATH的前面。即,export PATH=/home/wind/shellscript:$PATH

DISPLAY 常用在重定向显示,当我们不想让图形显示在当前的屏幕的时候,可以指定让其显示在某个已存在的屏幕上。

[wind@fedora shellscript]$ ssh -XY wind@10.0.2.15
Last login: Tue Aug 22 17:49:12 2023
[wind@fedora ~]$ printenv DISPLAY
localhost:10.0
[wind@fedora ~]$ glxgears 
2230 frames in 5.0 seconds = 445.893 FPS
[wind@fedora ~]$ export DISPLAY=:0
[wind@fedora ~]$ glxgears 
8142 frames in 5.0 seconds = 1628.385 FPS
8494 frames in 5.0 seconds = 1698.696 FPS
8729 frames in 5.0 seconds = 1745.698 FPS
[wind@fedora shellscript]$ ssh -XY wind@10.0.2.15
wind@10.0.2.15's password: 
Last login: Wed Aug 23 10:32:28 2023 from 10.0.2.15
[wind@fedora ~]$ printenv DISPLAY
localhost:11.0
[wind@fedora ~]$ 

每个SSH客户端,X均会为其非配一个端口用来在需要重定向窗口内容的时候进行重定向。
source 环境变量文件,立即在当前进程生效。这是批量生效的方法。如果环境变量文件中存在函数定义, 也同样会生效。

LANG 常用来设置语言和字符集。
当想改变当前终端显示的内容的语言时,可以通过修改LANG实现。设置LANG的格式<语言_地区.字符编码>
比如:

[wind@fedora ~]$ export LANG=zh_CN.UTF-8
[wind@fedora ~]$ ls -rlth
总计 388K
drwxr-xr-x.  2 wind wind 4.0K 2023年 1月10日 Videos
drwxr-xr-x.  2 wind wind 4.0K 2023年 1月10日 Templates
drwxr-xr-x.  2 wind wind 4.0K 2023年 1月10日 Public
drwxr-xr-x.  2 wind wind 4.0K 2023年 1月10日 Pictures
drwxr-xr-x.  2 wind wind 4.0K 2023年 1月10日 Music
drwxr-xr-x.  2 wind wind 4.0K 2023年 1月10日 Documents

上图设置的LANG解释为,中文_中国大陆地区.UTF-8编码。

定制提示符

[wind@bogon ~]$ export PS1="\[\033[0;31m\]<\u@\h \W>\$ "
<wind@bogon ~>$ ls
black_screen   Documents      gl33-gtf-master.txt  mount_point  shellscript
build_package  Downloads      gl33-master.txt      Music        Templates
data           genbu_service  hello                Pictures     Videos
Desktop        git            log                  Public
<wind@bogon ~>$ 
<wind@bogon ~ 18:20:13>$ export PS1="\[\033[0;31m\]<\u@\h \W \t>\$ "
<wind@bogon ~ 18:20:16>$ export PS1="\[\033[0;31m\]<\u@\h \W \T>\$ "
<wind@bogon ~ 06:20:21>$ export PS1="\[\033[0;31m\]<\u@\h \W \d>\$ "
<wind@bogon ~ Thu Aug 24>$ 

通过修改 PS1 变量来定制自己的提示符。

网络

本章节涉及到的命令:ping nc telnet netstat wget ssh

Linux的网络包处理过程大致如下(引用自Linux内核网络数据包处理流程):

多队列网卡

协议数据解析

上图来自 《深入linux内核框架》第12章网络-使用套接字缓冲区处理数据。

在网络连接方面,Linux可以说是万能的。Linux工具可以建立各种网络系统及应用,包括防火墙、路由器、域名服务器、NAS(网络附加存储)盒等。由于网络连接涉及的领域很广,所以用于控制、配置网络的命令自然很多。
大家可能听说过iptables,iptables的实现就内核的Netfliter机制。Netfliter就像是一个滤网,可以过滤输入、输出的网络数据包。它强大到可以做代理,反向代理,可以对各个端口配置策略等等,足以单独另起一次分享。


Netfliter

感兴趣的话,可以详细查看Netfliter的实现,非常非常有趣。

如果家里没有路由器,但是你有一台Linux主机,就可以实现自己的软路由。可以配置各种路由策略,端口映射策略,创建各种网卡,桥接等等,可以满足你各类奇怪有趣的网络需求。

Linux的实现的网络的强大,足以让你研究上若干年。再加上各种网卡新硬件出现,docker的出现,namespace机制的应用,openvswitch的出现,使得网络更加有趣味性,可以在Linux上玩SDN(software define network)。

ping 向指定网络主机发送特殊的数据包

ping 命令常常用来检测目标主机或者链接是否正常在线。
ping 命令使用ICMP协议,发送ECHO包给对端。对端收到之后会回复。

[wind@fedora ~]$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         10.0.2.2        0.0.0.0         UG    100    0        0 enp0s3
10.0.2.0        0.0.0.0         255.255.255.0   U     100    0        0 enp0s3
[wind@fedora ~]$ ping 10.0.2.2
PING 10.0.2.2 (10.0.2.2) 56(84) bytes of data.
64 bytes from 10.0.2.2: icmp_seq=1 ttl=64 time=0.185 ms
64 bytes from 10.0.2.2: icmp_seq=2 ttl=64 time=0.114 ms
64 bytes from 10.0.2.2: icmp_seq=3 ttl=64 time=0.117 ms
64 bytes from 10.0.2.2: icmp_seq=4 ttl=64 time=0.221 ms
^C
--- 10.0.2.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3071ms
rtt min/avg/max/mdev = 0.114/0.159/0.221/0.045 ms
[wind@fedora ~]$ 

nc 通过命令行在网络上读写数据

nc常用来进行某个端口的链接,以及发送数据。接收数据。

[wind@fedora ~]$ nc -l 10000                                       │[wind@fedora ~]$ nc 127.0.0.1 10000
hello                                                              │hello 
world                                                              │world
wahahah                                                            │wahahah
                                                                   │

也可以用来测试某个节点的端口是不是开通了。

[wind@fedora ~]$ nc 127.0.0.1 22
SSH-2.0-OpenSSH_8.8

telnet 使用telnet协议与其它host链接

telnet的功能和nc大致相同,这里不再赘述。telnet自己不能 listen 。

netstat 查看网络设置及统计数据

netstat -ie 查看目前网卡设备都有哪些。

pi@raspberrypi:~ $ netstat -ie
Kernel Interface table
eth0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        ether dc:a6:32:79:54:2a  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 6020  bytes 101123702 (96.4 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 6020  bytes 101123702 (96.4 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.3.102  netmask 255.255.255.0  broadcast 192.168.3.255
        inet6 fe80::2e2e:1d81:cd7a:7342  prefixlen 64  scopeid 0x20<link>
        ether dc:a6:32:79:54:2b  txqueuelen 1000  (Ethernet)
        RX packets 37680  bytes 16617725 (15.8 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 21014  bytes 3456633 (3.2 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

pi@raspberrypi:~ $

netstat -r 显示路由表信息

pi@raspberrypi:~ $ netstat -r
Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
default         192.168.3.1     0.0.0.0         UG        0 0          0 wlan0
192.168.3.0     0.0.0.0         255.255.255.0   U         0 0          0 wlan0
pi@raspberrypi:~ $

netstat -a 显示所有网络连接的信息
常用参数,
-n 显示自数字形式
-p 显示程序名称
-t 显示tcp相关套接字
-l 显示listen状态的套接字
-u 显示udp相关套接字

wget 下载http链接或者ftp链接对应的资源,比如:

pi@raspberrypi:~ $ wget www.baidu.com
--2023-08-23 14:14:31--  http://www.baidu.com/
Resolving www.baidu.com (www.baidu.com)... 14.119.104.254, 14.119.104.189
Connecting to www.baidu.com (www.baidu.com)|14.119.104.254|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 2381 (2.3K) [text/html]
Saving to: ‘index.html’
index.html                                          100%[=================================================================================================================>]   2.33K  --.-KB/s    in 0.001s

2023-08-23 14:14:31 (1.72 MB/s) - ‘index.html’ saved [2381/2381]
pi@raspberrypi:~ $

下载了百度的首页资源。
如果想要提交表单内容,可以使用curl来实现,curl不仅可以发GET请求,POST也可以。

ssh 安全登录远程计算机

ssh 分为服务端和客户端。服务端监听22端口。启动ssh服务端可以使用:

启动ssh服务

pi@raspberrypi:~ $ sudo systemctl start sshd
pi@raspberrypi:~ $ systemctl status sshd
● ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2023-08-18 17:03:42 BST; 4 days ago
       Docs: man:sshd(8)
             man:sshd_config(5)
    Process: 528 ExecStartPre=/usr/sbin/sshd -t (code=exited, status=0/SUCCESS)
   Main PID: 542 (sshd)
      Tasks: 1 (limit: 3933)
        CPU: 1.568s
     CGroup: /system.slice/ssh.service
             └─542 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups

如果发现没有安装ssh服务端,可以安装 openssh-server 即可。

连接远端

使用 ssh user@ip 即可:

pi@raspberrypi:~ $ ssh pi@192.168.3.102
The authenticity of host '192.168.3.102 (192.168.3.102)' can't be established.
ECDSA key fingerprint is SHA256:618KauzvFZ9+J4LaIOKQLB4gNO6tb8hytMSYYxZHvI4.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '192.168.3.102' (ECDSA) to the list of known hosts.
pi@192.168.3.102's password:
Linux raspberrypi 6.1.21-v8+ #1642 SMP PREEMPT Mon Apr  3 17:24:16 BST 2023 aarch64
Last login: Wed Aug 23 14:34:11 2023 from 192.168.3.84
pi@raspberrypi:~ $

如果是第一次链接某个机器,则会出现上述情况,需要敲一个yes,然后输入密码即可。

X 重定向

如果我们连接上对端之后,想运行图形程序,可以将图形内容重定向到当前机器显示,不过在 ssh 连接时就需要明确告诉ssh server,需要重定向图形内容,可以通过-X参数来指定。

pi@raspberrypi:~ $ ssh -X pi@192.168.3.102

这样,就可以将图形内容传回来了。

ssh 直接执行命令

如果我们只需要执行一条或者几条简单的命令,确实没有必须登录终端。直接后面接命令和参数即可。

pi@raspberrypi:~ $ ssh pi@192.168.3.102 "ls -lrth"
pi@192.168.3.102's password:
total 44K
drwxr-xr-x 2 pi pi 4.0K May  3 04:23 Music
drwxr-xr-x 2 pi pi 4.0K May  3 04:23 Documents
drwxr-xr-x 2 pi pi 4.0K Aug 22 14:20 shell_script
-rw-r--r-- 1 pi pi 2.4K Aug 23 14:14 index.html
pi@raspberrypi:~ $

多个命令之间使用 ; 连接:

pi@raspberrypi:~ $ ssh pi@192.168.3.102 "ls -lrth; pwd"
pi@192.168.3.102's password:
total 44K
drwxr-xr-x 2 pi pi 4.0K May  3 04:23 Documents
drwxr-xr-x 2 pi pi 4.0K Aug 22 14:20 shell_script
-rw-r--r-- 1 pi pi 2.4K Aug 23 14:14 index.html
/home/pi
pi@raspberrypi:~ $

磁盘相关

本章节涉及到的命令:mount umount df fdisk fsck mkfs dd md5sum
所有的文件均存放在磁盘上,通过文件系统将文件组织成块,磁盘是块设备。Linux为我们提供了强大的磁盘管理功能。你可以用Linux系统做自己的RAID,做自己的NAS,还可以用LVM动态拉伸分区,跨磁盘拉伸都可以。

要管理存储设备首先要将存储设备挂载。就像你在一根树枝上挂了一个房子,每个树枝都可以挂房子。这里的树枝对应的就是目录,每个目录都可以挂载设备。
系统启动之后会有一些默认的挂载位置,这些信息均记录在一个 /etc/fstab文件中。

[wind@bogon ~]$ lsblk -f
NAME   FSTYPE FSVER LABEL UUID                                 FSAVAIL FSUSE% MOUNTPOINTS
sda                                                                           
├─sda1                                                                        
├─sda2 ext4   1.0         af739eb9-1bbd-4df5-a7f8-6613a4acf999  662.8M    25% /boot
└─sda3 ext4   1.0         a8dbe20e-9bb5-4dc2-9535-ffc0cde61227    8.5G    64% /
zram0                                                                         [SWAP]
[wind@bogon ~]$ cat /etc/fstab 
# /etc/fstab
UUID=a8dbe20e-9bb5-4dc2-9535-ffc0cde61227 /                       ext4    defaults        1 1
UUID=af739eb9-1bbd-4df5-a7f8-6613a4acf999 /boot                   ext4    defaults        1 2
[wind@bogon ~]$ 

字段解释:

  1. UUID,每个文件系统均会有一个唯一ID来标识,可以使用lsblk -f,可以看到 sda3 这块设备,挂载到了 / 根路径。
  2. 挂载路径,每个设备想使用起来,均需要挂到某个位置。
  3. 文件系统名称,ext4 , ext3 , xfs, ntfs , fat32 等
  4. 挂载属性,只读、读写等
  5. dump 备份的频率
  6. fsck 校验文件系统的优先级
    如果想要开机自动挂载,只需要修改这个文件即可,添加自己要挂载的内容。

注意,如果这个文件配错了,开机就起不来了。

mount 查看所有已挂载的文件系统列表

设备的排布也是按照编号,比如/dev/sda1 /dev/sda2等等。

挂载设备到指定位置

mount 设备路径 目标挂载路径

[wind@bogon ~]$ sudo mount ./data mount_point/
[wind@bogon ~]$ lsblk 
NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
loop0    7:0    0    1G  0 loop /home/wind/mount_point
sda      8:0    0   30G  0 disk 
├─sda1   8:1    0    1M  0 part 
├─sda2   8:2    0    1G  0 part /boot
└─sda3   8:3    0 28.5G  0 part /
zram0  252:0    0  1.9G  0 disk [SWAP]
[wind@bogon ~]$ 

上面将一个文件当做设备挂载起来,Linux支持将文件块当一个设备进行挂载使用,挂载的类型为loop设备。loop设备作为一个伪设备使用。
注意挂在前需要给设备制作文件系统。后续讲述。

umount 卸载已经挂载的文件系统

umount 设备名称或者挂载路径,均可以卸载掉。
为什么一定要卸载,卸载的重要性在于,外设的访问通常较慢,但是操作系统为了加快设备的读写,通常会给设备分配缓存空间,来将写入操作先缓存起来,到未来的某个时间或者等到缓存达到阈值又或者设备卸载时等多种触发机制,会将缓存的内容刷到外设,比如磁盘中。
`Bash
[wind@bogon ~]sudo umount /dev/loop0 [wind@bogon ~] lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 30G 0 disk
├─sda1 8:1 0 1M 0 part
├─sda2 8:2 0 1G 0 part /boot
└─sda3 8:3 0 28.5G 0 part /
zram0 252:0 0 1.9G 0 disk [SWAP]
[wind@bogon ~]$


## df 查看设备使用情况
常用参数 -H , human readable
```Bash
[wind@bogon ~]$ df -H
Filesystem      Size  Used Avail Use% Mounted on
devtmpfs        4.2M     0  4.2M   0% /dev
tmpfs           1.1G     0  1.1G   0% /dev/shm
tmpfs           410M  1.2M  409M   1% /run
/dev/sda3        30G   20G  9.2G  68% /
tmpfs           1.1G  8.8M  1.1G   1% /tmp
/dev/sda2       1.1G  256M  696M  27% /boot
D_DRIVE         108G   56G   53G  52% /mnt
tmpfs           205M   95k  205M   1% /run/user/1000
/dev/loop0      1.1G   58k  951M   1% /home/wind/mount_point
[wind@bogon ~]$ 

du 查看文件、目录占用空间大小

递归查看当前路径下的所有的
du -sh ./* -R
-s show summary,按目录为级别汇总大小
-h human readable
--max-depth 统计深度

[wind@bogon ~]$ du -sh ./*
4.0K    ./a.txt
4.0K    ./black_screen
104K    ./build_package
33M ./data
40M ./Desktop
4.0K    ./Documents
640K    ./Downloads
24K ./game
6.9G    ./git
84K ./gl33-gtf-master.txt
248K    ./gl33-master.txt
4.0K    ./Templates
4.0K    ./Videos
[wind@bogon ~]$ 

fdisk 在设备上创建新分区

fdisk 支持多种设备管理格式,GPT,MBR均支持。详细的GPT和MBR的原理,可以自行wiki。

fdisk -l 显示设备分区表

[wind@bogon ~]$ sudo fdisk -l /dev/sda
Disk /dev/sda: 30 GiB, 32212254720 bytes, 62914560 sectors
Disk model: VBOX HARDDISK   
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: D7A333BE-91D8-41FD-A58C-678EA9F60DE7

Device       Start      End  Sectors  Size Type
/dev/sda1     2048     4095     2048    1M BIOS boot
/dev/sda2     4096  2101247  2097152    1G Linux filesystem
/dev/sda3  3125248 62912511 59787264 28.5G Linux filesystem
[wind@bogon ~]$ 

fdisk 初始化分区管理格式、创建、删除分区

初始化分区管理格式为GPT

[wind@bogon ~]$ fdisk /dev/sdb

Welcome to fdisk (util-linux 2.38.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Command (m for help): g
Created a new GPT disklabel (GUID: D6FCCB88-140E-9D43-9744-F2DDF3079D96).

Command (m for help): w
The partition table has been altered.
Syncing disks.

[wind@bogon ~]$ 

详细的其它功能,可以在进入fdisk后,按输入m查看help信息。

mkfs 给分区创建文件系统

mkfs 支持给设备制作多种文件系统:

[wind@bogon ~]$ mkfs.
mkfs.btrfs    mkfs.exfat    mkfs.ext3     mkfs.fat      mkfs.hfsplus  mkfs.msdos    mkfs.vfat     
mkfs.cramfs   mkfs.ext2     mkfs.ext4     mkfs.gfs2     mkfs.minix    mkfs.ntfs     mkfs.xfs      
[wind@bogon ~]$ mkfs.ext4 /dev/sda1

mkfs.ext4 设备路径

fsck 测试修复文件系统

fsck 会检查文件系统的完整新,还能修复损坏的文件系统,修复的程度取决于损坏的程度,在Linux下,已修复的文件会放置在 lost+found 目录中。

[me@linuxbox ~]$ sudo fsck /dev/sdb1
fsck 1.40.8 (13-Mar-2012)
e2fsck 1.40.8 (13-Mar-2012)
/dev/sdb1: clean, 11/3904 files, 1661/15608 blocks

上面输出直接粘贴的。
目前遇到过需要fsck的情况出现在经常断电,或者强制关机的场景,文件系统可能损坏。提醒我们正产关机下电的重要性。

dd 直接从/向设备转移数据

dd if=input_file of=output_file [bs=block_size [count=blocks]]

# 比如
dd if=/dev/sdb of=/dev/sdc
# 也可直接接将某个设备的所有内容dump出来
dd if=/dev/sdb of=flash_drive.img

md5sum 计算文件的md5sum 校验值

md5sum 文件名
当我们需要判断文件的完整性时,可以使用。尤其是在网络进行传输文件之后,非常有必要校验完整性。

文件搜索

本章节涉及到的命令: finds

find 强大的搜索命令

常用的方式:

  1. 直接 find | grep file_name
    此种方式,find的默认搜索路径是当前路径以及当前路径下的所有子目录。
pi@raspberrypi:~ $ find | grep nohup
./shell_script/nohup.out
pi@raspberrypi:~ $

非常简洁但实用的查找方式,适合于你记住了项目种的文件名或者文件名的一部分,但不记得路径。

  1. 通过名字查找文件
    find 路径 -name 文件名
pi@raspberrypi:~ $ find ./ -name nohup.out
./shell_script/nohup.out
pi@raspberrypi:~ $

忽略大小写,实用 -iname 而不是 -name
文件名还支持模糊匹配:

pi@raspberrypi:~ $ find ./ -iname "*hup*"
./shell_script/nohup.out
pi@raspberrypi:~ $
  1. 通过文件类型查找文件
    可以通过指定 type 的方式查找文件,比如要找一个目录文件:
pi@raspberrypi:~ $ find ./ -type d -name wahaha
./shell_script/wahaha
pi@raspberrypi:~ $

这时候需要再添加 -name 指定名称参数

  1. 对查找到的文件自定义操作
pi@raspberrypi:~ $ find ./ -name "nohup*" -and -type f -size +1c -exec ls -l '{}' ';'
-rw------- 1 pi pi 12 Aug 23 15:40 ./shell_script/nohup.out

可以使用xargs实现同样的操作:

pi@raspberrypi:~ $ find ./ -name "nohup*" -and -type f -size +1c | xargs ls -l
-rw------- 1 pi pi 12 Aug 23 15:40 ./shell_script/nohup.out
pi@raspberrypi:~ $

find 查找到的文件,通过管道传递给 xargs,xargs 将内容作为 ls -l 的输入 。

  1. 指定查找的文件最大层级
pi@raspberrypi:~ $ find ./ -maxdepth 1 -name a.c
pi@raspberrypi:~ $ find ./ -maxdepth 2 -name a.c
./shell_script/a.c
pi@raspberrypi:~ $
  1. 添加多个查找条件
pi@raspberrypi:~ $ find ./ -name "nohup*" -and -type f -size +1c
./shell_script/nohup.out
pi@raspberrypi:~ $

size 支持的常用单位 c(字节),k,M,G,+表示大于,-表示小于。

还可以查找指定大小范围的文件、指定时间内修改的文件、指定权限的文件、合并多条件查询等。

包管理

本章节涉及到命令:dpkg apt rpm yum dnf


发行版

包文件是组成软件包系统的基本软件单元,它是由组成软件包的文件压缩而成的文件集。有些包直接提供一个命令,有些包则提供一些二进制库和开发所需的文件。
包和包之间也存在依赖关系,一些包需要依赖基础环境包存在。

包管理的命令区分不同的发行版本也会不同。


包管理命令

明白自己使用的是Debian系列的还是RedHat系列的发行版是第一步。别在Debian系列的系统上用rpm或者yum使劲敲发现一直报错,还不知道为啥。

apt 安装、卸载、更新软件

在开始安装之前,更新元信息。sudo apt-get update

包搜索

apt-cache search package_name
在使用apt-cache search之前,先更新元信息。apt-get update

安装、卸载、更新

apt-get install package_name
apt-get remove package_name
apt-get update package_name
查看已安装软件的信息:
apt-cache show package_name

yum 安装、卸载、更新软件

搜索

yum search package_name
在使用yum search之前,也可以生成cache。yum makecache

安装、卸载、更新

yum install package_name
yum remove package_name
yum update package_name
查看已安装软件的信息:
yum info package_name

dpkg 安装、卸载、查询

dpkg -i pacakge_file
dpkg -r package_file
dpkg -l | grep package_name
查找文件是由那个包提供的:

dpkg --search file_name

rpm 安装、卸载、查询

rpm -i package_file
rpm -e package_name
rpm -q package_name
查找文件是由那个包提供的:
rpm -qf file_name

[wind@bogon include]$ pwd
/usr/include
[wind@bogon include]$ ls | head
aio.h
aliases.h
alloca.h
a.out.h
argp.h
argz.h
ar.h
arpa
asm
asm-generic
[wind@bogon include]$ rpm -qf aio.h
glibc-headers-x86-2.36-9.fc37.noarch
[wind@bogon include]$ 

时间与日期

date

显示当前日期和时间,可以 -s 设置系统时间,格式"YYYY-MM-DD H:M:S。也可以设置日期的输出格式,在脚本中是非常有用的按照自己想要的格式获取当前时间的命令。
十分常用的命令,当系统时间不正确时,可以自行设置。

内存相关

本章节涉及到的命令:free

free 显示当前内存使用情况

pi@raspberrypi:~ $ free -h
               total        used        free      shared  buff/cache   available
Mem:           3.7Gi       267Mi       2.8Gi        35Mi       685Mi       3.3Gi
Swap:           99Mi          0B        99Mi

常用参数:
-h human readable
-m 按MB显示
-g 按GB显示

各字段的解释:
total: 总的内存大小
used: 已经使用的内存大小
free: 空闲的内存大小
shared: 共享的内存大小,这部分内存是由多个进程共享的,例如tmpfs文件系统。
buff/cache: 缓冲区和缓存的内存大小。这部分内存是由系统用来缓存磁盘数据和文件系统元数据的,可以提高系统的性能。当其他进程需要内存时,这部分内存可以被回收。
available: 可用的内存大小。这部分内存是指系统可以不用交换就能分配给进程的内存大小。

CPU 主板相关

lscpu 查看CPU型号、主频、缓存大小、支持指令集

或者 cat /proc/cpuinfo

lspci 查看pci设备信息

lspci -v 显示更详细的信息

dmidecode 查看主板信息

perf CPU端性能分析

详情参考:
https://www.brendangregg.com/perf.html

gdb,pstack,gstack 调用栈分析

当一个进程线上运行,但是卡住了,不知道用户态卡住在哪里,就可以使用gdb,再attach到进程。
或者如果系统装了 pstack 或者 gstack 可以直接跟 PID ,就可以查看,不仅可以把进程的调用栈打出来,还可以打印线程的调用栈。

stace 跟踪应用程序的系统调用。

用来排查进程的行为是否符合我们的预期,比如是否加载了我们期望的动态链接库、是否调用了指定的系统调用等。
是一个查看可执行文件执行时行为的利器。
strace -e 跟踪指定的系统调用列表
strace -p PID 指定进程ID跟踪。
strace 不仅能够跟踪系统调用,也能够统计每个系统调用执行的时间。
-T 参数可以统计出每个系统调用执行花费的时间。单位(s)
-tt 参数可以打印出追踪的系统调用开始执行时的挂钟时间。(精确到us)

[wind@bogon ~]$ strace -e "openat,read" -tt -T ls
15:12:16.350608 openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 <0.000009>
15:12:16.350775 openat(AT_FDCWD, "/lib64/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3 <0.000007>
15:12:16.350842 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832 <0.000005>
15:12:16.351009 openat(AT_FDCWD, "/lib64/libcap.so.2", O_RDONLY|O_CLOEXEC) = 3 <0.000008>
15:12:16.351078 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832 <0.000005>
15:12:16.351240 openat(AT_FDCWD, "/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 <0.000011>
15:12:16.351505 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\320v\2\0\0\0\0\0"..., 832) = 832 <0.000005>
15:12:16.351681 openat(AT_FDCWD, "/lib64/libpcre2-8.so.0", O_RDONLY|O_CLOEXEC) = 3 <0.000008>
15:12:16.351752 read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832 <0.000005>
15:12:16.352376 openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3 <0.000009>
15:12:16.352538 openat(AT_FDCWD, ".", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3 <0.000007>
black_screen   data Documents  game       git              gl33-master.txt  log      Music     Public       Templates
build_package  Desktop  Downloads  genbu_service  gl33-gtf-master.txt  hello        mount_point  Pictures  shellscript  Videos
15:12:16.353080 +++ exited with 0 +++
[wind@bogon ~]$ 

lsof,fuser 查看进程都使用了哪些设备或者文件,及设备、文件都被哪些进程使用。

lsof 排查句柄泄露的好工具。

当我们遇到进程的实际使用内存(RSS)一直在上涨的时候,除了怀疑有内存泄露之外,如果进程中有文件操作,还需要怀疑是否存在句柄泄露(根本上还是内存泄露),即fd泄露。句柄泄露不仅包含文件句柄,也包含网络句柄、等其它句柄。
这种情况,我们使用 lsof 来观测进程是否一直
lsof -p

fuser 查看设备、文件被哪些进程占用

fuser -v /dev/xxxx

valgrind 内存检查利器

valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all ./a.out

常见查问题使用到的命令

linux下观测系统工具概览

上述图片来自 brendangregg

Tips

如何在终端使用计算器

  1. 使用算数扩展 echo $((expr)),计算整数的加减乘除次幂取余没问题。
  2. 使用bc ,可以计算小数,设置精度。
  3. 使用python 前面两个能干的,这个都能干。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容