exec执行普通文件和解释器文件的区别 2014-11-15 23:52:45

分类: LINUX

exec执行普通文件和解释器文件的区别

——lvyilong316

1. 从一个问题开始

首先要从项目中遇到的一个问题说起。编写一个python文件test.py,文件test.py内容如下:

#! /usr/bin/python

....

如果在命令行方式执行test.py的方式是:

test.py -in inputfile -out outputfile;或python test.py -in inputfile -out outputfile;

但是因为需要,用exec函数(这里使用execl)去调用这个python文件。在项目中是这样写的:

execl(”test.py”,”-in”,”inputfile”,”-out”,”outputfile”,(char*)0);

但执行结果并不是预想的test.py执行,而是启动了python交互程序,不知道是什么原因。因为一直以为如果写一个C程序,比如main。那么在命令行输入:main arg1 arg2执行的效果和execl(”main”,”arg1”,”arg2”,(char*)0)的效果应该是一样的。

当然同时伴随我有另一个问题:

execl(“usr/bin/python”,”test.py”,(char*)0);和输入命令”/usr/bin/python test.py”有什么区别?

为了回答些问题,自己通过再反复看apue和做实验测试,终于一点一点明白了,下面就来一点一点的分析。

2. 命令行执行程序和exec执行程序的区别

首先我们来分析一下在命令行执行一个程序和通过exec函数执行程序有什么区别,或者说需要注意的地方(一下所有编写的文件都在/mnt/hgfs/VWShared/目录下)。

编写程序foo.c如下,并编译为可执行文件foo。它打印参数列表(argv)的所有参数.

l foo.c

#include

int

main(int argc,char* argv[])

{

int i;

for(i=0;i<argc;i++)< span="">

printf("argv[%d]: %s\n",i,argv[i]);

exit(0);

}

再编写main.c如下,将其编译为可执行文件main,它使用execl调用foo。

l main.c

#include

#include

int main(int argc,char* argv[])

{

int n=0;

if( (n=execl("/mnt/hgfs/VWShared/foo",(char*)0))==-1 )

{

perror("execl error");

exit(0);

}

exit(1);

}

直接在命令行下运行foo,结果如图1:

图1

运行main(通过execl运行foo)结果如图2:

图2

可以看出直接在命令行运行foo,则”./foo”被当做argv[0],但是通过exec运行foo发现并没有参数传入foo(程序没有任何输出),也就是说argc值为0。这是什么原因呢?我们知道argv存放的是传递给main函数的命令行参数,当在命令行键入”./foo”时,唯一的命令行参数”./foo”就被传入给main的argv了。所以直接在命令行运行foo就打印出唯一的参数”./foo”。

那么execl的情况呢?首先看一下execl的原型:

int execl(const char* pathname,const char* arg0,.../*(char*)0*/);

注意到了吧,第一个参数是要执行的程序名,第二个参数才是要传入待执行程序的第一个参数,而上述main.c中没有第二个参数(这里说的是execl的第二个参数),也就是没有给foo传递任何参数,foo的参数表argv当然就是空了,或者说argc为0。

通过这个例子我们要有以下认识:

argv[0]不一定就是所执行程序的名称,确切的说它只是命令行的第一个参数,只是通常启动程序是在命令行键入程序名称启动的,所以程序的名称才成为argv[0]。但是也有情况argv[0]不是程序名称的,如:

(1) 通过exec执行时,argv[0]是什么要视exec的参数来定。

例如:我们将main中的execl语句改为:execl("/mnt/hgfs/VWShared/foo","xxxxx",(char*)0);

再运行main,效果如图3:

图3

可以看到argv[0]变为了我们传入的参数”xxxxx”。

(2) 通过程序别名启动时,argv[0]就是程序的别名。如我们给foo创建一个软连接sfoo,然后执行sfoo效果如图4:

图4

