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