一、脚本执行过程中的控制

之前的内容中,运行编写好的脚本时都是在命令行上直接确定运行的,并且运行的脚本是实时的,这并不是脚本唯一的运行方式,下面的内容是脚本的其他运行方式。例如在Linux系统中如何控制脚本的执行过程,想在脚本运行过程中对运行中的脚本执行流程进行控制,或者控制脚本的运行时机等等,这些都是通过信号来实现的。

15.1Linux信号

在Linux系统中,Linux是通过信号和运行在系统上的进程实现通信的。信号就是一个很短的信息,可以发送给一个或多个进程。在前面的内容中讲了如何通过信号启动、停止或终止一个进程的运行。当然可以通过这些信号来控制Bash脚本的运行,如终止一个进程或将运行的脚本挂起(暂停执行)。

15.1.1常用的信号

在Linux中,系统和Shell进程中进行通信的信号有几十个,可以通过下面的命令在控制台显示这些信号的值和对应的信号名称。kill -l

trap -l

控制台显示:$  trap -l

1) SIGHUP        2) SIGINT        3) SIGQUIT     4) SIGILL           5) SIGTRAP

6) SIGABRT      7) SIGBUS        8) SIGFPE         9) SIGKILL     10) SIGUSR1

11)  SIGSEGV    12) SIGUSR2     13) SIGPIPE       14) SIGALRM   15) SIGTERM

16)  SIGSTKFLT 17) SIGCHLD   18) SIGCONT   19) SIGSTOP    20) SIGTSTP

21)  SIGTTIN      22) SIGTTOU    23) SIGURG      24)  SIGXCPU    25) SIGXFSZ

26)  SIGVTALRM        27) SIGPROF     28) SIGWINCH 29) SIGIO 30) SIGPWR

31)  SIGSYS       34) SIGRTMIN  35) SIGRTMIN+1       36) SIGRTMIN+2       37)  SIGRTMIN+3

38) SIGRTMIN+4  39)  SIGRTMIN+5  40) SIGRTMIN+6     41) SIGRTMIN+7  42) SIGRTMIN+8

43) SIGRTMIN+9  44)  SIGRTMIN+10  45) SIGRTMIN+11 46) SIGRTMIN+12  47) SIGRTMIN+13

48) SIGRTMIN+14  49)  SIGRTMIN+15  50) SIGRTMAX-14  51) SIGRTMAX-13 52) SIGRTMAX-12

53) SIGRTMAX-11  54)  SIGRTMAX-10  55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7

58) SIGRTMAX-6  59)  SIGRTMAX-5  60) SIGRTMAX-4    61) SIGRTMAX-3  62) SIGRTMAX-2

63)  SIGRTMAX-1  64) SIGRTMAX

但是常用到的系统信号如下表中列出的。

Linux系统中常用到的信号以及对应的信号名称和描述信息:编号信号名称描述默认状态

1SIGHUP终止终端或进程进程终止

2SIGINT来自键盘的中断,【Ctrl+C】进程终止

3SIGQUIT来自键盘的退出,【Ctrl+\】进程流产

9SIGKILL强迫进程终止,不可以屏蔽进程终止

15SIGTERM使进程终止进程终止

17SIGCHLD使子进程停止或终止忽略

18SIGCONT进程继续运行,与SIGSTOP结合使用忽略

19SIGSTOP进程暂停执行进程暂停

20SIGTSTP来自键盘的进程挂起,【Ctrl+Z】进程暂停

在Linux系统中使用信号的目的主要有两个:

1.让运行的进程感知特定的事件并作出反应。

2.在脚本进程执行代码过程中处理相关信号的代码块。

在Linux系统中,信号的传递过程中主要分为两个阶段,第一个阶段是信号的发送阶段,第二个阶段是进程对信号的接收阶段。在第一个阶段中系统修改目标进程的状态描述符表示一个新的信号已经发送。第二个阶段系统强制进程做出反应并改变进程的运行状态,或者执行一段特定的代码,这段代码是脚本中捕获信号后执行的特定代码。

另外需要注意的是信号只被当前正在运行的进程接收,并且进程可以屏蔽特定的信号,相同的信号除第一个被进程接收后其他的会被屏蔽。下面会通过例子详细说明。

通常情况下,运行一个脚本会启动一个新的Shell进程,在这个Shell进程中运行脚本,当脚本执行结束后对应的Shell进程也会终止。特殊情况下,如果使用source或点命令启动脚本不会启动一个新的Shell进程,而是在当前的Shell进程中运行脚本,这就会使结果有些不同,下面会通过例子说明。当Shell进程接收到挂起或终止信号时,Shell进程会终止运行,但Shell进程终止前会将接收到的信号发送到本Shell进程中运行的所有的子进程,包括运行的脚本进程或某个命令进程。

15.1.2SIGINT和SIGTSTP

挂起一个进程和终止一个进程是不同的,当一个进程被挂起时,进程的运行状态会保存到内存,只是系统将不会在分配CPU的时间片给挂起的进程,当通过命令启动被挂起的进程时,进程会从断点开始继续执行。而终止一个进程是将进程的运行状态从内存中清除并且不能在继续执行。

在Linux系统中,使用组合键可以生成两个基本的Linux信号,它们是【Ctrl+C】终止进程信号和【Ctrl+Z】挂起一个进程。使用这两个组合键可以将运行的进程终止或挂起,下面通过简单的例子说明。

使用【Ctrl+C】终止一个命令行上的命令进程,sleep命令可以程序运行过程中暂停运行指定的秒数。使用这个命令会更加直观,其他命令很快就会执行结束。$ sleep 60

^C

$

当sleep命令执行开始,会等待60秒后才会显示命令行提示符$,当采用组合键【Ctrl+C】可以终止这个进程,注意:终止的是当前运行的进程,可以是一个或多个进程。

通过ps命令查看sleep命令执行时和终止sleep命令进程的进程信息。$ ps au

USER       PID %CPU %MEM    VSZ    RSS TTY      STAT START   TIME COMMAND

yarn      4991  0.0   0.0   6876  1680 pts/0    Ss    20:20   0:00 bash

yarn      5084  0.0   0.0   6876  1672 pts/1    Ss    20:37   0:00 bash

yarn      5099   0.0  0.0   5680    488 pts/0    S+   20:38    0:00 sleep 60

yarn      5109  2.0   0.0   6548  1060 pts/1    R+    20:38   0:00 ps -au

$ ps au

USER       PID %CPU %MEM    VSZ    RSS TTY      STAT START   TIME COMMAND

yarn      4991   0.0  0.0   6876   1680 pts/0    Ss+  20:20    0:00 bash

yarn      5084  0.0   0.0   6876  1672 pts/1    Ss    20:37   0:00 bash

yarn       5129  0.0  0.0    6556  1048 pts/1    R+    20:42   0:00 ps -au

可以看到sleep命令没有执行结束时的进程号是5099,执行终止命令后进程被清除。

采用【Ctrl+Z】组合键可以挂起一个进程,下面还用sleep命令演示。$ sleep 100

^Z

[1]+  Stopped                 sleep 100

$

通过组合键挂起了命令行进程,通过ps命令查看进程状态。$ ps au

USER       PID %CPU %MEM    VSZ    RSS TTY      STAT START   TIME COMMAND

yarn      5084  0.0   0.0   6876  1680 pts/1    Ss    20:37   0:00 bash

yarn      5192  0.0   0.0   6956  1808 pts/3    Ss    20:50   0:00 bash

yarn      5304   0.0  0.0   5680    492 pts/3    S+   21:05    0:00 sleep 100