可以看出输出的argv[0]是./sfoo 而不是./foo,再次证明argv[0]是什么和程序名称无关,只是和传入的命令行第一个参数有关。

补充:在创建上述软连接过程中遇到了一点小问题,不妨也在这里写下来:

【问题】

在编译VMware下的Linux系统对从Windows中共享过来的文件,进行编译的时候,遇到:ln: creating symbolic link XXXXXX : Operation not supported
【解决办法】

出现这类问题,主要是由于在编译的时候,要用ln去建立一些软链接,而这些文件是从Windows中,通过VMWare虚拟机共享进Linux的,而虽然此种操作在Linux系统中很常见,但Windows不支持,所以,编译会报错。比较方便的解决办法是先将文件考到linux的其他目录,再在其他非共享目录中创建软连接。另外还有个解决办法就是,在VMWare下的Linux中,建立Samba服务,然后新创建新samba用户和文件夹,然后在windows中就可以访问到该文件夹了。然后把在Linux中,从共享目录拷贝到你所要共享的samba目录中,这样,也可以实现我们所要的文件共享。此时在去编译这些代码的时候,由于是在Linux系统中的,所以就OK了。

3. 解释器文件和解释器

先解释两个概念;解释器文件和解释器。

l 解释器文件:一种文本文件,开头通常是:#! pathname [option-argument];比较常见的是#! /bin/bash,shell脚本和python脚本都属于解释器文件。

l 解释器:解释器文件第一行中pathname指定的程序,如bash。

3.1 解释器文件的执行

当执行(exec)"解释器"文件时,exec系统调用会识别这种文件,内核使调用exec函数的进程实际执行的并不是该"解释器文件",而是pathname指定的解释器。

我们可以自己写一个解释器,如之前所写的foo.c:

l foo.c

#include

int

main(int argc,char* argv[])

{

int i;

for(i=0;i<argc;i++)< span="">

printf("argv[%d]: %s\n",i,argv[i]);

exit(0);

}

编译成为foo然后保存在/mnt/hgfs/VWShared/。

下面我们在自己写一个”解释器文件”——test:

l test

#!/mnt/hgfs/VWShared/foo

3.1.1 通过命令行执行解释器文件

直接在命令行中键入:”./test”,运行test,效果如图5。

图5

将test内容修改为:#!/mnt/hgfs/VWShared/foo  argA argB,再次在命令行运行test,效果如图6。

图6

通过这两个例子,可以看出命令行运行时当执行文件是”解释器文件”时,参数是如何传递给解释器的:

(1) 通过执行”解释器文件”执行解释器,传递给解释器的第一个参数是解释器文件的pathname,即解释器的路径。

(2) “解释器文件”中pathname后的可选参数(这里的argA,argB)如果存在的话会一起作为第二个参数传递给解释器。

(3) “解释器文件”名称会作为下一个参数传递给解释器。

3.1.2 通过execl执行解释器文件

接下来通过execl执行解释器文件test,修改main中的exec语句如下:

execl("/mnt/hgfs/VWShared/test","arg1",”arg2”,(char*)0));然后执行main,效果如图7。

图7

从这个例子可以了解当执行文件是解释器文件时,内核如何处理exec函数的参数及解释器文件第一行的可选参数。我们知道执行解释器文件实际是执行解释器,由解释器去读取解释器文件中的语句执行,而第一行的pathname以#开头在执行时会被当做注释忽略。下面就让我们分析一下最终传入解释器foo的参数都是什么。

(1) argv[0]是该解释器文件的pathname;

(2) argv[1]是该解释文件中的可选参数;

(3) argv[2]是解释器文件本身名字;

(4) argv[3]是execl出入的第二个参数(第一个参数是arg1)。

