在Bash下操作,不可避免的需要让程序运行在后台而不妨碍前台的操作。在Linux有个概念称之为Job Control,由于Bash下的运行的任何程序都是当前Bash下的子程序,而job control也即管理当前Bash下的子程序。所以,Job Control的概念是不能跨Bash管理,比如我们不能在tty1中管理tty2中的程序。
其中我们在Bash中最常用的应该就是&符了,这个符号跟在要运行的程序的后面,像下面这样:
ping blog.useasp.net &
这样配置后,我们将会发现,运行后,系统返回的是类似下面这样的:
[1] 23911
这样我们就将这个ping
放到了当前Bash的后台进行运行,我们此时仍可以正常的在Bash下进行其他的操作。但是你也许会发现这种方式运行的程序虽然不影响你当前操作,但是运行的输出却总是和我们抢占Bash的屏幕,非常的郁闷!但当我们想Ctrl+C来终止的时候,却又无能为力——因为在Bash下你Ctrl+C
根本无法终止这个后台运行的程序!
为了防止这种情况,我们需要用到重定向(Redirection),如果你不太清楚,可以本博客之前的博文《Linux下高效编写Shell-Shell特殊字符汇总》篇里面的重定向,代码变化不大,类似下面这样:
## 重定向输出到/dev/null这个特殊文件
## 也就是没有输出内容了
ping blog.useasp.net &>/dev/null &
当然,我们一般更希望后台运行的程序的输出能够保存到指定文件,而不是直接丢弃,所以更一般形式是:
ping blog.useasp.net >>/tmp/background_programs.log 2>&1 &
## 想想这是什么意思?
但我们在这里是要Bash界面能够不被后台的程序搞砸。——毫无疑问,我们前一种已经达到了我们要的效果。
恩,迄今为止我们已经能够将程序放到后台运行了,但是,对于已经占据我们Bash的程序,我们却无能为力,为了不让它们继续霸占我们的Bash屏幕,我们迄今能做的只有Ctrl +C
,让它见鬼去吧!这样我们跑了很久的程序也就终结了,不过,运行的数据可能也没有了。也许更温柔一点是个不错的选择——Ctrl+Z
!
Ctrl+Z
是让程序暂停,在已经运行的程序上直接按下Ctrl+Z
,他们就会非常自觉的将自己挂起,并放入到后台中,此时的程序是处于停止状态的,如果要让他在后台运行,就还要用到后面将学到的命令。但此时,我们的Bash屏幕能恢复干净清爽才最主要。
好了,在这难得平静的Bash界面上,让我们来学习下job control的一个命令吧,也许我们后台已经有一堆程序在跑呢,我们怎么也得看看才放心,jobs命令就是让我们干这个事情的:
jobs [-lnprs] [jobspec ...] or jobs -x command [args]
参数:
-l 列出后台程序的job number,PID, 状态和命令串
-n 显示自从上次用户知晓状态后程序改变过状态的程序
-r 仅列出正在后台运行(RUNNING)的程序
-s 仅列出后台中处于停止(STOPPED)的程序
更详细的内容可以参看man,这里基本够我们用了。
用用看,你也许会发现你的后台有很多程序在运行哦,同时你也会看到一些不同的地方,比如:
jobs -l
[1]- 10314 Running ping blog.useasp.net &>/dev/null &
[2]+ 10833 Stopped emacs (wd:~)
中括号后面的竟然有+和-号,这是什么?这个是Bash根据我们放入后台的顺序的一个标记,+号表示最近被放到后台的程序,-号次之,如果有三个以上,我们就会发现第三个以上是没有+/-符号的。而下面我们将要看到的fg
在取出后台程序的时候,+号在默认情况下就是那个被切换到前台的程序了。
既然上面我们说了fg,那就看看它是干什么的。fg
应该是foreground的简写,即前台,它的作用是将在后台的程序切换到前台来运行。在切换的时候,fg
可以指定参数——Job Number!
## 使用上面的jobs -l中的例子
fg ## 这个切换到前台运行的程序将会是emacs——Why?
## 或者我们使用下面这种形式
fg %1 ## 切换到前台的将会是ping blog.useasp.net &>/dev/null &
## 此时你可以使用Ctrl+Z将其放回到后台,jobs查看之后你会发现,ping的最后没有了&了。
## 好神奇——想想为什么?
fg - ## 这个是将-号那个程序切换到前台运行。
## 上述的例子中就是job number为1的程序 ping blog.useasp.net &>/dev/null &
有foreground,自然应该有background了,Yes,它的简写就是bg!相对应的,bg
就是将程序切换到后台运行,注意:不仅切换到后台,更重要的是要在后台运行。Ctrl+Z
只是把程序放入后台,但状态是停止。前面我们说过,我们可以将Ctrl+Z
放入后台,并将已经停止的程序在后台运行的命令就是它了。来看看是怎么做到的吧:
ping blog.useasp.net &>/dev/null
## 这个将会占用Bash屏幕,因此我们在这里Ctrl+Z将它放入后台
## [2]+ Stopped ping blog.useasp.net &>/dev/null
jobs -l
## 此时我们使用jobs来查看,可以发现ping 这个程序已经是STOPPED了
## [1]- Stopped emacs (wd: ~)
## [2]+ Stopped ping blog.useasp.net &>/dev/null
## 让我们想办法让ping在后台继续运行
bg %2
## 此时ping将被放到后台运行
## [2]+ ping blog.useasp.net &>/dev/null &
jobs -l
## 查看下当前的状态:
## [1]+ Stopped emacs (wd: ~)
## [2]- Running ping blog.useasp.net &>/dev/null &
## 已经在后台运行了。
到此为止,你基本上可以在Linux下熟练的管理在Bash上程序了。当然,这些只是对程序的运行态进行管理,对程序本身并没有影响,那在实际操作中我们有一类操作是会影响程序本身的,比如:重启程序,关闭程序。而这类操作我们要使用的是另外一个命令:kill
。
kill命令能够通过信号来控制运行中的程序:
kill -SIGNAL %jobNumber
## -SIGNAL可以通过kill的-l(小写的L)参数列出本机能够支持的信号(SIGNAL)列表
## 常用的SIGNAL有:
## 信号值 信号名 描述
## -2 SIGINT 终端产生的中断,Ctrl+C
## -9 SIGKILL 强制关闭程序(KILL),直接杀死
## -15 SIGTERM 退出程序运行(TERMINATION)
## 这个和强制关闭不同,这个是程序正常退出的流程
## 如果不带SIGNAL,则默认是-15,以正常的方式退出程序
## 当然,我们更常用的可能是kill的另外一个用法
kill -SIGNAL PID
## 通过PID来kill程序
## 如果要一次性移除多个程序,也许你会想用:
killall ping
有了这些命令,我们就能将尽可能剥削系统的资源,而不至于让我们累个半死,系统闲的要死了。不会剥削系统资源的操作者不是好管理员!
有了这些命令的时候,你也许很兴奋,终于,我们可以在我们不在系统上的时候,让系统能够为我们做事了,于是,愉快在bash上写上了无数以&结尾的命令,然后,妥妥的关闭了我们的远程连接——恭喜你,你被耍了!系统也是会偷懒的,谁叫你不好好监工呢,如果你不想自己监工,那么请一个吧:nohup
nohup的作用就是让我们的程序在我们退出远程登录(登出系统)的时候,能够不受到影响而继续执行,不过要注意,nohup
并不直接支持Bash内建的命令,请使用Shell脚本将命令放在文件中,而后使用nohup
执行你的Shell脚本,这才能稳稳的保证系统不偷懒!
nohup Command [parameters] &
## 将程序用nohup在后台运行
## Command是我们的Shell脚本或是非bash内建命令
## parameters非必须,是传递给Command的参数
## &放到后台运行
## 使用nohup运行的命令,会自动的将所有的输出重定向到当前用户的根目录下面的nohup.out文件
当然,除了nohup
,你也可以使用其他的命令来达到同样的目的,比如下面这些都可以
at
:不过at需要先启用,这个就是Windows下的计划任务嘛。具体的这里不展开了,又可以写上很多,有兴趣可以参考at
的文档;
setsid
:setsid command
,该命令使我们的命令的父进程是init,而不是当前Bash,只有关机,该程序才会退出;
(command &)
:这个和上面的类似,也是将父进程设置为了init;
disown
:我们使用&来让程序在后台运行,之后我们可以根据该程序的Job Number,使用disown -h %job_number
来将程序不受HANGUP信号影响;
举栗子总是更容易让人明白:
setsid ping blog.useasp.net &>/dev/null
## 让ping运行在一个新的会话当中,当然,会话的用户还是当前用户
(ping blog.useasp.net &>/dev/null &)
## 同上
ping blog.useasp.net &>/dev/null &
## 返回:
## [1] 24074
disown -h %1
## 脱离当前用户的信号控制
## disown后查看父进程,还是能看到ping是挂在当前bash
## 但重登录之后,就可以发现ping的父进程是init
如果你要查看进程关系,可以试试pstree
。
OK,打完收工