zoukankan      html  css  js  c++  java
  • shell调用另一个脚本的三种方式fork/exec/source

      exec和source都属于bash内部命令(builtins commands),在bash下输入man exec或man source可以查看所有的内部命令信息。

    bash shell的命令分为两类:外部命令和内部命令。外部命令是通过系统调用或独立的程序实现的,如sed、awk等等。内部命令是由特殊的文件格式(.def)所实现,如cd、history、exec等等。

      在说明exe和source的区别之前,先说明一下fork的概念。

      fork是linux的系统调用,用来创建子进程(child process)。子进程是父进程(parent process)的一个副本,从父进程那里获得一定的资源分配以及继承父进程的环境。子进程与父进程唯一不同的地方在于pid(process id)。

        环境变量(传给子进程的变量,遗传性是本地变量和环境变量的根本区别)只能单向从父进程传给子进程。不管子进程的环境变量如何变化,都不会影响父进程的环境变量。 

    shell script:

    有两种方法执行shell scripts,一种是新产生一个shell,然后执行相应的shell scripts;一种是在当前shell下执行,不再启用其他shell。

    新产生一个shell然后再执行scripts的方法是在scripts文件开头加入以下语句

    #!/bin/sh

    一般的script文件(.sh)即是这种用法。这种方法先启用新的sub-shell(新的子进程),然后在其下执行命令。

    另外一种方法就是上面说过的source命令,不再产生新的shell,而在当前shell下执行一切命令。

    source:

    source命令即点(.)命令。

    在bash下输入man source,找到source命令解释处,可以看到解释”Read and execute commands from filename in the current shell environment and …”。从中可以知道,source命令是在当前进程中执行参数文件中的各个命令,而不是另起子进程(或sub-shell)。

     exec:

    在bash下输入man exec,找到exec命令解释处,可以看到有”No new process is created.”这样的解释,这就是说exec命令不产生新的子进程。那么exec与source的区别是什么呢?

    exec命令在执行时会把当前的shell process关闭,然后换到后面的命令继续执行。

    1. 系统调用exec是以新的进程去代替原来的进程,但进程的PID保持不变。因此,可以这样认为,exec系统调用并没有创建新的进程,只是替换了原来进程上下文的内容。原进程的代码段,数据段,堆栈段被新的进程所代替。

    一个进程主要包括以下几个方面的内容:

    (1)一个可以执行的程序

    (2) 与进程相关联的全部数据(包括变量,内存,缓冲区)

    (3)程序上下文(程序计数器PC,保存程序执行的位置) 

    2. exec是一个函数簇,由6个函数组成,分别是以excl和execv打头的。

    执行exec系统调用,一般都是这样,用fork()函数新建立一个进程,然后让进程去执行exec调用。我们知道,在fork()建立新进程之后,父进各与子进程共享代码段,但数据空间是分开的,但父进程会把自己数据空间的内容copy到子进程中去,还有上下文也会copy到子进程中去。而为了提高效率,采用一种写时copy的策略,即创建子进程的时候,并不copy父进程的地址空间,父子进程拥有共同的地址空间,只有当子进程需要写入数据时(如向缓冲区写入数据),这时候会复制地址空间,复制缓冲区到子进程中去。从而父子进程拥有独立的地址空间。而对于fork()之后执行exec后,这种策略能够很好的提高效率,如果一开始就copy,那么exec之后,子进程的数据会被放弃,被新的进程所代替。

     3. exec与system的区别

    (1) exec是直接用新的进程去代替原来的程序运行,运行完毕之后不回到原先的程序中去。

    (2) system是调用shell执行你的命令,system=fork+exec+waitpid,执行完毕之后,回到原先的程序中去。继续执行下面的部分。

    总之,如果你用exec调用,首先应该fork一个新的进程,然后exec. 而system不需要你fork新进程,已经封装好了。

    先来说一下主要以下有几种方式:

    • fork: 如果脚本有执行权限的话,path/to/foo.sh。如果没有,sh path/to/foo.sh。
    • exec: exec path/to/foo.sh
    • source: source path/to/foo.sh

    fork

    fork 是最普通的, 就是直接在脚本里面用 path/to/foo.sh 来调用 foo.sh 这个脚本,比如如果是 foo.sh 在当前目录下,就是 ./foo.sh。运行的时候 terminal 会新开一个子 Shell 执行脚本 foo.sh,子 Shell 执行的时候, 父 Shell 还在。子 Shell 执行完毕后返回父 Shell。 子 Shell 从父 Shell 继承环境变量,但是子 Shell 中的环境变量不会带回父 Shell。

    exec

    exec 与 fork 不同,不需要新开一个子 Shell 来执行被调用的脚本. 被调用的脚本与父脚本在同一个 Shell 内执行。但是使用 exec 调用一个新脚本以后, 父脚本中 exec 行之后的内容就不会再执行了。这是 exec 和 source 的区别.

    source

    与 fork 的区别是不新开一个子 Shell 来执行被调用的脚本,而是在同一个 Shell 中执行. 所以被调用的脚本中声明的变量和环境变量, 都可以在主脚本中进行获取和使用。

    其实从命名上可以感知到其中的细微区别,下面通过两个脚本来体会三种调用方式的不同:

    第一个脚本,我们命名为 1.sh:

     1 #!/bin/bash
     2 A=1
     3 echo "before exec/source/fork: PID for 1.sh = $$"
     4 export A
     5 echo "In 1.sh: variable A=$A"
     6 case $1 in
     7     --exec)
     8         echo -e "==> using exec…
    "
     9         exec ./2.sh ;;
    10     --source)
    11         echo -e "==> using source…
    "
    12         . ./2.sh ;;
    13     *)
    14         echo -e "==> using fork by default…
    "
    15         ./2.sh ;;
    16 esac
    17 echo "after exec/source/fork: PID for 1.sh = $$"
    18 echo -e "In 1.sh: variable A=$A
    "

    第二个脚本,我们命名为 2.sh

    1 #!/bin/base
    2 echo "PID for 2.sh = $$"
    3 echo "In 2.sh get variable A=$A from 1.sh"
    4 A=2
    5 export A
    6 echo -e "In 2.sh: variable A=$A
    "

    注:这两个脚本中的参数 $$ 用于返回脚本的 PID , 也就是进程 ID。这个例子是想通过显示 PID 判断两个脚本是分开执行还是同一进程里执行,也就是是否有新开子 Shell。当执行完脚本 2.sh 后,脚本 1.sh 后面的内容是否还执行。

    chmod +x 1.sh 2.sh 给两个脚本加上可执行权限后执行情况:

    fork

    fork 方式可以看出,两个脚本都执行了,运行顺序为1-2-1,从两者的PID值(1.sh PID=82266, 2.sh PID=82267),可以看出,两个脚本是分成两个进程运行的。

    exec

    exec 方式运行的结果是,2.sh 执行完成后,不再回到 1.sh。运行顺序为 1-2。从pid值看,两者是在同一进程 PID=82287 中运行的。

    source

     source方式的结果是两者在同一进程里运行。该方式相当于把两个脚本先合并再运行。

  • 相关阅读:
    VIJOS-P1340 拯救ice-cream(广搜+优先级队列)
    uva 11754 Code Feat
    uva11426 GCD Extreme(II)
    uvalive 4119 Always an Interger
    POJ 1442 Black Box 优先队列
    2014上海网络赛 HDU 5053 the Sum of Cube
    uvalive 4795 Paperweight
    uvalive 4589 Asteroids
    uvalive 4973 Ardenia
    DP——数字游戏
  • 原文地址:https://www.cnblogs.com/zhiminyu/p/12505184.html
Copyright © 2011-2022 走看看