194. Linux中的命令之进程与程序
bash和sh
区别与联系
sh
路径为 /bin/sh
sh就是Bourne shell
这个是UNIX标准的默认shell,对它评价是concise简洁 compact紧凑 fast高效,由AT&T编写,属于系统管理shell
bash
路径为 /bin/bash
bash是 GNU Bourne-Again SHell (GNU 命令解释程序 “Bourne二世”)
是linux标准的默认shell ,它基于Bourne shell,吸收了C shell和Korn shell的一些特性。bash是Bourne shell的超集,bash完全兼容Bourne shell,也就是说用Bourne shell的脚本不加修改可以在bash中执行,反过来却不行,bash的脚本在sh上运行容易报语法错误。
小结
bash是sh的完整版,bash完全兼容sh命令,反之不行
用法
具体信息请使用如下命令查看
查看bash
1
$ man bash
查看sh
1
$ man sh
这个命令有点问题, 具体请自行搜索吧
解决命令的权限问题, 可以扩大权限的范围
比如要向 test.asc 文件中随便写入点内容,可以:
1 | $ echo "hello world" > test |
如果将 test.asc 权限设置为只有 root 用户才有权限进行写操作:
1 | $ sudo chown root:root test |
然后,我们使用 sudo 并配合 echo 命令再次向修改权限之后的 test.asc 文件中写入信息:
1 | $ sudo echo "hello" >> test |
这时可以看到 bash 拒绝这么做,说是权限不够。这是因为重定向符号 “>
” 和 “>>
” 也是 bash 的命令。我们使用 sudo 只是让 echo 命令具有了 root 权限,但是没有让 “>
” 和 “>>
” 命令也具有 root 权限,所以 bash 会认为这两个命令都没有像 test.csv文件写入信息的权限。
解决这一问题的途径有两种。
方法1
利用
"sh -c"
命令,它可以让 bash 将一个字串作为完整的命令来执行,这样就可以将 sudo 的影响范围扩展到整条命令。具体用法如下:1
$ sudo bash -c 'echo "hello" >> test'
或者
1
$ sudo sh -c 'echo "hello" >> test'
方法2
利用管道和 tee 命令,该命令可以从标准输入中读入信息并将其写入标准输出或文件中,具体用法如下:
1
2$ echo "hello" | sudo tee -a test
hello注意,tee 命令的 “
-a
” 选项的作用等同于 “>>
” 命令,如果去除该选项,那么 tee 命令的作用就等同于 “>” 命令
命令解释器工作模式
命令解释器(这里是/bin/sh或/bin/bash) 有两种工作模式:交互模式和非交互模式。
交互模式
交互模式如下图所示,就是使用 ssh 连接到 Linux 服务器上,然后在终端上敲入命令就可以显示对应结果。这样与终端进行交互执行命令的方式就称为交互模式。

非交互模式
非交互模式如下图所示,非交互模式就是调用 bash 解释器,通过 bash -c
或 sh -c
后接命令的形式来解释执行命令。