yarn      5306  1.0   0.0   6548  1048 pts/1    R+    21:05   0:00 ps -au

$ ps au

USER       PID %CPU %MEM    VSZ    RSS TTY      STAT START   TIME COMMAND

yarn      5084  0.0   0.0   6876  1680 pts/1    Ss    20:37   0:00 bash

yarn      5192  0.0   0.0   6956  1808 pts/3    Ss+   20:50   0:00 bash

yarn      5304   0.0  0.0   5680    492 pts/3    T    21:05    0:00 sleep 100

yarn       5308  0.0  0.0    6552  1052 pts/1    R+    21:05   0:00 ps -au

可以看到sleep命令进程的STAT字段由S+变为了T,从运行状态转变为挂起状态。挂起的进程状态保存到了内存可以通过命令jobs查看当前挂起的进程。$ sleep 100

^Z

[1]+  Stopped                 sleep 100

$ sleep 200

^Z

[2]+  Stopped                 sleep 200

$ jobs

[1]-  Stopped                 sleep 100

[2]+   Stopped                 sleep  200

对于挂起的进程,可以使用fg命令恢复进程的运行,如下所示。$ sleep 100

^Z

[1]+  Stopped                 sleep 100

$ fg

sleep 100

$

当通过exit命令退出Shell进程时,如果Shell进程中存在被挂起的进程,Shell进程会给出提示信息,如果再次执行exit命令Shell进程将会终止并退出,暂停运行的进程也会被终止。$ sleep 100

^Z

[1]+  Stopped                 sleep 100

$ exit

exit

There are  stopped jobs.

如果想终止一个暂停的进程,可以使用kill命令向暂停的进程发送SIGKILL信号将进程终止。

15.2在脚本中捕捉信号

默认情况下,脚本会忽略这些Linux系统信号,发送的信号会被Shell进程接收并处理,例如,当终止或挂起一个进程(或多个进程)时,接收到信号的Shell进程会被终止或者被挂起,运行在Shell进程中的脚本也同时被终止或被挂起。但是,也可以让脚本不忽略这些信号,在信号出现时拦截并执行相应的代码块,完成特定的任务或者释放资源。需要注意的是如果信号在脚本中被拦截,Shell进程将不再接收并处理这些信号,而是在拦截信号的脚本中被处理。在脚本中捕捉信号是非常有意义,当脚本在运行过程中遇到不可预知的异常情况下,可以采用这种方式做一些系统的善后工作,如断电等异常情况下造成的进程终止。在脚本中可以使用trap命令来拦截Linux系统发出的信号,命令格式如下:trap 执行的命令or脚本 拦截的信号列表

在脚本执行过程中,如果出现trap命令列出的信号类型列表中的其中一个信号,这个信号将被脚本拦截,并且暂停脚本的继续执行,进而会执行trap命令指定的命令。当trap命令指定的命令被执行后,脚本会继续接着暂停执行的断点继续向下执行,直到脚本执行结束或再次出现指定的信号,拦截、执行指定的命令、继续执行脚本。

15.2.1trap命令

在脚本执行过程中可以通过trap命令拦截指定的系统信号,并且执行指定的命令。下面通过几个简单的例子说明在脚本中如何通过trap命令拦截信号和拦截信号后脚本的执行流程。第一个例子是当脚本运行过程中不允许通过键盘组合键终止脚本的执行。

例:trapTest01.sh#!/bin/bash

# 使用trap命令拦截键盘终止进程信号SIGINT

# 当拦截到SIGINT信号后,在控制台输出相应的信息

trap "echo ' --Process can not be terminated!'" SIGINT

# 循环

for (( i = 1;i < 10;i++ ))

do

echo "Var i = $i"

# 使进程暂停5秒

sleep 5

done

控制台显示:$ trapTest01.sh

Var i = 1

^C --Process can not be terminated!

Var i = 2

Var i = 3

^C --Process can not be terminated!

Var i = 4

Var i = 5

Var i = 6

Var i = 7

Var i = 8

^C --Process can not be terminated!

Var i = 9

$

可以看到,当从控制台通过【Ctrl+C】发出终止进程的信号时,被脚本中的trap命令拦截,并在控制台输出信息,当指定的命令执行结束后,脚本继续向下执行并没有被终止。trap命令指定的命令可以是一个脚本,在脚本中执行相关的代码。修改上面的例子,将输出的信息放置在脚本中。

例:trapTest02.sh#!/bin/bash

# 当拦截到控制台终止进程信息时,执行指定的脚本

trap "trapTest02_1.sh" SIGINT

# 循环

for (( i = 1;i < 10;i++ ))

do

echo "Var i = $i"

# 暂停1秒

sleep 1

done

例:trapTest02_1.sh#!/bin/bash

# 可以在脚本中执行一系列相关的代码

echo " --Process can not be terminated!"

控制台显示:$ trapTest02.sh

Var i = 1

Var i = 2

^C --Process can not be terminated!

Var i = 3

Var i = 4

Var i = 5

^C --Process can not be terminated!

Var i = 6

Var i = 7

Var i = 8

^C --Process can not be terminated!

Var i = 9

$

第二种方式可以在脚本中执行相关的代码块,如释放资源等等。需要注意的是每一次拦截到指定的信号后都会执行一次指定的命令或脚本。

15.2.2trap命令捕捉Shell进程退出

启动一个脚本可以有两种方式,第一种是在新的Shell进程中运行脚本,另一种方式是在当前Shell进程中运行脚本。启动脚本的方式不同将会产生不同的结果,如下例子。

例:trapTest03.sh#!/bin/bash

# 当脚本运行结束,对应的Shell进程也同样会结束,当Shell进程终止时会被拦截

# 需要的注意的是,启动脚本的方式不同,结果也会不同

trap "echo '--OK! end of execution!'" EXIT

# 循环

for (( i = 1;i < 5;i++ ))

do

echo "Var i = $i"

sleep 1

done

第一种启动脚本的方式,启动一个新的Shell进程运行脚本,在这种情况下,脚本执行结束时会被拦截,因为脚本执行结束后运行脚本的Shell进程也同时会终止,拦截的是Shell进程的终止。

控制台显示:$ trapTest03.sh

Var i = 1

Var i = 2

Var i = 3

Var i = 4

--OK! end of execution!

$

第二种启动脚本的方式是,在当前Shell进程中运行脚本,这种情况下,当脚本运行结束后,运行脚本的Shell进程并不会终止,所以不会在控制台输出相关的信息。注意:拦截的是Shell进程的终止信号,不是脚本运行结束的信号。

控制台显示:$ . trapTest03.sh

Var i = 1

Var i = 2

Var i = 3

Var i = 4

$

通过source命令或点命令启动脚本时,不会启动一个新的Shell进程,而是在当前的Shell进程中运行脚本,要注意。同样,如果在脚本运行过程中终止脚本的运行,也同样会被拦截。下面通过组合键【Ctrl+C】终止脚本的运行,同样分为两种情况。

控制台显示:$ trapTest03.sh

Var i = 1

Var i = 2

^C--OK! end of execution!

$. trapTest03.sh

Var i = 1

Var i = 2

^C

$

上面通过两种方式运行脚本,第一种方式,当从键盘终止进程时会被拦截,因为脚本是在新的Shell进程中运行,而第二种方式运行脚本时是在当前的Shell进程中,所以当脚本运行结束后当前Shell进程并没有终止,所以没有Shell进程终止信号。

15.2.3移除捕捉信号

