理论

我们先看下man手册是怎么说的man system

RETURN VALUE
       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 speci‐
       fied  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.

机翻一下
返回值
错误时返回的值为-1(例如,fork(2)failed),否则返回命令的状态。后一个返回状态采用wait(2)中指定的格式。因此,命令的退出代码将是WEXITSTATUS(status)。在无法执行/bin/sh的情况下,退出状态将是退出(127)的命令的状态。
如果command的值为空,如果shell可用,system()将返回非零,如果不可用,则返回零。
system()不影响任何其他子项的等待状态。


参考:System函数返回值 和popen system是个综合的操作,分解开来看就是相当于执行了
1 fork 生成一个子进程。
2 在子进程执行 execl("/bin/sh",“sh”,"-c" command,(char*)0);
3 waitpid

返回值:
1 如果fork失败了,或者waitpid返回除了EINTR之外的错误,system返回 -1;
2 execl执行失败,其返回值如同shell执行了"exit(127)" 一样。
3 如果上述三步都执行成功,那么,system返回值是shell的终止状态。

如果/bin/sh拉起shell命令失败,或者是shell命令没有正常执行 (比如命令根本就是非法的命令),那么,将原因填入status的8~15位。
一般错误来说
status = 256 * 信号编码
WEXITSTATUS(status) = 128 + 信号编号。

实测

下面给出一个程序。system函数传入一串乱七八糟的字符,我们看下结果

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/types.h>


int main(void) 
{
	pid_t status = system("wrqwqwd");

	if (-1 == status)
	{
		printf("返回-1,fork失败了,或者waitpid返回除了EINTR之外的错误\n");
		
		return 0;
	}
	else
	{
		printf("exit status value = [0x%x]\n", status);

		if (WIFEXITED(status))
		{
			if (0 == WEXITSTATUS(status))
			{
				printf("0 == WEXITSTATUS(status),运行完毕\n");
			}
			else
			{
				printf("0 != WEXITSTATUS(status),script exit code: %d\n", WEXITSTATUS(status));
				
				return 0;
			}
		}
		else
		{
			printf("WIFEXITED(status)为假,exit status = [%d]\n", WEXITSTATUS(status));
			
			return 0;
		}
	}
	
	return 0;
}

system函数返回值,Linux_#include


16进制 7f00 转 10进制 为 32515。WEXITSTATUS(status)为127

如果execl执行失败,也即command没有顺利执行,比如被信号中断,或者command命令根本不存在,system()函数返回127.

我们传入 ls 试试

pid_t status = system("ls");

system函数返回值,Linux_C_02


status 为 0,WEXITSTATUS(status)为0

执行成功,exit 0

我们执行下没有执行权限的shell脚本

pid_t status = system("./start.sh");

system函数返回值,Linux_#include_03


WEXITSTATUS(status)为126

shell脚本没有执行权限。

我们执行下有执行权限但是错误的shell脚本

start.sh

while :
do
	#echo 1
done

system函数返回值,Linux_#include_04


status为 ox200 即 512,WEXITSTATUS(status)为 2.

具体原因参考:System函数返回值 和popen

我们可以看到,直接在终端执行sleep 7,然后用Ctrl+C发送SIGINT信号,异常退出,shell返回值为130,130的原因,
APUE上解释了,因为SIGINT 等于2,终止状态是128+信号编号,所以为130.
按照APUE上,我们的system调用应该将SIGINT忽略掉,然后正常返回,同时返回值为130,但是实际上LINUX下并不是这样 
的。实际上system的返回值为2,并且异常退出了。

我们执行下有执行权限但是死循环的shell脚本

start.sh

while true
do
	echo 1;
done

system函数返回值,Linux_shell脚本_05


同样是死循环,然后我按了ctrl+C,发送 SIGINT信号。status为2

当我们运行死循环的shell脚本,然后kill 它

system函数返回值,Linux_C_06


