精华内容
下载资源
问答
  • 在linux下想调用shell脚本,肯定对于使用system不陌生?但是使用system()函数一定要谨慎。...这么说还是比较通俗易懂下面详细描述system: #include <stdlib.h> int system(const char *

    在linux下想调用shell脚本,肯定对于使用system不陌生?但是使用system()函数一定要谨慎。必要要理清返回值问题以及调用机制。不然很容易造成程序的异常和bug等问题。

    system函数介绍

    system的作用是在shell终端执行command。简单的说就是在C中执行system(“pwd”)这行代码的含义就相当于在shell执行pwd一样。这么说还是比较通俗易懂的,下面详细描述system:

    #include <stdlib.h>
    int system(const char *command);
    
    

    我们开看看man 3 system

    system() executes a command specified in command by calling /bin/sh -c command, and returns after the command has been com‐
    pleted. During execution of the command, SIGCHLD will be blocked, and SIGINT and SIGQUIT will be ignored.

    大体的意思就是:

    system()函数调用/bin/sh来执行参数指定的命令,/bin/sh 一般是一个软连接,指向某个具体的shell,比如bash,-c选项是告诉shell从字符串command中读取命令;

    在这里插入图片描述
    system()函数返回值

    还是通过man 3 system

    The value returned is -1 on error (e.g., fork(2) failed), and the return status of the command otherwise. This latter return
    status is in the format specified in wait(2). Thus, the exit code of the command will be WEXITSTATUS(status). In case
    /bin/sh could not be executed, the exit status will be that of a command that does exit(127).
    If the value of command is NULL, system() returns nonzero if the shell is available, and zero if not.
    system() does not affect the wait status of any other children.

    大体的意思就是:

    为了更好的理解system()函数返回值,需要了解其执行过程,实际上system()函数执行了三步操作:

    1.fork一个子进程(创建子进程等准备工作);
    2.在子进程中调用exec函数去执行command(调用/bin/sh拉起shell脚本);
    3.在父进程中调用wait去等待子进程结束。

    从上面man手册可知,不管shell脚本返回什么值,只要调用了/bin/sh,并且执行过程没有被信号中断,都算正常结束。因为脚本是在子进程中执行的,所以要想获取脚本是否执行成功的方法只能用系统提供的两个宏。

    WIFEXITED用来判断阶段二的返回值
    WEXITSTATUS用来判断阶段三的返回值

    下面的例子是判断一个脚本是否执行成功。

    test1.sh

    #! /bin/bash
    
    #basic for command
    
    for test in A B C D E F
    do
        echo "The next state is $test"
    done
    

    main.c

    #include <stdio.h> 
    #include <stdlib.h> 
    #include <sys/wait.h> 
    #include <sys/types.h> 
    
    int main(int argc, char **argv) { 
    
    	pid_t status; 
    
    
    	status = system("./test1.sh"); 
    
    	if (-1 == status) { 
    		printf("system error!"); 
    	} 
    
    	else 
    	{ 
    		printf("exit status value = [0x%x]\n", status); 
    
    		if (WIFEXITED(status)) //正确退出
    		{ 
    			if (0 == WEXITSTATUS(status)) //操作成功
    			{ 
    				printf("run shell script successfully.\n"); 
    			} 
    			else //操作失败
    			{ 
    				printf("run shell script fail, script exit code: %d\n", WEXITSTATUS(status)); 
    			} 
    		} 
    		
    		else //错误退出,这里属于信号中断了,WEXITSTATUS一定是0了
    		{ 
    			printf("exit status = [%d]\n", WEXITSTATUS(status)); 
    		} 
    	} 
    
    return 0; 
    }
    
    

    编译输出:

    在这里插入图片描述

    判断一个脚本是否执行成功,应该满足三个条件:

    -1 != ret
    WIFEXITED(ret)为真
    0 == WEXITSTATUS(ret)

    当shell脚本不存在时:
    在这里插入图片描述

    当shell脚本不存在时、没有执行条件等,前两个条件也会成立,此时WEXITSTATUS(ret)为127,所以shell脚本中不能将127作为返回值,shell脚本中的异常返回值最好从1开始递增,成功返回0。

    system函数源码

    好了,再先看Linux版system函数的源码:

    int system(const char * cmdstring)
    {
      pid_t pid;
      int status;
    
      if(cmdstring == NULL){
          
          return (1);
      }
    
      if((pid = fork())<0){
    
            status = -1;
      }
      else if(pid == 0){
        execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
        -exit(127); //子进程正常执行则不会执行此语句
        }
      else{
            while(waitpid(pid, &status, 0) < 0){
              if(errno != EINTER){
                status = -1;
                break;
              }
            }
        }
        return status;
    }
    

    当system接受的命令为NULL时直接返回,否则fork出一个子进程,因为fork在两个进程:父进程和子进程中都返回。

    这里要检查返回的 pid,fork在子进程中返回0,在父进程中返回子进程的pid,父进程使用waitpid等待子进程结束,子进程则是调用execl来启动一个程序代替自己,execl("/bin/sh", “sh”, “-c”, cmdstring,(char*)0)是调用shell,这个shell的路径是/bin/sh,后面的字符串都是参数,然后子进程就变成了一个 shell进程,这个shell的参数是cmdstring,就是system接受的参数。

    哎呀,终于说完了!!

    总结

    system用起来看似简单,但是有很多隐藏的坑需要自己深入理解原理才能更好地使用。也可以用其他实现方式完成相同的功能。

    还有,在编写使用system函数的程序时,一定要正确地解析返回值。如果无法启动shell运行命令,system将返回127;出现不能执行system调用的其他错误时返回-1。如果system能够顺利执行,返回那个命令的退出码。

    在这里插入图片描述
    欢迎关注公众号【程序猿编码】,添加本人微信号(17865354792),回复:领取学习资料。或者回复:进入技术交流群。网盘资料有如下:

    在这里插入图片描述

    展开全文
  • 我将在下面大概介绍。 if, else, elseif, if(): endif if (表达式一) { . . . } elseif (表达式二) { . . . } else { . . . } // 或者像Python一样 if (表达式一) : . . . . . . elseif (表达式二) : . . . else :...
  • 可是对于初学Socket人来说都不太爱用Select写程序,他们只是习惯写诸如connect、accept、recv或recvfrom这样阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件发生,...
  • 2.运行Qt Creator,首先弹出的是欢迎界面,这里可以打开其自带的各种演示 程序。 3.我们用File->New 菜单来新建工程。 4.这里我们选择Qt4 Gui Application。 5.下面输入工程名和要保存到的文件夹路径。我们这里的...
  • 不同编译器给出不同的i值,有的为3,有的为4,哪个是正确的? *3.4 有这样一个巧妙的表达式:a^=b^=a^=b;它不需要临时变量就可以交换a和b的值。 3.5 可否用显式括号来强制执行我所需要的计算顺序并控制相关的副...
  • 《你必须知道495个C语言问题》

    热门讨论 2010-03-20 16:41:18
    不同编译器给出不同的i值,有的为3,有的为4,哪个是正确的? 34  *3.4 有这样一个巧妙的表达式:a^= b^= a^= b; 它不需要临时变量就可以交换a和b的值。 34 3.5 可否用显式括号来强制执行我所需要的计算顺序并...
  • 关于代码一些问题

    2020-12-01 16:57:35
    UP感兴趣话也可以去查一查main()函数返回值到底意味着什么。 </li></ol> 代码问题其实很多,限于篇幅先只写这么多。说实话,并不是想打击UP学习编程积极性。而是我相信一些建议能够帮助UP写出更好...
  • 不同编译器给出不同的i值,有的为3,有的为4,哪个是正确的? 34  *3.4 有这样一个巧妙的表达式:a^= b^= a^= b; 它不需要临时变量就可以交换a和b的值。 34 3.5 可否用显式括号来强制执行我所需要的计算顺序并...
  •  此类函数被调用执行完后将向调用者返回一个执行结果, 称为函数返回值。如数学函数即属于此类函数。 由用户定义这种要返回函数值函数,必须在函数定义和函数说明中明确返回值类型。 (2)无返回值函数  此类...
  • LINGO软件学习

    2009-08-08 22:36:50
    LINGO内置建模语言是一种描述性语言,用它可以描述现实世界中一些问题,然后再借助于LINGO求解器求解。因此,集属性值一旦在模型中被确定,就不可能再更改。在LINGO中,只有在初始部分中给出集属性值在以后...
  • 显然,我们真正需要的是能够完成动态表达式解释执行的函数库调用。以上软件没有我们需要的功能。(注:利用MATLAB的计算引擎技术与DDE数据交换技术也可以间接实现表达式动态解释执行的目的,但是其执行效率很低,并且...
  • 如果该值为false,说明程序已经处于不正确的状态下,系统将给出警告或退出。一般来说,assertion用于保证程序最基本、关键的正确性。assertion检查通常在开发和测试时开启。为了提高性能,在软件发布后,assertion...
  • 对象声明描述的是存 储在对象中的信息以及可对对象执行的操作(类方法)。对象的某些组成部分对于外界来说是可见的(公有部 分),而某些部分却是隐藏的(私有部分)。特殊的类方法(构造函数和析构函数)在对象创建和释放...
  • 对象声明描述的是存 储在对象中的信息以及可对对象执行的操作(类方法)。对象的某些组成部分对于外界来说是可见的(公有部 分),而某些部分却是隐藏的(私有部分)。特殊的类方法(构造函数和析构函数)在对象创建和释放...
  • 对象声明描述的是存 储在对象中的信息以及可对对象执行的操作(类方法)。对象的某些组成部分对于外界来说是可见的(公有部 分),而某些部分却是隐藏的(私有部分)。特殊的类方法(构造函数和析构函数)在对象创建和释放...
  • php高级开发教程说明

    2008-11-27 11:39:22
    点,这些方案一般分为两类:简短变量和函数名及谈话式变量和函数名(描述变量类型和 目的更长名字)。 某个电话目录可能是这个样子,如表1 - 1所示。 第1章认开发思想部分3 下载 表1-1 电话目录 姓名地址...
  • 你必须知道495个C语言问题(PDF)

    热门讨论 2009-09-15 10:25:47
    3, 有的为4, 哪个是正确的? . . . . . . . . . . . . . . . . . . . . . 14 3.4 这是个巧妙的表达式: a ˆ= b ˆ= a ˆ= b 它不需要临时变量就可 以交换a 和b 的值。. . . . . . . . . . . . . . . . . . . . . . ....
  • 对于没有给定数据长度情况,可以利用gets( )函数的返回值为为空(它输入数据已经输入完毕)来判断输入是否结束。例如,如下形式。 char str[足够大元素个数]; while (gets(str)!=NULL) { //每循环一次,处理...
  • jQuery详细教程

    2013-04-25 14:16:42
    语法 描述 $(this) 当前 HTML 元素 $("p") 所有 <p> 元素 $("p.intro") 所有 class="intro" <p> 元素 $(".intro") 所有 class="intro" 元素 $("#intro") id="intro" 第一个元素 $("ul li:first") 每个 <ul> ...
  • 有关类Demo,描述正确的是:(选择1项) A) 当创建一个Demo类的实例对象时,count的值为0。 B) 当创建一个Demo类的实例对象时,count的值是不确定的。 C) 超类对象中可以包含改变count 值的方法。 D) Demo的子类对象...
  • 11群是四个群中最小的群,其中继计次表位于缓冲区的首位,打完电话后查询内存发现出中继群号在内存中是正确的,取完话单后再查就不正确了。 结 论: 话单池的一个备份指针Pool_head_1和中继计次表的头指针重合,...
  • 不同编译器给出不同的结果, 有的为 3, 有的为 4, 哪个是正确的? o 4.4 这是个巧妙的表达式: a ^= b ^= a ^= b 它不需要临时变量就可以交换 a 和 b 的值。 o 4.5 我可否用括号来强制执行我所需要的计算顺序? o ...
  • java面试宝典

    2013-02-28 16:04:01
    Overloaded 方法是否可以改变返回值的类型? 10 27、描述一下JVM 加载class 文件原理机制? 10 28、char 型变量中能不能存贮一个中文汉字?为什么? 10 29、abstract class 和interface 有什么区别? 10 30、Static ...
  • C#微软培训教材(高清PDF)

    千次下载 热门讨论 2009-07-30 08:51:17
    10.3 构造函数和析构函数 .119 10.4 小 结 .122 第十一章 方 法 .124 11.1 方法声明.124 11.2 方法中参数.125 11.3 静态和非静态方法.129 11.4 方法重载.130 11.5 操作符重载.134 11.6 小 ...
  • 千方百计笔试题大全

    2011-11-30 21:58:33
    Overloaded 方法是否可以改变返回值的类型? 10 27、描述一下JVM 加载class 文件原理机制? 10 28、char 型变量中能不能存贮一个中文汉字?为什么? 10 29、abstract class 和interface 有什么区别? 10 30、Static ...
  • 他认为对于SQL学习是永无止境,相信每一个查询Oracle数据库人都需要精通SQL语言,才能写出高效查询。他参与本书编写就是为了帮助别人实现这一目标。 目录 封面 -11 封底 -10 扉页 -9 版权 -8 版权声明 -7...
  • windows 程序设计

    2011-07-24 21:16:30
    每个函数都有一个描述名称,例如CreateWindow。该函数(如您所猜想)为程序建立新窗口。所有应用程序可以使用Windows函数都在表头文件里预先声明过。 在Windows程序中,使用Windows函数的方式通常与使用如...

空空如也

空空如也

1 2 3
收藏数 46
精华内容 18
关键字:

下面对于函数返回值描述正确的是