在脚本中可以使用trap命令加单破折号来移除指定要捕捉的信号,一旦捕捉信号被移除,相应的信号将不会再被捕捉。命令格式如下。trap - 指定捕捉的信号

通过trap命令可以移除指定要捕捉的信号,当捕捉信号被移除后,脚本将不会在拦截这样的信号,信号将会被Shell进程接收并处理,下面通过例子说明。

例:trapTest04.sh#!/bin/bash

# 拦截脚本结束后对应的Shell进程的结束信号

trap "echo '--OK! end of execution!'" EXIT

for (( i = 1;i < 5;i++ ))

do

echo "Var i = $i"

sleep 1

done

# 移除拦截信号

trap -  EXIT

echo "Signal is removed!"

for (( i = 1;i < 5;i++ ))

do

echo "Var i = $i"

sleep 1

done

控制台显示:$ trapTest04.sh

Var i = 1

Var i = 2

Var i = 3

Var i = 4

^C--OK! end of execution!

$ trapTest04.sh

Var i = 1

Var i = 2

Var i = 3

Var i = 4

Signal is removed!

Var i = 1

Var i = 2

Var i = 3

^C

$

拦截信号没有移除时,终止信号被拦截控制台输出相关信息。当拦截信号移除后,终止信号会终止脚本进程但不会被脚本拦截。

例:trapTest05.sh#!/bin/bash

# 拦截控制台终止进程信号

trap "echo ' --Process can not be terminated!'" SIGINT

for (( i = 1;i < 5;i++ ))

do

echo "Var i = $i"

sleep 1

done

# 移除控制台终止信号

trap -  SIGINT

echo "Signal is removed!"

for (( i = 1;i < 5;i++ ))

do

echo "Var i = $i"

sleep 1

done

控制台显示:$ trapTest05.sh

Var i = 1

Var i = 2

^C --Process can not be terminated!

Var i = 3

Var i = 4

Signal is removed!

Var i = 1

Var i = 2

^C

$

一旦捕捉信号被移除后,脚本将会忽略该信号。但是在移除捕捉信号前,脚本还是会拦截指定的信号。

15.3系统后台运行模式

之前运行脚本时都是在命令行直接运行,这种运行脚本的方式是在系统前台的运行模式。这种运行脚本的方式在某些情况下不是最方便的方法,如有的脚本运行时间很长,在这段时间内,只能等待脚本运行结束而不能进行其他的工作。或者某些服务在系统生命周期内都在运行,这样的服务脚本如果运行在系统前台,用户将无法工作。

通过ps命令可以将系统运行的进程显示到控制台,但不是所有的进程都运行在终端上的,如下所示。$ ps -ef

UID        PID  PPID   C STIME TTY          TIME CMD

root         1     0   0 09:12 ?        00:00:02  /sbin/init

root         2      0  0 09:12 ?        00:00:00 [kthreadd]

root         3     2   0 09:12 ?        00:00:00  [migration/0]

root         4     2   0 09:12 ?        00:00:01  [ksoftirqd/0]

root         5     2   0 09:12 ?        00:00:00  [migration/0]

yarn      2814     1   0 09:18 ?        00:00:32  gnome-terminal

yarn      2816  2814   0 09:18 ?        00:00:00  gnome-pty-helper

yarn      2817  2814   0 09:18 pts/0    00:00:00 bash

yarn      2845  2814   0 09:19 pts/1    00:00:00 bash

postfix   3757  2138   0 10:53 ?        00:00:00 pickup  -l -t fifo -u

yarn      4400  2817   3 12:30 pts/0    00:00:00 ps -ef

不是运行在终端内的进程我们称为后台进程,这些进程运行在后台模式下,通常都是一些服务类进程,当然,用户也可以将自己的脚本在后台运行,这种运行方式不会占用终端资源。对于用户来说,后台运行的进程是透明的,用户可以不用关心后台运行的进程。

15.3.1在系统后台运行脚本

在Linux系统中,以后台模式运行自己编写的脚本同样要在终端命令行启动脚本进程,与在前台启动脚本不同的是,需要在命令后面加一个&符号,&符号会将命令或脚本与Shell分离开,并将命令或脚本作为独立的进程在系统后台运行,命令格式如下。脚本或命令 &

通过终端命令行比较前台模式运行和后台模式运行的区别,还以sleep命令演示,如下。$ sleep 1 &

[1] 5186

$ sleep 2 &

[2] 5188

[1]   Done                    sleep 1

$ sleep 3 &

[3] 5189

[2]   Done                    sleep 2

$

上面的例子中依次执行了三条sleep命令,三条命令都是在后台执行,当执行了第一条sleep命令后,光标没有停顿等待暂停的秒数,而是在控制台显示了一行信息,方括号中的是当前Shell进程中的正在运行的进程的作业号,第一个作业是1,第二个作业的作业号是2,以此类推,作业号后面的的数字是进程ID号(进程ID是唯一的)。命令实际上是在后台运行,控制台只显示作业号和进程ID。第二条命令暂停2秒,显示了两行信息,第一行是作业号和进程ID。第二行显示的是第一条命令在后台执行结束后的信息,作业号、进程状态是完成和执行的命令名称。第三条命令和第二条命令显示的内容相同,作业号、进程ID和第二条命令执行结束后的信息。

一旦在控制台显示了作业号和进程ID,新的命令行提示符会重新出现,这时用户可以在命令行执行其他的命令或脚本,而之前执行的命令正在后台模式下运行。前台进程和后台进程平行运行,如下例子。$ sleep 20 &

[1] 5358

$ echo "Process is  running"

Process is running

$ echo "Process is closed"

Process is closed

[1]+  Done                    sleep 20

$

上面第一条sleep命令在后台运行,第二条命令是在前台运行,两条命令是平行运行的,当第三条命令执行时,后台运行的sleep命令执行结束。

运行脚本也可以在后台运行,方式与命令在后台执行是相同的,如下例子。

例:process01.sh#!/bin/bash

# 循环

for (( i = 1;i < 5;i++ ))

do

# 控制台显示信息

echo "$i:  Hello bash Shell"

# 暂停3秒

sleep 3

done

控制台显示:$ process01.sh &

[1] 5427

$ 1: Hello bash Shell

2: Hello bash Shell

3: Hello bash Shell

4: Hello bash Shell

[1]+  Done                    process01.sh

$

上面例子中,脚本后台运行时任然会将标准输出输出到控制台。通常情况下可以将输出重定向到日志文件。

15.3.2运行多个后台作业

在终端命令行可以同时启动多个后台作业。

例:process02_1.sh#!/bin/bash

echo "This is first process!"

# 暂停30秒

sleep 30

例:process02_2.sh#!/bin/bash

echo "This is second process!"

# 暂停30秒

sleep 30

例:process02_3.sh#!/bin/bash

echo "This is third process!"

# 暂停30秒

sleep 30

例:process02_4.sh#!/bin/bash

echo "This is fourth process!"

# 暂停30秒

sleep 30

控制台显示:$ process02_1.sh &

[1] 5895

$ This is first process!

$ process02_2.sh &

[2] 5898

$ This is second process!

$process02_3.sh &

[3] 5901

$ This is third process!

$process02_4.sh &

[4] 5907

$ This is fourth process!

每启动一个新的作业,Linux系统都会为其分配一个作业号和一个唯一的进程ID号,通过ps命令可以查看到运行的进程。$ ps -ef

UID        PID  PPID   C STIME TTY          TIME CMD

yarn      2845  2814   0 09:19 pts/1    00:00:00 bash

yarn      5316  2814  0 15:41 pts/6    00:00:00 bash

yarn      6133  5316  0 17:27 pts/6    00:00:00 /bin/bash ./process02_1.sh