安装软件
从命令行安装应用程序最常见的方法是通过软件仓库(software respositories)(软件存储的地方)使用软件包管理器。所有 Linux 应用程序都以包的形式分发,这些包只不过是与包管理系统(package management system)相关联的文件。每个 Linux 发行版都有一个包管理系统,但它们并不完全相同。
什么是包管理系统
软件包管理系统是由一组工具和文件格式组成,它们一起用于安装、更新和卸载 Linux 应用程序。最常见的两个软件包管理系统来自Red Hat和Debian。Red Hat、CentOS和Fedora都使用rpm系统(.rpm
文件),而Debian、Ubuntu、Mint和Ubuntu使用dpkg(.deb
文件)。Gentoo Linux使用一个名为Portage的系统,Arch Linux只使用tarball(.tar
文件)。这些系统之间的主要区别在于它们如何安装和维护应用程序。
你可能很想知道一个 .rpm
, .deb
, 或者 .tar
文件里面放的是什么。其实这都只是一些普通的、旧的归档文件(比如zip
),其中包含应用程序的代码、安装说明、依赖项(它可能依赖于哪些其他应用程序)以及配置文件应该放在哪里。读取并执行所有这些指令的软件称为包管理器(package manager)。
Debian、Ubuntu、Mint等
Debian,Ubuntu,Mint 还有其他基于 Debian 的发行版都使用 .deb 文件和 dpkg 包管理系统。在这种系统中有两种方式安装应用程序,你可以使用 apt 应用程序从软件仓库中安装,也可以使用 dpkg 应用程序从 .deb
文件中安装应用程序。
下面我们分别来看下这两种方式。
使用 apt
安装很容易:
1 | $ sudo apt install app_name |
使用 apt
卸载应用也很简单:
1 | $ sudo apt remove app_name |
要更新已安装的应用,首先需要更新应用仓库:
1 | $ sudo apt update |
更新完成后,就可以更新任何应用程序了:
1 | $ sudo apt upgrade |
如果要只更新某一个应用呢?也可以:
1 | $ sudo apt update app_name |
另外,如果你想要安装的应用在 Debian 仓库中不可用,但是可以下载一个 .deb
文件,那么你可以使用 dpkg
(apt帮助管理)手动安装:
1 | $ sudo dpkg -i app_name.deb |
RHEL、CentOS、Fedora、Mageia和OpenMandriva
Red Hat 的上游项目 Fedora 和“中游”项目 CentOS 使用 dnf
软件包管理器。它有自己的语法,是RPM系统的前端。虽然语法不同,但 dnf
在机制和目标相同的意义上与 apt
相似。Mageia 和 OpenMandriva 发行版曾经专门关注用于包管理的 urpmi,现在它们的发行版中也包括 dnf
。
dnf
包管理器实际上是 yum
命令的继承者。长期以来,yum
都在用户服务器中使用自己的语言,因此为了避免破坏用户系统上已经存在了十多年的自定义脚本,yum
和 dnf
现在可以互换(事实上,yum
现在是基于 dnf
的)。
安装一个应用的命令:
1 | $ sudo dnf install app_name |
卸载应用的命令也很简单:
1 | $ sudo dnf remove app_name |
更新:
1 | $ sudo dnf upgrade --refresh |
dnf
(或 yum
)命令是 RPM 打包系统的前端。如果在软件存储库中找不到应用程序,但可以直接从其供应商网站下载,则可以使用 dnf 手动安装 .rpm
文件。
1 | $ sudo dnf install ./app_name.rpm |
如你所见,通过命令行安装、卸载或者更新 Linux 应用一点都不难。事实上,一旦你习惯了它,你就会发现它比基于GUI的管理工具更快。
关于命令行安装的更多信息,可以访问Debian Apt wiki官网,Yum cheat sheet,还有DNF wiki。
查看进程
ps
Linux ps (process status)命令用于显示当前进程的状态,类似于 windows 的任务管理器。
语法
1 | ps [options] [--help] |
参数说明
ps 的参数非常多, 在此仅列出几个常用的参数并大略介绍含义
参数 | 说明 |
---|---|
-A |
列出所有的进程 |
-e |
显示所有进程 |
-f |
全格式 |
-w |
显示加宽可以显示较多的资讯 |
-au |
显示较详细的资讯 |
-aux |
显示所有包含其他使用者的行程 |
au(x) 输出格式
1 | USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND |
字段 | 说明 |
---|---|
USER |
行程拥有者 |
PID |
pid |
%CPU |
占用的 CPU 使用率 |
%MEM |
占用的记忆体使用率 |
VSZ |
占用的虚拟记忆体大小 |
RSS |
占用的记忆体大小 |
TTY |
终端的次要装置号码 (minor device number of tty) |
STAT |
见[STAT: 该行程的状态](#STAT: 该行程的状态) |
START |
行程开始时间 |
TIME |
执行的时间 |
COMMAND |
所执行的指令 |
STAT: 该行程的状态
参数 | 说明 |
---|---|
D |
无法中断的休眠状态 (通常 IO 的进程) |
R |
正在执行中 |
S |
静止状态 |
T |
暂停执行 |
Z |
不存在但暂时无法消除 |
W |
没有足够的记忆体分页可分配 |
< |
高优先序的行程 |
N |
低优先序的行程 |
L |
有记忆体分页分配并锁在记忆体内 (实时系统或捱A I/O) |
例子
查找指定进程格式:
1 | ps -ef | grep 进程关键字 |
grep
命令是查找,中间的|
是管道命令是指ps
命令与grep
同时执行。
例如显示所有的Java进程:
1 | $ ps -ef | grep java |
显示进程信息
1 | $ ps -A |
显示所有进程信息,连同命令行:
1 | # ps -ef //显示所有命令,连带命令行 |
top
在Linux系统中,查看系统运行状态(或者故障排除),比如内存或者CPU使用率的时候,可以使用top
命令,它提供了系统运行情况的信息。根据man
手册,top
程序提供了运行系统的动态实时视图,它可以显示系统摘要信息以及当前由Linux内核管理的进程或线程的列表。
通常,在top
命令的信息中,我们只需要快速浏览一下,就可以确定当前系统的问题是什么。但是top
命令的的功能远不止我们看到的这些,具体的功能会因你运行的系统版本(procps-ng, Busybox, BSD)而异,可以参照man
手册。
要运行top命令,可以直接在终端中键入:
1 | $ top |
正在运行的进程显示在顶部屏幕的表标题下方,系统统计信息显示在其上方。如下图所示:

按 Z 键可以更改输入的颜色,这可以让输出更易读。按 1 键查看系统上每个CPU 核心的图形表示。反复按 1 以评估CPU核心的核心统计信息。
通过调用 top
命令,然后按 m 键,可以以图形方式显示内存使用情况。
字段说明
1 | $top |
第一行
参数 说明 09:14:56
系统当前时间 264 days, 20:56
系统开机到现在经过了多少时间 1 users
当前2用户在线 load average: 0.02, 0.04, 0.0
0系统1分钟、5分钟、15分钟的CPU负载信息 第二行
参数 说明 Tasks
任务; 87 total
很好理解,就是当前有87个任务,也就是87个进程。 1 running
1个进程正在运行 86 sleeping
86个进程睡眠 0 stopped
停止的进程数 0 zombie
僵死的进程数 第三行
参数 说明 Cpu(s)
表示这一行显示CPU总体信息 0.0%us
用户态进程占用CPU时间百分比,不包含renice值为负的任务占用的CPU的时间。 0.7%sy
内核占用CPU时间百分比 0.0%ni
改变过优先级的进程占用CPU的百分比 99.3%id
空闲CPU时间百分比 0.0%wa
等待I/O的CPU时间百分比 0.0%hi
CPU硬中断时间百分比 0.0%si
CPU软中断时间百分比 注:这里显示数据是所有cpu的平均值,如果想看每一个cpu的处理情况,按1即可;折叠,再次按1;
第四行
参数 说明 Men
内存的意思 8175320kk total
物理内存总量 8058868k used
使用的物理内存量 116452k free
空闲的物理内存量 283084k buffers
用作内核缓存的物理内存量 第五行
参数 说明 Swap
交换空间 6881272k total
交换区总量 4010444k used
使用的交换区量 2870828k free
空闲的交换区量 4336992k cached
缓冲交换区总量 再下面就是进程信息
参数 说明 PID
进程的ID USER
进程所有者 PR
进程的优先级别,越小越优先被执行 NInice
值 VIRT
进程占用的虚拟内存 RES
进程占用的物理内存 SHR
进程使用的共享内存 S
进程的状态。S表示休眠,R表示正在运行,Z表示僵死状态,N表示该进程优先值为负数 %CPU
进程占用CPU的使用率 %MEM
进程使用的物理内存和总内存的百分比 TIME+
该进程启动后占用的总的CPU时间,即占用CPU使用时间的累加值。 COMMAND
进程启动命令名称
常用的 top 命令选项
如果只查找由特定用户启动的进程,可以使用 -u
选项获取该信息:
1 | $ top -u 'username' |
要获取系统上空闲进程的列表,请使用 -i
选项:
1 | $ top -i |
信息的更新时间默认为3秒,可以将更新间隔设置为以秒为单位的任意值。比如我们将其更改为5,如下所示:
1 | $ top -d 5 |
此外,还可以在计时器上运行top。例如,以下命令将迭代次数设置为两次,然后退出:
1 | $ top -n 2 |
top命令交互操作指令
参数 | 说明 |
---|---|
q |
退出top 命令 |
<Space> |
立即刷新 |
s |
设置刷新时间间隔 |
c |
显示命令完全模式 |
t: |
显示或隐藏进程和CPU状态信息 |
m |
显示或隐藏内存状态信息 |
l |
显示或隐藏uptime 信息 |
f |
增加或减少进程显示标志 |
S |
累计模式,会把已完成或退出的子进程占用的CPU时间累计到父进程的MITE+ |
P |
按%CPU 使用率排行 |
T |
按MITE+ 排行 |
M |
按%MEM 排行 |
u |
指定显示用户进程 |
r |
修改进程renice 值 |
kkill |
进程 |
i |
只显示正在运行的进程 |
W |
保存对top的设置到文件^/.toprc ,下次启动将自动调用toprc 文件的设置。 |
h |
帮助命令。 |
q |
退出 |
注:
- 强调一下,使用频率最高的是
P
、T
、M
,因为通常使用top
,我们就想看看是哪些进程最耗cpu资源、占用的内存最多;- 通过”
shift + >
”或”shift + <
”可以向右或左改变排序列- 如果只需要查看内存:可用
free
命令。- 只查看
uptime
信息(第一行),可用uptime
命令;
实例
实例1:多核CPU监控
在top基本视图中,按键盘数字“1
”,可监控每个逻辑CPU的状况;
1 | [rdtfr@bl685cb4-t ^]$ top |
实例2:高亮显示当前运行进程
1 | 在top基本视图中,按键盘“b”(打开/关闭加亮效果); |
实例3:显示完整的程序命令
命令:top -c
1 | [rdtfr@bl685cb4-t ^]$ top -c |
实例4:显示指定的进程信息
命令:top -p pidid
1 | /opt/app/tdv1/config#top -p 17265 |
指定进程信息有多个时,需要结合其它工具将回车替换为,(-p
支持pid,pid,pid
语法)
命令:top -p pgrep MULTI_PROCESS | tr “\n” ”,” | sed ‘s/,$//’
1 | /opt/app/tdv1$top -p `pgrep java | tr "\\n" "," | sed 's/,$//'` |
使用top定位进程
使用 Shift+L
组合键可以按名称查找进程。这将在加粗的表格标题行上方创建一个提示。键入要查找的流程的名称,然后按 Enter 键或 Return 键,以查看在新排序的流程列表中突出显示的该进程的实例。
使用top停止进程
你可以使用top命令停止或者“杀掉”一个正在运行的进程。首先,使用 Shift+L
或 pgrep
查找要停止的进程,然后按 k
键并输入要停止进程的 ID
。默认值为列表顶部的任何值,因此请确保在按下回车键之前输入正确的 PID
,否则可能会停掉你本不打算停止的进程。

top命令有许多迭代,包括htop, atop, btop, 还有 ttop。此外,还有一些专用的 top 命令,比如用于电源的 powertop,以及用于网络信息的 ntop 等。
ldd
查看程序依赖库
作用
用来查看程序运行所需的共享库,常用来解决程序因缺少某个库文件而不能运行的一些问题。
示例
查看test程序运行所依赖的库:
1 | $ldd test |
- 第一列:程序需要依赖什么库
- 第二列: 系统提供的与程序需要的库所对应的库
- 第三列:库加载的开始地址
通过上面的信息,我们可以得到以下几个信息:
- 通过对比第一列和第二列,我们可以分析程序需要依赖的库和系统实际提供的,是否相匹配
- 通过观察第三列,我们可以知道在当前的库中的符号在对应的进程的地址空间中的开始位置
如果依赖的某个库找不到,通过这个命令可以迅速定位问题所在;
注解
原理:
ldd
不是个可执行程式,而只是个shell脚本;ldd
显示可执行模块的dependency的工作原理,其实质是通过ld-linux.so
(elf动态库的装载器)来实现的。ld-linux.so
模块会先于executable
模块程式工作,并获得控制权,因此当上述的那些环境变量被设置时,ld-linux.so
选择了显示可执行模块的dependency。
lsof
一切皆文件
lsof
(list open files)是一个查看当前系统文件的工具。在linux环境下,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件。如传输控制协议 (TCP
) 和用户数据报协议 (UDP
) 套接字等,系统在后台都为该应用程序分配了一个文件描述符,该文件描述符提供了大量关于这个应用程序本身的信息。
lsof打开的文件可以是:
- 普通文件
- 目录
- 网络文件系统的文件
- 字符或设备文件
- (函数)共享库
- 管道,命名管道
- 符号链接
- 网络文件(例如:NFS file、网络socket,unix域名socket)
- 还有其它类型的文件,等等
命令参数
参数 | 说明 |
---|---|
-a |
列出打开文件存在的进程 |
-c<进程名> |
列出指定进程所打开的文件 |
-g |
列出GID号进程详情 |
-d<文件号> |
列出占用该文件号的进程 |
+d<目录> |
列出目录下被打开的文件 |
+D<目录> |
递归列出目录下被打开的文件 |
-n<目录> |
列出使用NFS的文件 |
-i<条件> |
列出符合条件的进程。(4、6、协议、:端口、 @ip ) |
-p<进程号> |
列出指定进程号所打开的文件 |
-u |
列出UID号进程详情 |
-h |
显示帮助信息 |
-v |
显示版本信息 |
使用实例
实例1:无任何参数
1 | $lsof | more |
说明:
lsof输出各列信息的意义如下:
COMMAND
:进程的名称PID
:进程标识符PPID
:父进程标识符(需要指定-R参数)USER
:进程所有者PGID
:进程所属组FD
:文件描述符,应用程序通过文件描述符识别该文件。如cwd
、txt
等:标识 说明 cwd
表示current work dirctory,即:应用程序的当前工作目录,这是该应用程序启动的目录,除非它本身对这个目录进行更改 txt
该类型的文件是程序代码,如应用程序二进制文件本身或共享库,如上列表中显示的 /sbin/init 程序 lnn
library references (AIX); er
FD information error (see NAME column); jld
jail directory (FreeBSD); ltx
shared library text (code and data); mxx
hex memory-mapped type number xx. m86
DOS Merge mapped file; mem
memory-mapped file; mmap
memory-mapped device; pd
parent directory; rtd
root directory; tr
kernel trace file (OpenBSD); v86
VP/ix mapped file; 0
表示标准输入 1
表示标准输出 2
表示标准错误 一般在标准输出、标准错误、标准输入后还跟着文件状态模式:r、w、u等
标识 说明 u
表示该文件被打开并处于读取/写入模式 r
表示该文件被打开并处于只读模式 w
表示该文件被打开并处于 空格
表示该文件的状态模式为unknow,且没有锁定 -
表示该文件的状态模式为unknow,且被锁定 同时在文件状态模式后面,还跟着相关的锁
标识 说明 N
for a Solaris NFS lock of unknown type; r
for read lock on part of the file; R
for a read lock on the entire file; w
for a write lock on part of the file;(文件的部分写锁) W
for a write lock on the entire file;(整个文件的写锁) u
for a read and write lock of any length; U
for a lock of unknown type; x
for an SCO OpenServer Xenix lock on part of the file; X
for an SCO OpenServer Xenix lock on the entire file; space
if there is no lock. TYPE
:文件类型,如DIR
、REG
等,常见的文件类型:参数 说明 DIR
表示目录 CHR
表示字符类型 BLK
块设备类型 UNIX
UNIX 域套接字 FIFO
先进先出 (FIFO) 队列 IPv4
网际协议 (IP) 套接字 DEVICE
:指定磁盘的名称SIZE
:文件的大小NODE
:索引节点(文件在磁盘上的标识)NAME
:打开文件的确切名称
实例2:查找某个文件相关的进程
1 | $lsof /bin/bash |
实例3:列出某个用户打开的文件信息
1 | $lsof -u username |
实例4:列出某个程序进程所打开的文件信息
1 | $lsof -c mysql |
-c
选项将会列出所有以mysql这个进程开头的程序的文件,其实你也可以写成 lsof | grep mysql
, 但是第一种方法明显比第二种方法要少打几个字符;
实例5:列出某个用户以及某个进程所打开的文件信息
1 | $lsof -u test -c mysql |
实例6:通过某个进程号显示该进程打开的文件
1 | $lsof -p 11968 |
实例7:列出所有的网络连接
1 | $lsof -i |
实例8:列出所有tcp 网络连接信息
1 | $lsof -i tcp |
实例9:列出谁在使用某个端口
1 | $lsof -i :3306 |
实例10:列出某个用户的所有活跃的网络端口
1 | $lsof -a -u test -i |
实例11:根据文件描述列出对应的文件信息
1 | $lsof -d description(like 2) |
示例:
1 | $lsof -d 3 | grep PARSER1 |
说明: 0
表示标准输入,1
表示标准输出,2
表示标准错误,从而可知:所以大多数应用程序所打开的文件的 FD
都是从 3
开始
实例12:列出被进程号为1234的进程所打开的所有IPV4 network files
1 | $lsof -i 4 -a -p 1234 |
实例13:列出目前连接主机nf5260i5-td
上端口为:20, 21, 80
相关的所有文件信息,且每隔3秒重复执行
1 | lsof -i @nf5260i5-td:20,21,80 -r 3 |
实例14: 解决类似umount target is busy挂载盘卸载不掉问题
通过lsof
命令处理
1 | # lsof /mnt/ |
找到PID
对应的进程或者服务,然后杀死或者停止相应服务即可。
后台运行
Linux 命令是使用终端与系统交互的好方法。但是,有时可能需要一段时间才能完成手头的任务。这迫使用户等待相当长的时间或完全生成一个新的 shell。
幸运的是,您可以通过一些简单的方法在后台运行 Linux 命令。
&
在你的命令后添加一个 &
运行 Linux 后台命令的最简单方法是在命令后添加与号 (&
)。例如,如果从终端启动 gedit 文本编辑器,则在关闭编辑器之前无法使用 shell。但是,当您在命令中添加额外的 & 时,您将能够立即使用 shell。
1 | $ gedit & |
对于需要长时间执行的任务,&
符号可以使当前终端窗口不被占用,这样就能继续在同一个终端上工作,甚至关闭终端窗口也不会影响任务的正常执行。不过需要注意的是,如果要求这个任务输出内容到标准输出中(例如 echo
或 ls
),即便使用了 &
,也会等待这些输出任务在前台运行完毕;当使用 &
将一个进程放置到后台运行的时候,Bash 会提示这个进程的进程 ID。在 Linux 系统中运行的每一个进程都有一个唯一的进程 ID,我们可以使用进程 ID 来暂停、恢复或者终止对应的进程。
nohup
Linux 中 的nohup命令允许管理员运行不受挂断(Hang Up)信号影响的终端命令。您可以使用 nohup 在后台运行 Linux 命令。
nohup 的一个主要好处是,即使您退出 shell,您的命令也会运行。此外,它会生成执行的日志文件。在当前目录或 $HOME
中查找nohup.out
。语法是:
1 | nohup 命令 |
如: 后台运行Nmap 端口扫描
1 | $ nohup sudo nmap -sS --top-ports=15 192.168.150.1/24 |
Q: Within zsh the command
&!
is a shortcut for disown, i.e. the program won’t get killed upon exiting the invoking shell.
程序退出不被杀死, 同时如果再次登录shell, 那么使用 ps -ef | grep "clash"
会显示clash 的父进程是 1
, 这是因为创建的父进程被杀死, 那么由该进程创建的子进程会在父进程被杀死后, 父进程改为 1
1 | nohup clash &> /dev/null &! |
程序退出会被杀死的进程
1 | nohup clash &> /dev/null & |
fg和bg
命令由前台转后台
- 键入
ctrl+z
以暂停这条命令并返回客户端 - 键入
bg
命令让这条shell命令在后台执行 - 键入
disown -h
这条命令保证当终端关闭时,Shell脚本不会被杀死
更一般地
ctrl+z将任务暂停,这时屏幕会打印这样一句
[1]+ Stopped ./myserver
使用
bg %1
将这个任务转入后台运行(1
是刚才暂停任务的job号,步骤1中屏幕提示方括号里面的内容,如果忘了也可以用jobs
命令查看)使用
disown –h %1
指定shell退出时不要发送SIGHUP给任务1(注意任务号前面有个%)
命令由后台转前台
- 键入
jobs
查看后台执行的命令 - 键入
fg %<d>
将 jobs[d] 切换到前台
disown
使用 disown 将 Linux 命令设置为后台运行
Linux 中 的disown
命令可以轻松地在后台运行命令。首先,您需要使用&
运算符在后台发送任务。然后,键入disown
以将其与shell分离。
1 | $ gedit & disown |
disown
的一个主要优点是,与 nohup 一样,当您关闭 shell 或注销时,系统不会终止您的任务。
使用 Tmux 在后台运行 Linux 命令
Tmux 是一个强大的多路复用器,它允许我们在单个窗口中运行多个终端会话。对于不熟悉它的人来说,学习 tmux 是一个很好的选择。Tmux 使在 Linux 中运行后台命令毫不费力。
1 | tmux new -d 'ping -c 10 8.8.8.8 > www.itpro.net.cn.log' |
当您运行上述tmux命令时,它将在单独的 shell 中执行ping命令并将其保留在后台。您可以使用此方法在后台执行任何 Linux 命令。
能够在后台运行命令使系统管理对管理员来说更有效率。您可以通过多种方式为您的任务做背景。像&和Ctrl + Z
这样的 Bash 功能很方便,但系统会在 shell 关闭时终止后台作业。另一方面,即使您注销或终止 shell ,诸如nohup
和disown
之类的工具也会使您的命令保持运行。
如果您将程序长时间留在后台,如果编码不当,它们可能会变成僵尸进程。这些过程会显着降低系统速度。因此,请确保每隔一段时间识别并杀死僵尸进程。
设置开机自启动
shell启动时自启动
不建议使用: 因为每次启动shell都会启动, 而像clash这样的程序只需要执行一次即可, 并且如果多个用户执行, 会导致后执行clash的shell执行失败.
打开
~/.bashrc
或./.zshrc
在文件末尾添加又在每次用户登录时执行的命令, 如
1
nohup clash &> /dev/null &
source ~/.bashrc
或source ~/.zshrc
以使更改生效
使用”启动应用程序偏好设置”
(Startup Applications Preferences)
- Startup Applications Preferences 的配置信息通常存储在
~/.config/autostart
目录中的.desktop
文件中. 每个启动的应用程序都有一个相应的.desktop
文件,该文件包含有关应用程序启动方式的信息。
添加文件 vim <startup_program>.desktop
, 添加如下配置
1 | [Desktop Entry] |
如:
1 | Desktop Entry] |
Exec
行指定了要在启动时运行的程序的路径
使用systemctl方式-RECOMMNAD
采用systemctl的方式
强烈建议看看这个文档,里面介绍了一些参数的具体含义.
准备工作
写好脚本(假设脚本名为 exec_clash.sh)或将程序放入本地可以运行, 这里以clash为例, 其中, clash位于
/usr/local/bin/clash
注: exec_clash.sh 中的 exec_clash 是可根据具体脚本的功能改为相应的名字
若是脚本, 则赋予脚本运行权限:
chmod +x exec_clash.sh
配置
编写一个
<clash>.service
, 内容如下:1
2
3
4
5
6
7
8
9
10
11
12
13[Unit]
Description=clash
[Service]
Type=simple
User=qeuroal
ExecStart=/usr/local/bin/clash
Restart=always
RestartSec=30
StartLimitInterval=0
[Install]
WantedBy=multi-user.target graphical.target说明
User
: 表示以哪个用户启动,不写默认是root. 而用户名也决定了程序启动去哪里读取相应的配置ExecStart
: 就是要执行的语句。由于为脚本赋予了
x
(可执行)权限, 因此,ExecStart
后面的内容可以直接写成脚本的绝对路径Restart=always
: 只要不是通过systemctl stop
来停止服务,任何情况下都必须要重启服务,默认值为no。还可以配置有条件重启,具体参考上述文档。RestartSec=5
: 重启间隔,比如某次异常后,等待5(s)再进行启动,默认值0.1(s)StartLimitInterval
: 无限次重启,默认是10秒内如果重启超过5次则不再重启,设置为0表示不限次数重启
拷贝
clash.service
到/lib/systemd/system
, 需要注意权限问题修改配置文件后需要重加载配置:
sudo systemctl daemon-reload
设置开机自启:
sudo systemctl enable clash.service
之后就可以直接重启了
相关操作
操作 命令 启动 sudo systemctl start clash.service
停止 sudo systemctl stop clash.service
查看状态 sudo systemctl status clash.service
测试自动重启功能
查看程序启动命令
最佳解决方法
大多数默认应用程序将具有位于/usr/share/applications
中的.desktop
文件。
要了解在启动其中一个应用程序时将运行的相应终端命令,请打开文件浏览器Nautilus,然后右键单击应用程序的图标以在上下文菜单中选择“属性”。这将为您提供所需的所有详细信息(此处显示将运行gnome-control-center -overview
的系统设置)
次佳解决方法
如果通过存储库或dpkg安装了应用程序,则可以使用以下命令:
1 | dpkg -l | grep "<application name>" |
这将搜索所有已安装的应用程序,并搜索其描述;搜索描述是这里的重要部分,因为描述通常包含应用程序的名称,即使运行它的”command”不包含应用程序名称。
例:
在GNOME中,有一个名为Disk Usage Analyzer的应用程序。但是,从终端运行它的命令不是disk-usage-analyzer。要查找其命令,您可以运行:
1 | dpkg -l | grep "disk usage" |
输出应包含此条目:
1 | alaa@aa-lu:~$ dpkg -l | grep "disk usage" |
看第二栏。实际运行磁盘使用率分析器的命令是baobab
。
参考目录
clash API 使用
教程
首先,阅读Clash的API的文档RESTful - Proxies
GET
获取所有代理
1 | /proxies |
PUT
切换 Selector 中选中的代理
1 | /proxies/<name> |
(这边的name
可以为节点名称,也可以为Selector。只要在proxies/
后直接加上字符串就可以,不需要引号或者:
)
当前接口只支持切换 Selector 中的代理
Path Parameters | Body Parameters |
---|---|
name |
name |
string |
string |
代理Selector名称(大小写敏感) | 要切换的代理名字 |
接着,curl
使用指南 curl 的用法指南
最后,使用curl
切换节点, 使用/proxies
获取所有可用代理,然后curl
发送PUT请求解决战斗。
Example
1 | curl -X PUT -H "Content-Type: application/json" -d '{"name":"节点名"}' http://localhost:port/proxies/<name> |
-H
添加 HTTP 请求的标头Content-Type: application/json
,根据链接2,不设置标头为application/json
可能会有问题。
-d
参数用于发送 POST 请求的数据体。
最后的网址为clash的external-controller
的网址端口,最后Selector为要选择的proxy-groups
的名称。
实际指令类似下面这条:
获取命令
1
curl -X GET -H "Content-Type: application/json" http://127.0.0.1:9090/proxies -o get.json
更改节点
1
curl -X PUT -H "Content-Type: application/json" -d '{"name":"香港-08"}' http://127.0.0.1:9090/proxies/国外流量 -o put.json
所有命令
1 | curl -X PUT -H "Content-Type: application/json" -d '{"name":"香港-08"}' http://127.0.0.1:9090/proxies/国外流量 -o put.json |
引用
cron创建定时任务
cron是创建定时任务的方法,定时任务称为cronjob,它是在crontab文件中创建的。这是Linux系统用户创建定时任务最简单,也是最古老的方法。

