大家好,我是半虹,这篇文章来讲 Linux 系统怎么在后台运行命令
0、序言
很多时候,特别是在连接服务器进行开发时,通常会遇到以下问题:
- 运行一些命令时,终端被阻塞,无法执行其他操作
- 运行一些命令时,终端被关闭,命令执行也被终止
这些时候,就需要后台运行命令了
什么是后台运行?
一般来说,我们直接在终端输入命令并执行,这种操作我们称之为前台运行
前台运行的命令产生的信息会显示在终端上,并且运行时会占据着当前终端
与之相反,如果命令的运行不会占据着终端,那么我们就能称之为后台运行
后台运行的命令用户是无法感知的,同时也能解决本文开头提出的两个问题
对于很多场景都非常重要,下面介绍一些后台运行命令的常用手段
1、&
后台运行命令最简单的方式就是在命令的最后加上 &
符号
这样系统就会自动把命令放到后台执行,格式如下:
command &
但要注意,这种方法仍然会将程序输出显示在终端
举个例子,假设有个 python
脚本 run.py
,每隔一秒打印一个数字,到第十个数字抛出一个错误
import time
for i in range(1, 10 + 1):
if 1 <= i <= 9:
print(i)
time.sleep(1)
else:
raise Exception('Error')
然后,在终端通过 &
的方式将命令放到后台去执行
python run.py &
# 程序输出如下:
# 1
# 2
# 3
# 4
# 5
# 6
# 7
# 8
# 9
# Traceback (most recent call last):
# File "run.py", line 8, in <module>
# raise Exception('Error')
# Exception: Error
可以看到,上述程序的正常输出和错误输出都会显示在终端上面
虽然此时的终端是可交互的,但是这些输出还是会影响正常使用
因此通过这种方式后台运行,通常都会同时做一个重定向的操作,将命令的输出重定向到文件
在 Linux 中怎么做重定向呢?这里想花一小段篇幅系统讲解一下
在 Linux 中,存在三个特殊的文件描述符,分别是:
- 标准输入 (
stdin
): 通常是键盘输入,对应文件描述符为0
- 标准输出 (
stdout
): 通常是终端屏幕,对应文件描述符为1
- 标准错误 (
stderr
): 通常是终端屏幕,对应文件描述符为2
上面例子中的正常输出和错误输出,默认是连接到标准输出和标准错误
因此都会默认显示在终端屏幕上面
为此我们可以通过重定向操作符来对命令的输出进行重定向
常用的重定向操作符如下:
>
:符号表示将左参数以覆盖写的方式重定向到右参数>>
:符号表示将左参数以追加写的方式重定向到右参数
这样说或许很难理解,我们来举个例子:
1>xxx
:表示将标准输出重定向到xxx
位置(1
是标准输出对应的文件描述符)2>xxx
:表示将标准错误重定向到xxx
位置(2
是标准错误对应的文件描述符)
例如:
python run.py 1>out.log 2>err.log &
通过这种方式运行,可以发现程序的输出不再显示到屏幕上面
而是将正常输出打印到 out.log
文件,将错误输出打印到 err.log
文件
如果你希望将正常输出和错误输出都打印到一个文件,可以使用下面的命令:
python run.py 1>out.log 2>&1 &
其中,2>&1
表示将标准错误重定向到标准输出,注意右参数要加上 &
才能表示对应的描述符
由于标准输出已经被重定向到 out.log
,所以标准错误也随之重定向到 out.log
最后介绍一些常用的重定向符的简写:
>
:相当于1>
&>
:表示将标准输出和标准错误同时重定向到右参数
例如下面两个写法和上述命令的意思一致
# 上述命令
python run.py 1>out.log 2>&1 &
# 写法一,使用简写 >
python run.py >out.log 2>&1 &
# 写法二:使用简写 &>
python run.py &>out.log &
关于重定向的问题就介绍到这里,最后总结一下使用 &
实现后台运行的一般性写法:
command &>out.log &
这种写法可以解决本文开头提出的第一个问题,即:命令执行时终端被阻塞
但是要注意,这种写法仍然存在着第二个问题,即:终端关闭时命令也终止
第二个问题要怎么解决,我们会在本文的后两节进行介绍
在此之前,我想先补完这小节的最后一个内容,即:正在前台运行的命令要怎么放到后台
这里就不得不引出信号的概念
我们知道,当同时按下 ctrl + c
的时候,就能终止正在前台运行的命令
这本质上就是发送一个中断信号给该命令,当该命令收到信号时,就终止执行
与此类似,系统里有个挂起信号,该信号能暂停前台命令执行并将其放到后台
挂起信号可通过 ctrl + z
触发
虽说这样能将前台命令放到后台,但是该命令的执行也会被暂停
后续可以通过以下三个命令操作:
bg <jobid>
:将指定后台任务直接后台执行,相当于直接使用&
fg <jobid>
:将指定后台任务放到前台执行kill <pid>
:将指定后台任务杀死不再执行
这个 <jobid>
实际上就是后台任务标识,<pid>
则是进程标识
二者都可以通过 jobs
命令来看到,下面是例子
# 前台运行
python run.py
# 然后按下 ctrl + z 将其暂停并放入后台
# 此时通过 jobs -l 可以看到该脚本状态,包括任务标识、进程标识、命令调用等的参数
jobs -l
# [1]+ 830380 Stopped python run.py
# 这时存在三种选择
# 第一,使用 fg 1 将该任务放到前台运行
# 第二,使用 bg 1 将该任务继续后台运行
# 第三,使用 kill -9 830076 杀死进程
2、nohup
上面说到,运行命令时带上 &
,虽然不会阻塞终端,但在关闭终端时,命令还是会被终止
这是因为,后台运行的命令的父进程还是对应终端,如果终端退出也就意味着父进程退出
而当父进程退出时,会给每个子进程发送 hangup
信号,子进程收到该信号后也随之终止
因此,想在关闭终端后还能继续执行命令,一个直观的方案就是让命令忽略 hangup
信号
这点可以通过在命令前加上 nohup
实现,格式如下:
nohup command &
这里需要注意,nohup
只是让命令忽略 hangup
信号,但是仍然会阻塞终端
因此还要搭配 &
让命令后台执行,这样才能同时解决开头所提出的两个问题
另外,执行完上述命令后,最好手动使用 exit
命令退出终端
避免终端因其他原因退出,发送其他信号导致子进程意外终止
最后,不知道大家是否注意到,上面给出的命令没有用重定向
这是因为 nohup
命令默认会将标准输出和标准错误都重定向到 nohup.out
文件
当然,也可以手动指定重定向来覆盖默认的设置
至此,本文开头提出的两个问题已经被完美解决
现在来补上最后一块拼图:使用 nuhup
加 &
来运行后台命令,退出终端后怎么查看命令
我们知道, jobs
只能查看当前终端执行的任务
想要看到所有进程,这时候就要用到 ps
命令了
但是 ps
参数很多,这里不逐一介绍,只给出两个常用的组合,二者都可以显示进程状态
# 参数组合一
ps -ef
# 参数组合二
ps -aux
3、tmux
上述通过 nohup
和 &
可以让命令在后台运行,不受终端断连或关闭的影响
但是这种方法使用起来还是有一定的心智负担,比如说需要记住使用后手动 exit
终端等等
这种方法适合作为临时使用,对于有多个终端的场景,其实会有更好的方案
那就是使用上终端复用工具,这里个人比较推荐 tmux
原本的链路是本地主机通过终端登录到远程主机,然后在该终端中执行任务
该任务作为终端子进程存在,因此当终端断连时,其中子进程也会随之终止
而现在本地通过终端登录到远程后,可以先创建一个 tmux
会话,然后在该会话中执行任务
该会话的功能与原有终端完全一致,唯一的不同在于 tmux
会话由远程 tmux server
管理
当终端断连时, tmux
会话仍在 tmux server
之上运行 ,其中执行的任务自然也不会终止
且后续本地如果再次登录到远程时,也能够连接原有 tmux
会话,恢复到上次退出时的状态
虽说 tmux
的运行原理有一点复杂,但使用起来还是非常简单的,下面一起来看看
如果你还没安装 tmux
,可以通过以下命令安装:
apt install tmux
tmux
提供会话管理、窗口管理、窗格管理等的众多功能,由于篇幅限制,这里不逐一介绍
下面只介绍会话管理这个最基础的功能,其他的功能大家感兴趣可以后面再去了解
一般来说,会话管理已经能够满足日常的使用了,相关的命令以及快捷键如下所示:
命令 | 快捷键 | 作用 |
---|---|---|
tmux ls | 同时按下 ctrl + b ,松开后按 s | 列出所有会话 |
tmux new -s <name> | 创建新的会话 | |
tmux attach -t <name> | 连接已有会话 | |
tmux detach | 同时按下 ctrl + b ,松开后按 d | 分离当前会话 |
tmux kill-session -t <name> | 删除已有会话 | |
tmux rename-session -t <old-name> <new-name> | 重命名旧会话 | |
tmux switch -t <name> | 切换到新会话 |
上面这么多命令可能看着有点乱,总结下使用 tmux
的最简要步骤:
- 本地主机连接远程主机,创建并进入新会话
tmux new -s <name>
- 在新会话执行所需命令,之后可以分离会话
tmux detach
- 后面等到有需要的时候,可以再次连接会话
tmux attach -t <name>
最后提一个点,很多人说用 tmux
的时候看不到历史输出,其实是可以的
只需同时按下 ctrl + b
,然后松开后按 [
,这样就能通过方向键翻页了,后面可以按 q
退出
好啦,本文到此结束,感谢您的阅读!
如果你觉得这篇文章有需要修改完善的地方,欢迎在评论区留下你宝贵的意见或者建议
如果你觉得这篇文章还不错的话,欢迎点赞、收藏、关注,你的支持是对我最大的鼓励 (/ω\)