yarn      6134  6133  0 17:27 pts/6    00:00:00 sleep 50

yarn      6135  5316  0 17:27 pts/6    00:00:00 /bin/bash ./process02_2.sh

yarn      6136   6135  0 17:27 pts/6    00:00:00 sleep 30

yarn      6138  5316  0 17:27 pts/6    00:00:00 /bin/bash ./process02_3.sh

yarn      6139  6138   0 17:27 pts/6    00:00:00 sleep  30

yarn      6140  5316  0 17:27 pts/6    00:00:00 /bin/bash ./process02_4.sh

yarn      6141  6140   0 17:27 pts/6    00:00:00 sleep  30

yarn      6144  5316   3 17:28 pts/6    00:00:00 ps -ef

通过ps命令可以查看到所有启动的进程,包括后台进程和前台进程。即使是后台运行的进程如果有输出,默认情况下也同样会输出到控制台。在终端中启动后台进程时,每个后台进程都会绑定到启动后台进程的终端上,上面的例子中,所有的后台进程都绑定到了pts/6终端上(启动的第六个终端),如果pts/6终端退出,绑定在终端上的后台进程也可能会退出(不同的Linux版本略有不同)。需要注意的是有的Linux版本的终端模拟器会提示有后台进程在运行,有的终端不会提示,这就需要注意了,如果在当前终端启动了后台进程,在后台进程没有结束前,不能将终端退出运行。如果想将后台进程和控制台终端分离,需要使用其他的方式。

15.4在非控制台下运行脚本进程

在终端命令行上启动脚本,让脚本一直运行在后台,即使终端退出运行,脚本进程也不会退出运行,也就是后台进程不绑定特定的终端。这种运行脚本的方式使用nohup命令可以实现。命令格式如下。nohup 脚本或命令 &

先通过sleep命令做一个演示再进行说明,分为两种情况,执行命令的终端运行时和退出运行两种情况。$ nohup sleep 50 &

[1] 6583

$ nohup: 忽略输入并把输出追加到"nohup.out"

控制台显示:$ ps -ef

UID        PID  PPID   C STIME TTY          TIME CMD

yarn      2845  2814  0 09:19 pts/1    00:00:00 bash

yarn      6597 2814  0 18:09 pts/16   00:00:00 bash

yarn      6611 6597   0 18:09 pts/16  00:00:00 sleep 50

yarn      6614 2845 3 18:09 pts/1    00:00:00 ps -ef

$ ps -ef

UID        PID  PPID   C STIME TTY          TIME CMD

yarn      2845  2814   0 09:19 pts/1    00:00:00 bash

yarn      6611     1   0 18:09 ?        00:00:00 sleep 50

yarn      6620  2845   0 18:10 pts/1    00:00:00 ps -ef

上面的命令启动命令进程后使用ps命令查看进程,可以看到当终端没有退出运行时,sleep命令进程是绑定在pts/16终端上的,当终端退出运行后通过ps命令可以看到sleep命令进程没有绑定任何终端。就是说不管启动进程的终端是否运行,命令进程都会在后台独立运行,直到进程结束。实现的原理是由nohup命令启动进程时,nohup命令会运行另一个命令进程来阻断所有发送给当前进程的SIGHUP信号,这样在终端退出运行时进程也不会退出。终端退出运行时进程被阻止退出。

使用nohup命令启动后台进程和普通方式启动后台进程相同的是,Shell进程会为启动的进程分配作业号和进程ID,不同的是当使用nohup命令启动的进程在启动进程的终端退出运行时,进程会忽略终端发送过来的SIGHUP信号,不会终止进程,会继续在后台运行进程,直到进程运行结束。

在使用nohup命令启动后台进程时,在控制台可以看到一行输出“忽略输入并把输出追加到"nohup.out"”,这是因为nohup命令会从终端解除与进程的关联,进程会失去与标准输出STDOUT和STDERR的链接,为了保存进程运行过程中的输出,nohup命令会自动将进程运行过程中的STDOUT和STDERR信息重新定向到当前目录下的nohup.out文件中,nohup.out文件保存了所有输出到控制台的信息,相当于进程运行过程中的日志文件,可以通过cat命令显示文件内容。需要注意的是如果在当前目录使用nohup命令启动多个后台运行的进程,多个进程的输出都会输出到nohup.out文件中。

下面的例子采用nohup命令在后台启动多个脚本进程并通过ps命令查看后台进程的运行状态。采用的脚本还是上面例子的脚本process02_*.sh。

在控制台使用nohup命令启动脚本:$ nohup process02_1.sh &

[1] 3797

$ nohup: 忽略输入并把输出追加到"nohup.out"

$ nohup process02_2.sh &

[2] 3800

$ nohup: 忽略输入并把输出追加到"nohup.out"

$ nohup process02_3.sh &

[3] 3804

$ nohup: 忽略输入并把输出追加到"nohup.out"

$ nohup process02_4.sh &

[4] 3807

$ nohup: 忽略输入并把输出追加到"nohup.out"

$

通过ps命令查看后台进程的运行状态,分为两种情况,执行命令的终端运行时和退出运行两种情况。$ ps -ef

UID        PID   PPID  C STIME TTY          TIME CMD

yarn      3659   3638  0 11:55 pts/1    00:00:00 bash

yarn     3774   3638  0 12:05 pts/2    00:00:00 bash

yarn      3797   3774  0 12:06 pts/2    00:00:00 /bin/bash ./process02_1.sh

yarn      3798   3797  0 12:06 pts/2    00:00:00 sleep 50

yarn      3800   3774  0 12:06 pts/2    00:00:00 /bin/bash ./process02_2.sh

yarn      3801   3800  0 12:06 pts/2    00:00:00 sleep 30

yarn      3804   3774  0 12:06 pts/2    00:00:00 /bin/bash ./process02_3.sh

yarn      3805   3804  0 12:06 pts/2    00:00:00 sleep 30

yarn      3807   3774  1 12:06 pts/2    00:00:00 /bin/bash ./process02_4.sh

yarn      3808   3807  0 12:06 pts/2    00:00:00 sleep 30

yarn      3809   3659  0 12:07 pts/1    00:00:00 ps -ef

$ps -ef

UID        PID   PPID  C STIME TTY          TIME CMD

yarn      3659   3638  0 11:55 pts/1    00:00:00 bash

yarn      3797    1  0 12:06 ?        00:00:00 /bin/bash ./process02_1.sh

yarn      3798  3797  0 12:06 ?        00:00:00 sleep 50

yarn      3800      1  0 12:06 ?        00:00:00 /bin/bash ./process02_2.sh

yarn      3801   3800  0 12:06 ?        00:00:00 sleep 30

yarn      3804      1  0 12:06 ?        00:00:00 /bin/bash ./process02_3.sh

yarn      3805   3804  0 12:06 ?        00:00:00 sleep 30

yarn      3807      1  0 12:06 ?        00:00:00 /bin/bash ./process02_4.sh

yarn      3808   3807  0 12:06 ?        00:00:00 sleep 30

yarn      3812   3659  0 12:07 pts/1    00:00:00 ps -ef

$

终端退出运行后,后台运行的进程就没有绑定到启动进程的终端了,并且进程继续运行直到运行结束。

查看nohup.out文件的内容。四个脚本进程将所有的输出都输出到nohup.out文件。$ cat  nohup.out

This is  first process!

This is  second process!

This is  third process!

This is  fourth process!

$

15.5进程控制

对于运行中的进程我们可以采用组合键将其终止或者挂起,使用kill命令向挂起的进程发送SIGCONT信号可以使挂起的进程重新启动。通常情况下,我们将启动一个进程、挂起一个进程、终止和重新启动进程这些操作称做进程控制,通过进程控制我们可以对运行在Shell进程中的所有进程进行控制。

15.5.1查看Shell进程中正在运行的进程

通过jobs命令可以查看当前Shell进程中挂起的进程,需要注意的是jobs命令只能查看当前Shell进程中的进程信息,如果在不同的Shell进程中启动的进程,只能在启动进程的Shell中可以查看到,如下所示。注:启动一个命令进程

$ sleep  50

注:让进程挂起

^Z

[1]+  Stopped                 sleep 50

注:使用jobs命令查看当前Shell进程中正在运行的进程

$ jobs

[1]+  Stopped                 sleep 50

注:通过bash命令重新启动一个新的子Shell进程(对于当前Shell进程)

$ bash

注:再通过jobs命令查看子Shell进程中正在运行的进程,没有运行的进程

$ jobs

注:退出并终止当前子Shell进程并返回到父Shell进程

$ exit

exit

注:再通过jobs命令查看当前Shell进程中运行的进程,可以看到挂起的进程

$ jobs

[1]+  Stopped                 sleep 50

$

下面的例子采用的脚本的方式

例:process03.sh#!/bin/bash

# 控制台显示,变量$$保存当前脚本进程的进程号

echo  "This is a process! Process number is $$"

# 控制循环变量

count=0

while [  $count -le 5 ]

do

echo "count = $count"

count=$[ $count + 1 ]

sleep 30

done

echo  "End of the test process!"

注:启动一个进程并通过键盘进进程挂起

$ process03.sh

This is  a process! Process number is 4708

count =  0

count =  1

^Z

[1]+  Stopped                 process03.sh

注:启动一个后台进程

$ process03.sh &

[2]  4712

$ This  is a process! Process number is 4712

count =  0

# 通过jobs命令可以看到两个进程,一个暂停运行,一个正在运行

$ jobs

[1]+  Stopped                 process03.sh

[2]-  Running                 process03.sh &

$

通过jobs命令可以查看到当前Shell进程中正在运行或者暂停运行的进程,可以看到输出的信息中一条信息带有加号,一条信息带有一个减号。带有加号的进程是当前Shell进程中默认的进程,带有减号的进程是当默认进程结束后将变为默认进程。不管当前Shell进程中有多少个进程,只有一个进程带有加号,一个进程带有减号。默认进程是当使用进程控制命令时没有指定进程,那么操作的就是默认进程。

上面的例子中在使用jobs命令时没有带任何参数,下面表格中是jobs命令常用到的参数。参数描述

-l显示进程的进程ID号

-n只列出状态改变的进程

-p只列出进程的PID

-r只列出运行中的进程

-s只列出已停止的进程

下面通过一个例子说明jobs命令的参数的用法。注:启动一个进程并将进程挂起

$  process03.sh

This is  a process! Process number is 4948

count =  0

^Z

[1]+  Stopped                 process03.sh

注:在后台启动一个进程

$  process03.sh &

[2]  4953

$ This  is a process! Process number is 4953

count =  0

注:启动第三个进程并将进程挂起

$ process03.sh

This is  a process! Process number is 4956

count =  0

^Z

[3]+  Stopped                 process03.sh

注:使用jobs命令的参数-l,显示进程调度ID号

$ jobs  -l

[1]-  4948 停止                  process03.sh

[2]   4953 Running                 process03.sh &

[3]+  4956 停止                  process03.sh

注:参数-p只显示进程的ID号

$ jobs  -p

4948

4953

4956

注:参数-r只显示运行的进程

$ jobs  -r

[2]   Running                 process03.sh &

注:参数-s只显示停止运行的进程

$ jobs  -s

[1]-  Stopped                 process03.sh

[3]+  Stopped                 process03.sh

$ jobs  -l

[1]-  4948 停止                  process03.sh

[2]   4953 Running                 process03.sh &

[3]+  4956 停止                  process03.sh

注:根据进程号终止进程,注意是默认进程

$ kill  -9 4956

注:带减号的进程成了默认进程

$ jobs  -l

[1]+  4948 停止                  process03.sh

[2]-  4953 Running                 process03.sh &

$

注意:参数可以连起来使用,如:jobs -ls。显示停止的进程信息,包括进程ID。另外,Shell进程的默认进程是最后启动的进程。当默认进程被终止后,带减号的进程变成了默认进程。

15.5.2重启停止的作业

在Shell进程中停止的进程可以通过命令重新启动,重启进程的模式有两种,既前台运行模式和后台运行模式。前台模式重启进程的命令格式如下。fg 进程作业号

以前台模式重启停止运行的进程需要注意的是重启后的进程会占用当前启动进程的终端,直到进程运行结束。下面通过例子说明。$  process03.sh

This is  a process! Process number is 5451

count =  0

^Z

[1]+  Stopped                 process03.sh

$ jobs

[1]+  Stopped                 process03.sh

$ fg 1

process03.sh

count =  1

count =  2

count =  3

count =  4

count =  5

End of  the test process!

$

以前台模式启动的进程会一直占用当前的终端,直到进程运行结束。

以后台模式重启进程,进程会在后台执行,命令格式如下。bg 进程作业号

下面通过例子说明。$  process03.sh

This is  a process! Process number is 5482

count =  0

^Z

[1]+  Stopped                 process03.sh

$  process03.sh

This is  a process! Process number is 5488

count =  0

^Z

[2]+  Stopped                 process03.sh

$ jobs  -l

[1]-  5482 停止                  process03.sh

[2]+  5488 停止                  process03.sh

$ bg 2

[2]+process03.sh &

count =  1

$ jobs  -n

[2]-  Running                 process03.sh &

$ bg 1

[1]+ process03.sh &

count =  1

$ jobs  -ln

[1]-  Running                 process03.sh &

$ jobs  -r

[1]-  Running                 process03.sh &

[2]+  Running                 process03.sh &

$ jobs  -l

[1]-  5482 Running                 process03.sh &

[2]+  5488 Running                 process03.sh &

通过bg命令加上进程作业号可以在后台重启停止的进程,重启的进程信息后面加上了&符号,说明进程在后台运行。需要说明的是如果bg命令后面不跟进程作业号,会默认重启带加号的进程,也就是当前Shell进程中默认的进程。通过jobs -n可以查看改变进程状态的进程信息。

15.6进程的谦让度

在Linux系统中,通过ps命令可以看到有多个进程同时在系统中运行,有的在前台运行,有的在后台运行。如下表中的bash进程、ps-ef进程是前台进程,字段TTY为?号的进程是后台进程。$ ps  -ef

UID        PID   PPID  C STIME TTY         TIME CMD

yarn      2808      1  0 14:04 ?        00:00:04 gnome-terminal

yarn      2809   2808  0 14:04 ?        00:00:00 gnome-pty-helper

yarn      2810   2808  0 14:04 pts/0    00:00:00 bash

root      2895      1  0 14:10 tty2     00:00:00 /sbin/mingetty /dev/tty2

root      3014   2042  0 14:24 ?        00:00:00 tpvmlpd2

yarn      3039   2810  3 14:26 pts/0    00:00:00 ps -ef

对于在Linux中运行的所有进程,Linux内核会将CPU时间片分配给系统上的每一个进程,在一个特定的时间点只有一个进程占用CPU,所以Linux内核会轮流将CPU的使用权分配给每一个进程。默认情况下,从Shell启动的所有进程在Linux系统中具有相同的调度优先级。调度优先级是系统内核分配给进程的CPU使用权的大小。