那么问题出现了,我们传入execl的arg1去哪里了呢?其实这就是exec执行”解释器文件”和执行一般程序的不同之处:在执行一般程序时,execl(const char* pathname,const char* arg0,...,(char*)0)中的arg0会被当做执行程序(pathname)的第一个参数argv[0],而在执行解释器文件时,内核取execl调用中的pathname而非第一个参数(arg0)作为第一个参数传递给解释器,因为一般而言,第一个参数arg0通常是解释器文件的名字,而pathname包含了比arg0更多的信息(解释器文件的完整路径)。所以当execl执行解释器文件时第一个参数arg0是无效的。

为了说明这个问题,我们再举一个例子,编写python文件pyth.py如下:

l pyth.py:

#! /usr/bin/python

import sys

for i in range(0,len(sys.argv)):

print "argv[%d]: %s"%(i,sys.argv[i])

它的功能和foo一样同样是打印每个命令行参数。我们分别将main中的execl语句改为:

execl("/mnt/hgfs/VWShared/foo","arg1","arg2",(char*)0))和

execl("/mnt/hgfs/VWShared/pyth.py","arg1","arg2",(char*)0)),对比execl一般程序(foo)和解释器文件(pyth.py)的效果如图8、9。

图8.execl("/mnt/hgfs/VWShared/foo","arg1","arg2",(char*)0))结果

图9.execl("/mnt/hgfs/VWShared/pyth.py","arg1","arg2",(char*)0))结果

可以看出execl对于执行普通文件和解释器文件选取第一个参数是不同的。

3.2 execl执行解释器文件和命令行执行解释器文件的不同

我们上面已经看到execl("/mnt/hgfs/VWShared/pyth.py","arg1","arg2",(char*)0))的结果(图9),下面我们试一下命令行方式:pyth.py arg1 arg2,结果图10:

图10

可以看到结果和通过execl执行是有区别的,通过命令行执行解释器文件就像通过命令行执行普通程序一样,程序名称作为第一个参数,命令行后面依次作为后续参数。正因为对于解释器文件的execl方式和命令行方式执行时选取第一个参数的方式不同,所以对于解释器文件a.py:

(1) 在命令行输入:./a.py arg1 arg2;

(2) execl("./a.py","arg1","arg2",(char*)0));

(3) execl("./a.py",”xxx”,"arg1","arg2",(char*)0));

方式(1)和方式(2)不等价,因为方式(1)中arg1会被当做第二个参数传递给解释器,而方式(2)中arg2会被当做第二个参数传递给解释器。方式(1)和方式(3)是等价的。

对于普通文件foo:

(1) 在命令行输入: ./foo arg1 arg2;

(2) execl("./foo","arg1","arg2",(char*)0))

方式(1)和方式(2)是等价的。

4. 回答开始的问题

为了达到命令行方式:test.py arg1 arg2的效果,使用execl("test.py","arg1","arg2",(char*)0))肯定是不行的,因为arg1会被忽略,提示缺少参数。正确的方式是:execl("test.py",”xxx”,"arg1","arg2",(char*)0)),这里”xxx”代表任意字符串,不过一般会使用解释器文件名,即”test.py”。

为了达到命令行方式:python test.py arg1 arg2的效果,使用execl("python",”test.py”,"arg1","arg2",(char*)0))也是不行的,因为test.py会被忽略,arg1会被当做第一个参数传给python解释器。正确方式是:

execl(“python",”xxx”,”test.py”,"arg1","arg2",(char*)0)),这里”xxx”代表任意字符串,不过一般会使用解释器文件名,即”test.py”。

unix解释器文件详解的更多相关文章

  1. Linux中/proc目录下文件详解

    转载于:http://blog.chinaunix.net/uid-10449864-id-2956854.html Linux中/proc目录下文件详解(一)/proc文件系统下的多种文件提供的系统 ...

  2. SUBLIME TEXT 2 设置文件详解

    SUBLIME TEXT 2 设置文件详解 Preferences.sublime-settings文件: // While you can edit this file, it’s best to ...

  3. 【转】linux中inittab文件详解

    原文网址:http://www.2cto.com/os/201108/98426.html linux中inittab文件详解 init的进程号是1(ps -aux | less),从这一点就能看出, ...

  4. Linux中/proc目录下文件详解(转贴)

      转载:http://www.sudu.cn/info/index.php?op=article&id=302529   Linux中/proc目录下文件详解(一) 声明:可以自由转载本文, ...

  5. C#中web.config文件详解

    C#中web.config文件详解 一.认识Web.config文件 Web.config 文件是一个XML文本文件,它用来储存 ASP.NET Web 应用程序的配置信息(如最常用的设置ASP.NE ...

  6. vue-cli生成的模板各个文件详解(转)

    vue-cli脚手架中webpack配置基础文件详解 一.前言 原文:https://segmentfault.com/a/1190000014804826 vue-cli是构建vue单页应用的脚手架 ...

  7. web.xml文件详解

      web.xml文件详解 Table of Contents 1 listener. filter.servlet 加载顺序 2 web.xml文件详解 3 相应元素配置 1 listener. f ...

  8. [转]AndroidManifest.xml文件详解

    转自:http://www.cnblogs.com/greatverve/archive/2012/05/08/AndroidManifest-xml.html AndroidManifest.xml ...

  9. delphi 资源文件详解

    delphi资源文件详解 一.引子: 现在的Windows应用程序几乎都使用图标.图片.光标.声音等,我们称它们为资源(Resource).最简单的使用资源的办法是把这些资源的源文件打入软件包,以方便 ...

随机推荐

  1. Moss、SharePoint数据库迁移问题(转)

    当项目快做完时,大家都要考虑将程序及数据迁移到正式环境部署.但是,如果用SharePoint开发,它会产生很多数据库,到底哪些需要迁移,哪些不需要迁移了?? 请看: 1.配置完成SharePoint后 ...

  2. ViewState的用法

    学习标签: ViewState 本文导读:在web窗体控件设置为runat = "server",这个控件会被附加一个隐藏的属性_ViewState,_ViewState存放了所有 ...

  3. MSSQL、C# 、Winform、ASP.NET - 数据库备份与还原模块

    数据库备份还原类: using System; using System.Collections.Generic; using System.Linq; using System.Text; usin ...

  4. Eclipse用法和技巧九:自动添加try/catch块2

    上一篇介绍了如何给未检查异常快速增加try/catch语句,这里在补充一点其他相关操作.有时候我们增加了try/catch之后还需要在加一个finally块,比如android上每次分配一个curso ...

  5. 使用awk和grep做简单的统计

    grep 或 egrep 或awk 过滤两个或多个关键词: grep -E ‘123|abc’ filename // 找出文件(filename)中包含123或者包含abc的行 egrep ‘123 ...

  6. 当向计算机中存入一个float类型的数值2.2 后,在从计算机中读出输出,这时2.2 的值已经发生了变化(转)

    problom : 'f1' value hava been changed when output. reason : the binary repersentation of 2.2f is : ...

  7. perl 改变对象属性

    [root@wx03 test]# cat Critter.pm package Critter; #unshift(@INC,"/root/test"); #use messag ...

  8. 性能测试之LoardRunner 自动关联

    1.什么是自动关联? 2.实例介绍 以下是详细介绍: 自动化关联:它是VuGen提供的自动化扫描关联处理策略,它的原理是对同一个脚本运行和录制时的服务器返回进行比较,来自动查找变化的部分,并且提示是否 ...

  9. ASP.NET - 多文件上传,纯代码,不使用插件

    解决方案: 前段代码: <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Mu ...

  10. 报错消息写在AT SELECTION-SCREEN OUTPUT和START-OF-SELECTION事件下的区别

    今天面试没答上来的问题,其实我是知道的,以前也遇到过.... START-OF-SELECTION下的话会在左下角报错 AT SELECTION-SCREEN OUTPUT消息会弹出框,然后点击就没有 ...