kill -9,status为9

同理,测试

system函数返回值,Linux_C_07


system函数返回值,Linux_#include_08


system函数返回值,Linux_linux_09

命令kill -l

system函数返回值,Linux_shell脚本_10


常见的信号

system函数返回值,Linux_linux_11


system函数返回值,Linux_shell脚本_12

简单来说

system 返回值,调用函数后的返回值
shell 返回值,指脚本执行后的返回值

阶段1:创建子进程等准备工作。如果失败,返回-1
阶段2:调用/bin/sh拉起脚本,如果拉起失败或者shell未正常执行,原因值被写入ret中
阶段3:如果shell脚本执行成功,shell脚本的返回值写入ret中
WIFEXITED 用来判断阶段二的返回值
WEXITSTATUS 用来判断阶段三的返回值
判断一个脚本是否执行成功,应该满足三个条件:
    -1 != ret
    WIFEXITED(ret)为真
    0 == WEXITSTATUS(ret)

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

一般错误来说
status = 256 * 信号编码
WEXITSTATUS(status) = 128 + 信号编号。

参考文章

Linux信号列表System函数返回值 和popenLinux system 函数

补充说明

man system

SYSTEM(3)                  Linux Programmer's Manual                 SYSTEM(3)

NAME
       system - execute a shell command

SYNOPSIS
       #include <stdlib.h>

       int system(const char *command);

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

RETURN VALUE
       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.
ATTRIBUTES
       For   an   explanation   of   the  terms  used  in  this  section,  see
       attributes(7).

       ┌──────────┬───────────────┬─────────┐
       │Interface │ Attribute     │ Value   │
       ├──────────┼───────────────┼─────────┤
       │system()  │ Thread safety │ MT-Safe │
       └──────────┴───────────────┴─────────┘
CONFORMING TO
       C89, C99, POSIX.1-2001.

NOTES
       If the _XOPEN_SOURCE feature test macro is  defined  (before  including
       any header files), then the macros described in wait(2) (WEXITSTATUS(),
       etc.) are made available when including <stdlib.h>.

       As mentioned, system() ignores SIGINT and SIGQUIT.  This may make  pro‐
       grams  that  call it from a loop uninterruptible, unless they take care
       themselves to check the exit status of the child.  E.g.

           while (something) {
               int ret = system("foo");

               if (WIFSIGNALED(ret) &&
                   (WTERMSIG(ret) == SIGINT || WTERMSIG(ret) == SIGQUIT))
                       break;
           }
        Do not use system() from a program  with  set-user-ID  or  set-group-ID
       privileges, because strange values for some environment variables might
       be used to subvert system integrity.  Use the exec(3) family  of  func‐
       tions  instead,  but not execlp(3) or execvp(3).  system() will not, in
       fact, work properly from  programs  with  set-user-ID  or  set-group-ID
       privileges  on systems on which /bin/sh is bash version 2, since bash 2
       drops privileges on startup.  (Debian uses a modified bash  which  does
       not do this when invoked as sh.)

       In  versions  of  glibc before 2.1.3, the check for the availability of
       /bin/sh was not actually performed if command was NULL; instead it  was
       always  assumed to be available, and system() always returned 1 in this
       case.  Since glibc 2.1.3, this check is performed because, even  though
       POSIX.1-2001  requires  a conforming implementation to provide a shell,
       that shell may not be available or executable if  the  calling  program
       has   previously   called   chroot(2)   (which   is  not  specified  by
       POSIX.1-2001).

       It is possible for the shell command to return 127, so that code is not
       a sure indication that the execve(2) call failed.
SEE ALSO
       sh(1), signal(2), wait(2), exec(3)

COLOPHON
       This  page  is  part of release 3.53 of the Linux man-pages project.  A
       description of the project, and information about reporting  bugs,  can
       be found at http://www.kernel.org/doc/man-pages/.

                                  2010-09-10                         SYSTEM(3)