在系统中,进程的调度优先级是使用整数值来表示的,取值范围是从-20~+19。-20具有最高的调度优先级,+19具有最低的调度优先级。默认情况下,Bash Shell以调度优先级0来启动所有的进程。调度优先级越高,获得CPU的时间机会越高,调度优先级越低,获得CPU的时间机会越低。要注意,整数值越大调度优先级越低。值越小调度优先级越高。

可以通过命令修改进程的调度优先级,对于运行时间较长的进程可以提高进程的调度优先级(取得CPU时间片的机会更多)或者降低一个进程的调度优先级(取得CPU时间片的机会减少)。

15.6.1nice命令

在Linux系统中可以通过nice命令在启动进程时指定进程的调度优先级,nice命令后面不跟任何参数将返回当前进程的调度优先级,在Shell进程中启动的脚本进程默认调度优先级为0,下面通过一个例子说明。

例:process04.sh#!/bin/bash

echo  "process begin"

# 在控制台显示当前进程的调度优先级值

nice

for ((  i = 0;i < 5;i++ ))

do

echo  "i = $i"

# 暂停30秒

sleep 30

done

控制台显示:$  process04.sh

process  begin

0

i = 0

可以看到脚本运行过程中的调度优先级值为默认的0。也可以通过ps命令查看进程的调度优先级值,如下。$ ps  -efl

F S UID         PID  PPID  C PRI   NI ADDR SZ WCHAN  STIME TTY          TIME CMD

0 S  yarn   2810  2808   0  80   0 -   1739 -      14:04 pts/0    00:00:00 bash

4 S  root 2895    1  0   80   0 -   502 ? 14:10 tty2   00:00:00 /sbin/mingetty /dev/tty2

0 S  yarn   3290  2808   0  80   0 -   1719 -  15:16 pts/1    00:00:00 bash

4 S  postfix 4321  2141  0   80   0 -  3150 ?   17:22 ? 00:00:00 pickup -l -t fifo -u

0 S  yarn4493  2810  0   80   0 -  1671 -17:59 pts/0  00:00:00 /bin/bash ./process04.sh

0 S  yarn   4504  4493   0  80   0  -  1420 -      18:00 pts/0    00:00:00 sleep 30

0 R  yarn   4506  3290   0  80   0  -  1639 -      18:00 pts/1    00:00:00 ps -efl

从上面控制台显示的信息中可以看到,字段NI显示的内容就是进程的调度优先级,可以看到所有的进程调度优先级默认情况下都是0。

如果nice命令未指定进程的调度优先值,则以缺省值10来调整程序运行优先级,既在当前程序运行优先级基础之上增加10。如下例子。$ nice  process04.sh

process  begin

10

i = 0

默认情况下,进程的调度优先级是0,上面的例子中在默认值的基础上增加10的优先级。如果调整后的调度优先级高于-20,则就以优先级-20来运行命令行;若调整后的程序运行优先级低于19,则就以优先级19来运行命令行。这里需要注意的是一般用户只能降低进程的调度优先级,如果想要提高进程的调度优先级,只有root用户才具备权限,下面通过一个例子说明。[yarn@YARN  bash01]$ nice -n -1 process04.sh

nice: 无法设置优先级: 权限不够

process  begin

0

i = 0

^C

[yarn@YARN  bash01]$su

密码:

[root@YARN  bash01]# nice -n -1 process04.sh

process  begin

-1

i = 0

^C

[root@YARN  bash01]#nice -n 30 process04.sh

process  begin

19

i = 0

^C

[root@YARN  bash01]#nice -n -30 process04.sh

process  begin

-20

i = 0

^C

[root@YARN  bash01]#

通过上面的例子说明,一般用户只能降低进程的调度优先级,而不能提高进程调度优先级。

15.6.2renice命令

对已经运行的进程进行调度优先级修改需要使用renice命令,需要指定进程的PID,命令格式如下。renice 调度优先级值 -p 进程PID

需要说明的是,设定的值是具体的优先级值。与nice命令不同的是可以使用renice命令多次对运行中的进程进行进程优先级设置,但需要注意的有以下几点:

1.用户只能对属于自己的进程进行优先级设置。

2.普通用户只能降低进程的调度优先级而不能提高进程的优先级。

3.root用户可以对如何进程进行优先级的提高和降低操作。

下面通过一个例子进行说明。$  process04.sh &

[1]  4249

[yarn@YARN bash01]$ renice 10 -p 4249

4249: old priority 0, new priority 10

[yarn@YARN  bash01]$renice 3 -p 4249

renice: 4249: setpriority:权限不够

[yarn@YARN  bash01]$ su

密码:

[root@YARN bash01]# renice -12 -p 4249

4249: old priority 10, new priority -12

[root@YARN bash01]# renice -16 -p 4249

4249: old priority -12, new priority -16

可以看到yarn用户第一次将进程的调度优先级设置成10,第二次设置成3时提示权限不够,优先级从10到3是调度优先级提高了,普通用户只能降低进程的调度优先级。而root用户可以对所有的进程进行优先级的提高或降低,这是要注意的。并且普通用户只能对属于自己的进程进行优先级设置,对于属于其他用户的进程是没有权限进行设置的。所以有时要用到root用户的权限进行操作。

15.7指定脚本的执行时机

通常情况下执行的脚本是实时的,比如手动启动某个服务进程。但是有些情况下需要在特定的情况下或时间点才需要启动脚本。在Linux系统中提供了这样的方法,用来指定脚本的执行时机或特定的时间执行脚本,本节的内容主要对at命令和cron表的使用方法进行说明。

15.7.1使用at命×××执行脚本

在Linux中,可以使用at命令指定何时执行脚本,at命令可以将这个定时启动脚本的作业提交到队列中,指定Shell进程何时启动脚本(需要注意的是,通过at命令指定执行的脚本只执行一次,如果需要定期执行脚本作业,在同一个时间点执行作业需要使用其他的命令)。at命令对应的后台服务进程是atd,Linux不同的版本有一些不同,有的版本中没有预装at工具包,有的版本没有自动启动atd服务。使用at命令需要安装at工具包和启动atd服务,大多数Linux发行版都会在启动时运行此守护进程。可以通过下面的命令检查是否安装at工具包。# 检查是否安装at工具包

$ rpm  -qa | grep at

# 检查atd服务是否启动

$ ps  -ef | grep atd

# 安装at工具包,注意:需要联网

$ yum  install at

注意:安装at工具包需要root用户的权限,

atd服务会定时检查一个特定的目录:/var/spool/at。此目录用来保存at命令提交的作业。默认情况下,atd服务会60秒检查一下这个目录。当有at命令提交的作业时,atd服务会检查作业设置的运行时间。如果设定运行的实际和当前时间匹配,atd服务会启动运行此作业。at命令的格式如下。at -f 执行的脚本 执行的日期时间

at 执行的日期时间

日期格式:日期格式举例

HH:MM05:30

HH:MM YYYY-MM-DD05:30  2016-06-18

[am|pm] + 数字 [minutes|hours|days|weeks]8am + 3  days

9pm + 2  weeks

now + 3  hours

now + 5  minutes

第一种方式是指定在特定的时间执行脚本文件,这种方式较为方便和明确,这种方式可以在脚本中使用,指定特定的时间执行不同的任务。需要说明的是,当作业开始运行时不会绑定任何终端,所以在命令行看不到任何执行过程,所执行的命令和脚本中所有的STDOUT和STDERR都会输出到当前执行at命令的用户的E-mail中。下面通过例子说明。

例:process05.sh#!/bin/bash

# 输出,注意:不会输出到控制台,会输出到当前用户的E-mail中

echo  "Test at command!"

echo  "Script is running"

# 切换目录

cd  /home/yarn/bash01

# 创建日志目录

mkdir  log.d

# 进入目录

cd  log.d

# 创建一个空的日志文件

touch  syslog.log

# 输出到E-mail中

echo  "Create Log file success"

控制台显示:[yarn@YARN  bash01]$ at -f  /home/yarn/bash01/process05.sh now + 5 minutes

job 6 at 2016-04-08 18:05

[yarn@YARN  bash01]$ atq

6     2016-04-08 18:05 a yarn

[yarn@YARN bash01]$ su

密码:

[root@YARN  bash01]# cd /var/spool/at

[root@YARN  at]# ll

总用量 12

-rwx------.  1 yarn   yarn   5024 4月   8 18:00 a0000701739bff

drwx------.  2 daemon daemon 4096 4月   8 17:57 spool

设置5分钟后执行process05.sh脚本,并输出了作业的设置时间日期和作业号,这个作业属于哪个用户等。at命令会将作业提交到队列中,在目录/var/spool/at下,这个目录要用root用户的权限才能看到,看到的a0000701739bff文件是队列文件,当作业完成后,此文件会被删除。

脚本执行结束后会提示将信息输出到了E-mail中:[root@YARN  at]# atq

You  have new mail in /var/spool/mail/yarn

所有的输出都输出到当前用户的E-mail中:# cat /var/spool/mail/yarn

From  yarn@YARN.localdomain  Fri Apr  8 18:05:01 2016

Return-Path:

X-Original-To:  yarn

Delivered-To:  yarn@YARN.localdomain

Received:  by YARN.localdomain (Postfix, from userid 500)

id CB04F408AD; Fri,  8 Apr 2016 18:05:01 +0800 (CST)

Subject:  Output from your job        6

To:  yarn@YARN.localdomain

Message-Id:  <20160408100501.CB04F408AD@YARN.localdomain>

Date:  Fri,  8 Apr 2016 18:05:01 +0800 (CST)

From: yarn@YARN.localdomain  (yarn)

Test at command!

Script is running

Create Log file success

a0000701739bff文件:#!/bin/sh

# atrun  uid=500 gid=500

# mail  yarn 0

umask 2

ORBIT_SOCKETDIR=/tmp/orbit-yarn;  export ORBIT_SOCKETDIR

HOSTNAME=YARN;  export HOSTNAME

USER=yarn;  export USER

#!/bin/bash

echo "Test at command!"

echo "Script is running"

cd /home/yarn/bash01

mkdir log.d

cd log.d

touch syslog.log

echo "Create Log file success"

marcinDELIMITER42a52adb

这个文件是个临时脚本文件,作业执行后此脚本会被删除。可以看到脚本的前面部分有环境变量,在脚本的后面是执行的脚本的代码复制。

脚本执行后创建的目录:$ ll  /home/yarn/bash01/log.d

总用量 0

-rw-rw-r--.  1 yarn yarn 0 4月   8 18:05 syslog.log

at命令的的另一种使用方式是在命令行直接输入要执行的命令,【Ctrl+d】结束。将上面的例子采用命令行方式设定执行计划。$ at  now + 5 minutes

at>  echo "Test at command!"

at>  echo "Script is running"

at>  cd /home/yarn/bash01

at>  mkdir log2.d

at>  cd log2.d

at>  touch syslog.log

at>  echo "Create Log file success"

at>

job 8  at 2016-04-21 17:18

$

上面的方式比较脚本而言适合执行不常用的任务,如果采用脚本的方式就不需要每次输入命令而直接指定脚本就可以了。两种方式中采用脚本的方式较为常见。

删除一个未执行的作业可以使用atrm命令,命令格式如下。atrm 作业号

删除一个未执行的作业需要指定作业的作业号,下面通过例子说明。[yarn@YARN  mail]$ at -f /home/yarn/bash01/process05.sh now + 5 minutes

job 13  at 2016-04-22 10:41

[yarn@YARN  mail]$ at -f /home/yarn/bash01/process05.sh now + 5 minutes

job 14  at 2016-04-22 10:41

[yarn@YARN  mail]$ at -f /home/yarn/bash01/process05.sh now + 5 minutes

job 15  at 2016-04-22 10:41

[yarn@YARN  mail]$ at -f /home/yarn/bash01/process05.sh now + 5 minutes

job 16  at 2016-04-22 10:41

[yarn@YARN  mail]$atq

16    2016-04-22 10:41 a yarn

14    2016-04-22 10:41 a yarn

15    2016-04-22 10:41 a yarn

13    2016-04-22 10:41 a yarn

[yarn@YARN  mail]$ atrm 16

[yarn@YARN  mail]$ atq

14    2016-04-22 10:41 a yarn

15    2016-04-22 10:41 a yarn

13    2016-04-22 10:41 a yarn

[yarn@YARN  mail]$atrm 15

[yarn@YARN  mail]$atq

14    2016-04-22 10:41 a yarn

13    2016-04-22 10:41 a yarn

[yarn@YARN  mail]$ atrm 13

[yarn@YARN  mail]$ atq

14    2016-04-22 10:41 a yarn

[yarn@YARN  mail]$

通过atrm命令可以删除未执行的作业,atq命令显示未执行的作业。

15.8启动时运行脚本或命令

在Linux系统启动时或者用户重新启动一个Shell进程时,执行一个或多个特定功能的脚本,完成特定的功能或启动特定的服务。例如当系统启动时启动WEB服务、数据库服务或重新生成一个系统日志文件等。另外,当用户重新启动一个Shell进程时,也同样可以先执行一个或多个脚本,例如用户专用的环境变量或特定的命令函数等。

15.8.1系统启动时运行脚本

Linux系统采用特定的顺序在系统启动时运行脚本,不同的Linux版本启动的流程略有不同。了解系统启动流程可以帮助我们在系统启动时运行自己的脚本。下面将系统启动时的顺序进行简要的说明。

1.开机过程

当开始运行Linux系统时,Linux会将系统内核加载到内存中并运行。首先读取/etc/inittab文件,/etc/inittab文件列出了系统的运行级别,不同的Linux运行级别会启动不同的程序和脚本。

/etc/inittab文件:# inittab is only used by upstart for the  default runlevel.

# Default runlevel. The runlevels used are:

#   0  - halt (Do NOT set initdefault to this)

#   1  - Single user mode

#   2  - Multiuser, without NFS (The same as 3, if you do not have networking)

#   3  - Full multiuser mode

#   4  - unused

#   5  - X11

#   6  - reboot (Do NOT set initdefault to this)

#

id:5:initdefault:

以CentOS系统为例,系统的运行级别分为六种,如下表说明。运行级别说明

0关机

1单用户模式

2多用户模式,通常不支持网络

3全功能多用户模式,支持网络

4可定义用户

5多用户模式,支持网络和图形化桌面系统

6重启

系统运行级别可以控制启动系统上的那些服务,可以看到/etc/inittab文件默认的运行级别是5,是多用户模式。每个运行级别将决定启动过程运行和停止哪些脚本。这些开机脚本都是Shell脚本,通过提供必要的环境变量来启动应用程序。开机脚本会放置在/etc/rc*.d目录或init.d目录下,*号代表系统运行级别。不同的Linux版本略有不同。

2.定义自己的开机脚本

不要将用户的启动脚本和系统的启动的脚本放置在同一目录下,例如:将自己的Shell脚本放置在rc5.d目录下,或者在系统启动脚本中添加自己的脚本内容,这样可能会出现不可预测的问题。通常情况下,以CentOS系统为例,用户可以在/etc/rc.d/rc.local文件中添加用户启动时执行的脚本。

在rc.local文件中可以指定要执行的命令或者脚本,但要注意的是执行的脚本需要完整的路径,否则系统找不到执行文件会提示错误信息。如下例子,在系统启动时执行yarn用户目录下的一个脚本文件,注意:需要提供完整路径。

/etc/rc.d/rc.local 文件 :#!/bin/sh

#

# This script will be executed *after* all  the other init scripts.

# You can put your own initialization stuff  in here if you don't

# want to do the full Sys V style init  stuff.

touch /var/lock/subsys/local

/home/yarn/bash01/init.sh

配置完成后,每次启动系统,都会执行init.sh脚本,如果创建文件或删除文件等等。

15.8.2启动新的Shell进程时运行脚本

在前面的章节中讲过,每个Linux系统用户的工作目录下都存在两个文件:.bash_profile文件和.bashrc文件,可以使用这两个文件设置用户环境变量、函数、命令别名和启动用户脚本。

当用户登录系统时启动的Shell进程会执行.bash_profile文件,并且只执行一次,所以可以将用户登录时要执行的脚本设置到这个文件中。而.bashrc文件是通过bash命令或打开一个终端时启动的新的Shell进程时都会执行一次,所以这个文件中也可以设置要运行的脚本,但要注意,每次启动一次新的Shell进程时都会运行一次。

或者在/etc/profile.d目录下创建用户的脚本,当用户登录时,这个脚本会被执行。或者在/etc/profile文件中指定要执行的脚本,需要指定脚本的完整路径。/etc/profile文件只在用户登录时执行一次。另外,可以在/etc/bashrc文件中指定要运行的脚本,但不同之处在于,系统上所有的用户只要通过bash命令或重新打开一个终端时都会执行一次,用户可以根据不同的情况选择不同的文件执行用户的脚本。

linux脚本执行过程中被挂起,Linux学习笔记(八)——脚本执行的过程控制相关推荐

  1. 1499飞天茅台脚本使用过程中遇到的Python问题汇总索引目录【淘宝-天猫超市、京东】

    1499飞天茅台脚本使用过程中遇到的Python问题汇总索引目录[淘宝-天猫超市.京东] 1499飞天茅台脚本使用过程中遇到的Python问题汇总索引目录[淘宝-天猫超市.京东] @[TOC] 原文地 ...

  2. Sql Server 因为触发器问题导致数据库更新报错“在触发器执行过程中引发了错误,批处理已中止”的问题处理...

    在维护一个非常旧的项目时,由于该项目版本已经非常老了,而且在客户现场运行的非常稳定,更要命的是本人目前没有找到该项目的代码,为了处理一个新的需求而且还不能修改程序代码,于是决定从数据库入手,毕竟该项目 ...

  3. Pytest----pytest-sugar 执行过程中显示进度条

    [原文链接]Pytest----pytest-sugar 执行过程中显示进度条 Pytest-sugar是一款用来改善控制台显示的插件,增加了进度条显示,使得在用例执行过程中可以看到进度条,而且进度条 ...

  4. Linux系统安装驱动过程中ko文件加载错误(Required key not available)的解决办法

    Linux系统安装驱动过程中ko文件加载错误(Required key not available)的解决办法 问题描述 在Ubuntu上使用CP210x USB转UART设备时需要安装驱动程序(CP ...

  5. 在动作观察,运动想象和站立和坐姿执行过程中解码脑电节律

    事件相关去同步化与同步化(ERD/S)和运动相关皮质电位(MRCP)在下肢康复的脑机接口(BCI)中,特别是在站立和坐姿中,起着重要的作用.然而,人们对站立和坐着的大脑皮层活动的差异知之甚少,尤其是大 ...

  6. java 方法执行结束局部变量释放_JAVA-方法在执行过程中,JVM的内存分配和变化情况,栈(stack)的情况浅析...

    方法在执行过程中,在JVM中的内存是如何分配的?内存是如何变化的? 一.方法只定义不调用,是不会被执行的,并且在JVM中也不会给该方法分配"运行所属"的内存空间.只有在调用这个方法 ...

  7. python 利用pyinstaller 编译.exe文件过程中编写完的.exe文件执行过程中闪退

    问题描述: python 利用pyinstaller 编译.exe文件过程中编写完的.exe文件执行过程中闪退,并提示no module named 'pyproj.datadir' 解决方法: 闪退 ...

  8. 对下图所示的连通网络G,用克鲁斯卡尔(Kruskal)算法求G的最小生成树T,请写出在算法执行过程中,依次加入T的边集TE中的边。说明该算法的基本思想及贪心策略,并简要分析算法的时间复杂度

    对下图所示的连通网络G,用克鲁斯卡尔(Kruskal)算法求G的最小生成树T,请写出在算法执行过程中,依次加入T的边集TE中的 边.说明该算法的基本思想及贪心策略,并简要分析算法的时间复杂度

  9. 一个进程在执行过程中可以被中断事件打断_Linux操作系统:中断类型和中断的作用...

    1.中断的概念 中断对于操作系统非常重要,它就好像机器中的齿轮,驱动各部件的动作.所以,许多人称操作系统是由"中断驱动"的. 所谓中断是指CPU对系统发生的某个事件做出的一种反应, ...

最新文章

  1. 图形基础 GPU架构(2)软件调用栈
  2. 黑盒测试法——等价类划分法(修改版)
  3. [SHOI2008]cactus仙人掌图
  4. 如何打造一支低效技术团队?
  5. Unity MegaFiers 顶点动画
  6. 蔚来汽车股价跌跌不休 盘中触及上市以来最低点
  7. Day3-Spring的事务管理、Spring框架的JDBC模板
  8. VS快捷键及调试方法(含VAssistX快捷键)
  9. MYBATIS 文档
  10. 矢量网络分析仪(矢网)的校准
  11. Postgresql学习03-C外部函数创建,及与Plsql自定义函数效率对比
  12. 征途单机版场景服务器端口被占用,《征途单机版》构架攻略
  13. ROS——Gazebo仿真——全向轮小车——运动学模型分析
  14. 107 THREE.JS 使用StereoEffect实现模拟VR双屏
  15. MEGA UNO固件烧写教程
  16. GEE引擎自定义进度条和自定义属性的脚本展示
  17. VGGNet网络结构
  18. matlab怎么分析突变点,小波变换检测信号突变点的MATLAB实现
  19. 信息安全——替换密码加密(使用Cryptool、物联网)
  20. springboot项目打包部署到阿里云

热门文章

  1. 放大招!!!落地成盒?教你开发自己的联网吃鸡游戏 1
  2. 自己曾经的C++笔记【在c盘爆满的时候找到的回忆】
  3. 计算机开机过程ppt,计算机开机、鼠标键盘使用讲解.ppt
  4. 动态报表D-Query 通过字频优化字段选择
  5. Python办公自动化实战 05 | Python-docx库:Python与Word的完美结合_ 利用代码实现Word中插入图片
  6. 【从零开始学微服务】07.微服务常用的开发框架
  7. NET Core微服务之路:基于Ocelot的API网关实现--http/https协议篇
  8. 医学专业自学计算机,医学专业计算机教学措施
  9. SolidWorks 2019 鼠标基本操作——选取对象操作
  10. [electron] 01 一分钟web应用秒变客户端软件(附原理及其他各种打包方案)