Linux下正确运用system函数以及理清返回值问题

在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),回复:领取学习资料。或者回复:进入技术交流群。网盘资料有如下:

在这里插入图片描述

<think>好的,我需要帮助用户理解接口函数的概念、定义及其在编程中的作用。首先,我得回忆一下用户提供的引用内容,看看有没有相关的信息可以引用。用户提到了函数式接口,这在引用[1]、[3]中有提到,特别是引用[3]提到函数式接口是带有@FunctionalInterface注解的接口,只能有一个抽象方法。这可能与接口函数相关,但需要进一步理清。 接口函数可能指的是函数式接口中的方法,或者更广义的接口中定义的函数。在面向对象编程中,接口通常定义一组方法,而实现类需要提供这些方法的具体实现。函数式接口则特指只有一个抽象方法的接口,可以用lambda表达式或方法引用实现,如引用[3]所述。 接下来,用户的问题是关于接口函数的定义和作用。我需要结合面向对象和函数式编程两方面来回答。首先定义接口函数,可能是指接口中声明的函数(方法),或者函数式接口中的那个单一方法。然后解释其作用,如解耦、多态、支持回调等。 需要确保回答结构清晰,分点说明,并引用提供的资料。例如,引用[1]提到函数式接口是函数式编程的核心概念,引用[3]说明函数式接口与lambda的关系,引用[4]展示函数作为参数传递的例子,引用[5]可能涉及函数签名和类型检查。 另外,用户可能混淆了接口函数函数式接口,需要明确区分。接口中的函数一般是抽象方法,而函数式接口特指只有一个抽象方法的接口。同时,函数作为一等公民,可以作为参数或返回值,如引用[4]中的例子。 需要检查是否有其他引用相关内容,比如引用[5]中的函数签名,可能用于说明接口函数的类型定义。最后,生成相关问题时要围绕接口函数函数式接口的应用、区别、实例等。</think>### 接口函数的定义及其作用 #### 1. **接口函数的定义** **接口函数**(Interface Function)在编程中有两层含义: - **面向对象编程(OOP)中的接口函数**:指接口(Interface)中定义的抽象方法。接口是一种规范,声明了一组方法签名,但不提供具体实现。例如: ```java public interface Calculator { int add(int a, int b); // 接口函数声明 } ``` 实现类必须提供这些方法的具体实现。 - **函数式编程中的函数式接口**:特指**仅包含一个抽象方法**的接口(如Java中的`@FunctionalInterface`),支持通过Lambda表达式或方法引用实现函数逻辑[^3]。例如: ```java @FunctionalInterface public interface Runnable { void run(); // 唯一的抽象方法 } ``` #### 2. **接口函数的作用** - **解耦与多态** 接口函数将方法的定义与实现分离,使代码模块化。调用方只需依赖接口,无需关心具体实现类,从而提高灵活性和可维护性[^1]。 - **支持回调与高阶函数** 接口函数可作为参数传递或返回值,实现回调机制。例如引用[4]中的Python代码,将函数`add`作为参数传递给`gj`函数: ```python def gj(f, a, b): f(a, b) gj(add, 2, 3) # 输出5 ``` - **实现函数式编程范式** 函数式接口通过Lambda表达式简化代码。例如Java中`Runnable`接口的Lambda实现: ```java Runnable task = () -> System.out.println("Task executed"); ``` - **类型安全与工具支持** 函数签名(如引用[5]中的`max :: Int -> Int -> Int`)定义了函数的输入输出类型,辅助编译器进行类型检查,避免运行时错误[^5]。 #### 3. **与其他概念的对比** - **普通类方法**:接口函数只有声明,无实现;类方法需具体实现。 - **抽象类**:接口函数不包含状态(字段),而抽象类可以包含字段和部分实现。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值