编写定时任务
要创建定时任务,可以使用 -e
选项编辑crontab:
1 | $ crontab -e |
这将使用默认的文本编辑器打开crontab。要设置默认的文本编辑器,可以使用编辑器环境变量:
1 | $ EDITOR=nano crontab -e |
cron 语法
要创建一个定时任务,可以编写一个cron表达式,后跟定时任务需要执行的命令。cron表达式在命令运行时进行调度:
- 分钟(0 - 59)
- 小时(0 - 23)
- 日(1 - 31)
- 月(1 - 12)
- 星期(0 - 6,0是星期天)
字段中的星号(*
)转换为“每个”。例如,此表达式在每个月的每一天每小时的第0分钟运行备份脚本:
1 | 0 * * * * /opt/backup.sh |
下面这个例子在每个星期天上午的3:30执行:
1 | 30 3 * * 0 /opt/backup.sh |
简化语法
最新版本的cron已经实现了简单的语法,而不是cron表达式:
参数 | 说明 |
---|---|
@hourly |
在每天的每个小时的第0分钟运行; |
@weekly |
在每周的星期天的第0个小时第0分钟运行; |
@monthly |
在每个月的第一天的第0小时第0分钟运行; |
例如,下面语句在每天的午夜零点执行备份任务:
1 | /opt/backup.sh @daily |
怎样结束一个定时任务
一旦你开始了一个定时任务,它就被设计成永远按计划运行。要在启动定时任务后停止它,必须编辑crontab,删除触发作业的行,然后保存。
1 | $ EDITOR=nano crontab -e |
要停止正在运行的作业,请使用标准Linux进程命令停止正在运行的进程。
它是自动化的
一旦你编写完crontab后,保存并退出编辑器,你的定时任务就已经安排好了,cron将会完成剩余所有的工作。
终止进程
当你在使用 Linux 系统的时候,突然有一个进程导致计算机的运行速度变慢,而这个进程又是一个不怎么重要的进程,所以你想要停止这个进程。
如果命令或者进程正在前台运行,则可以使用 Ctrl+C
终端快捷方式。但是,如果进程不可见(在后台运行),则可以使用专用命令终止它。
术语“终止进程”是指在执行过程中停止进程。如果知道进程ID(PID),可以使用如下命令:
1 | kill <signal> <PID> |
在上面的语法中,signal
指的是要发送用于终止的 kill 信号,PID
指的是进程的ID。
还有一个 killall
命令,在本文中也会涉及。
在开始终止进程之前,你需要了解一些事情。例如,有哪些终止信号可用,如何找到PID等。
我们先来了解一下信号 (signals
) 。
Linux中的终止信号
当一个进程被操作系统或用户终止时,换句话说也就是当进程不是自己完成而结束时,它将被发送一个终端信号。
以下是可用的终止信号:
**信号 ** | **数字值 ** | **描述 ** |
---|---|---|
SIGHUP |
1 | 信号挂起:当控制它的终端关闭时,发送给进程。 |
SIGINT |
2 | 信号中断:当用户终止进程时发送给进程的信号。(例如Ctrl+X ) |
SIGKILL |
9 | 信号终止:立即退出进程的信号,不允许它保存当前状态。 |
SIGTERM |
15 | 信号终止:发送到信号以请求终止进程。进程可以忽略此信号。但这是终止进程的首选方式,因为它可以在进程接收到SIGTERM时释放资源。 |
SIGSTOP |
19 (for x86, ARM and most others) 17 (for Alpha) 23 (for MIPS) 24 (for PARISC) |
信号停止:用于停止进程的信号,但稍后将恢复。 |
信号 9 和 15最常用,这里有一篇文章可了解 SIGKILL
和 SIGTERM
的区别。
获取进程的PID
你还需要知道要终止的流程的详细信息。使用 kill
命令时,必须提供进程的ID(PID)。你可以从进程名称中获取PID:
1 | pidof exact_process_name |
在Linux命令行中终止进程
我们先来了解 kill
命令,因为它比 killall
更常用。
使用 kill 命令
kill
命令要求你知道要终止的进程的ID以及终止信号(可选)。终止命令的语法很简单,上文也提到过:
1 | kill [signal] <PID> |
终止信号是可选的,如果没有提供信号,kill
默认发送SIGTERM
(15)。
我启动了sleep
命令的后台进程(它给了我一个PID),我们尝试使用 kill
命令终止它。如下所示:
1 | $ sleep 120 & |
如果要使用终止信号,可以使用数值或信号本身:
数值
1
2
3
4
5$ sleep 120 &
[1] 125759
$ kill -9 125759
[1] + killed sleep 120信号
1
2
3
4
5$ sleep 120 &
[1] 125746
$ kill -SIGKILL 125746
[1] + killed sleep 120
注:如果kill
命令未指定信号,则默认情况下使用SIGTERM
(15)。
使用 killall 命令
如果您不知道一个进程的PID是什么,或者要终止的进程有多个子进程,并且您希望同时终止子进程和父进程,那么可以使用killall
命令。
1 | killall [signal] <process-name> |
与kill
命令类似,指定终止信号是可选的。当未指定终止信号时,killall
将发送SIGTERM
(15)以正常关闭所述进程。
为了演示 killall
的使用,我们将终止两个正在运行的sleep
命令。
1 | $ sleep 120 & |
此外,你可以使用 -e
标志来查找与进程名称匹配的PID。
使用 pkill 命令
另一个 kill 命令是 pkill
命令。它是 pgrep
和 kill
命令的“组合”。
killall
可以终止所有与进程名称匹配的进程。
pkill
使用模式匹配来匹配进程并终止它们。如下语法:
1 | $ pkill [options] pattern |
pkill
命令提供以下几个选项:
选项 | 说明 |
---|---|
-u |
特定用户所拥有的进程 |
-x |
完全匹配模式的进程 |
-signal |
指定终止信号(默认为SIGTERM) |
假设我的计算机上有一个当前不应该运行任何进程的来宾用户,我想终止它们。该怎么做呢?
1 | $ ps aux | grep sleep |
请注意,我使用 sudo
是因为我处理的进程不属于我当前的用户。
kill 和 killall 命令的区别
kill
命令应对的是进程ID(PID
),它会根据你提供的 PID
来终止进程。而 killall
针对的是进程名称,它会终止给定进程名称的所有进程。例如,有三个 mysql
实例正在运行,killall
会终止所有的这三个进程:使用 killall mysql
命令;而如果是用 kill
的话,你需要提供这三个实例的PID来终止它们:kill PID1 PID2 PID3
。
简单地说,kill
命令与 PID
(通常是单个 PID
)一起使用,killall
命令与进程名称一起使用,并使用进程名称杀死所有进程。
来举个例子。
假如我们启动了三个名为 evince
的程序实例,让其在后台运行:
1 | evince & |
那么现在,我们有了三个名为 evince
的进程,但是每个进程的进程ID是不同的。
要使用 kill
命令强制停止进程,需要提供所需进程的PID
:
1 | kill PID1 |
但是,如果使用 killall 的话,提供进程名称,它将杀掉所有使用该进程名称命名的实例:
1 | killall evince |
如下图所示:

现在我们应该能直观的注意到,kill
命令需要进程ID,killall
命令需要进程名称。

使用哪个命令呢,kill 还是 killall ?
由于 kill
命令可用于单个进程,因此更安全。在使用 killall
命令之前,你需要确保没有其他类似名称的进程(并且你不想停掉的)正在运行着。
命令替换和变量替换
$()和``
在操作上,这两者都是达到相应的效果,但是建议使用$()
,理由如下:
``
很容易与”搞混乱,尤其对初学者来说。- 在多层次的复合替换中,
``
必须要额外的转义字符处理(反斜线),而$( )
比较直观。 - 最后,
$( )
的弊端是,并不是所有的类unix系统都支持这种方式,但反引号是肯定支持的。
eval
简介
eval 属 Shell 内建命令,通过连接参数构造命令。
eval可以读取一连串的参数,然后按照参数特性来执行。参数数目不限,使用空格分隔每个参数,构造的命令应由 Shell 读取和执行。
eval会对后面的命令进行两遍扫描
- 如果第一遍扫描后,命令是个普通命令,则执行此命令;
- 如果命令中含有变量的间接引用,则保证间接引用的语义。
也就是说,eval命令将会首先扫描命令行进行所有的置换,然后再执行该命令。因此,eval命令适用于那些一次扫描无法实现其功能的变量。
eval 执行以下两个步骤:
- step1. 执行变量替换,类似与C语言的宏替代;
- step2. 执行替换后的命令串。
分析替换原则
step1. 定义变量
1 | $ a=10 |
step2. 运行以下命令查看结果
情景1
1
2$ eval "x=$c"; echo $x
b解释
eval "x=$c"
:step1. 将
$c
替换成b
step2. 执行
x=b
情景2
1
2$ eval "x=\$$c"; echo $x
a解释
eval "x=\$$c"
:step1. 第一次扫描, 要执行的命令变为
x=$b
step2. 执行
x=$b
情景3
1
2$ eval "x=\$\$$c"; echo $x
3432188b解释
eval "x=\$\$$c"
:step1. 第一次扫描, 要执行的命令变为
x=$$b
step2. 执行
x=$$b
(其中:$$
为脚本运行的当前进程ID号, 这里为3432188
)
常用示例
回显变量
1
2
3
4
5$ NAME=foo
$ echo $NAME
foo
$ eval echo $NAME
foo先替换变量再执行命令
1
2
3
4
5
6$ cat test.txt
Hello World!
$ command="cat test.txt"; echo $command
cat test.txt
$ eval $command
Hello World!获取传给脚本或函数的最后一个参数。
我们知道 Shell 中使用特殊字符
$#
可以获取传递给脚本或函数的参数个数,使用$n
获取参数(n 为数字),$1
表示第一个参数,$2
表示第二个参数,所以$$#
表示最后一个参数。1
2
3
4
5
6
7
8$ cat test.sh
#!/usr/bin/bash
echo \$$#
eval echo \$$#
$ ./test.sh firstarg lastarg
$2
lastarg使用动态创建的变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
total=0
label="Looping complete. Total:"
for n in {1..10}
do
eval x$n=$n
echo "Loop" $x$n
((total+=$x$n))
done
echo $x1 $x2 $x3 $x4 $x5 $x6 $x7 $x8 $x9 $x10
echo $label $total它创建了一个名为
total
的变量,其中包含我们创建的变量值的总和。然后它创建一个名为label
的字符串变量。这是一个简单的文本字符串。我们将循环 10 次并创建 10 个名为
x1
到x10
的变量。循环主体中的eval
语句提供“x”并采用循环计数器$n
的值来创建变量名称。同时,它将新变量设置为循环计数器$n
的值。它将新变量打印到终端窗口,然后使用新变量的值递增
total
变量。在循环之外,10 个新变量再次打印,全部打印在一行上。请注意,我们也可以通过它们的真实名称来引用变量,而无需使用它们名称的计算或派生版本。
最后,我们打印
total
变量的值。
参考: