Unix/Linux编程:执行shell命令------system

理论

程序可通过调用 system()函数来执行任意的 shell 命令

#include<stdlib.h>
/*
* 功能: system()会调用fork()产生子进程,由子进程来调用/bin/sh-c string来执行参数string字符串所代表的命令,此命令执行完后随即返回原调用的进程。在调用system()期间SIGCHLD 信号会被暂时搁置,SIGINT和SIGQUIT 信号则会被忽略。
* 返回值:
*  	如果 system()在调用/bin/sh 时失败则返回127, 其他失败原因返回-1.
*   若参数string 为空指针(NULL), 则返回非零值.
*   如果system()调用成功则最后会返回执行shell 命令后的返回值, 但是此返回值也有可能为system()调用/bin/sh 失败所返回的127, 因此最好能再检查errno 来确认执行成功.
* 说明:
* 	在编写具有 SUID/SGID 权限的程序时请勿使用system(), system()会继承环境变量, 通过环境变量可能会造成系统安全的问题.
*/
int system(const char * string);

exec的区别

  • system()和exec()都可以执行进程外的命令,system是在原进程上开辟了一个新的进程,但是exec是用新进程(命令)覆盖了原有的进程
  • system()和exec()都有能产生返回值,system的返回值并不影响原有进程,但是exec的返回值影响了原进程

popen()和 pclose()函数同样可以用来执行 shell 命令,而且还允许调用程序向命令发送输入信息,或是读取命令的输出。

函数system()创建一个子进程来运行shell,并以之执行命令command。其主要优点是简便:

  • 无需处理对fork()、exec()、wait()和exit()的调用细节
  • system()会代为处理错误和信号
  • 因为system()使用shell来执行命令,所以会在执行command之前对其进行所有的常规处理、填好以及重定向操作。

但这些优点是以低效率为代价的。使用 system()运行命令需要创建至少两个进程。一个用于运行 shell,另外一个或多个则用于 shell 所执行的命令(执行每个命令都会调用一次 exec())。如果对效率或者速度有所要求,最好还是直接调用 fork()和 exec()来执行既定程序。

system()的返回值如下:

  • 当command为NULL指针时,如果shell可用则system()返回非0值,如果不可以用则返回0。这种返回值方式源于 C 语言标准,因为并未与任何操作系统绑定,所以如果 system()运行在非 UNIX 系统上,那么该系统可能是没有shell 的。此外,即便所有UNIX 实现都有 shell,如果程序在调用system()之前又调用了 chroot(),那么 shell 依然可能无效。若 command不为 NULL,则 system()的返回值由本列表中的余下规则决定
  • 如果无法创建子进程或是无法获取其终止状态,那么 system()返回-1
  • 若子进程不能执行 shell,则 system()的返回值会与子 shell 调用_exit(127)终止时一样
  • 如果所有的系统调用都成功,system()会返回执行 command 的子 shell 的终止状态。shell 的终止状态是其执行最后一条命令时的退出状态;如果命令为信号所杀,大多数shell 会以值 128+n 退出,其中 n 为信号编号

至于调用失败是由于 system()无法执行 shell,还是 shell 以状态 127 退出(若 shell 未能发现并执行既定名称的程序,就会导致后一种情况的发生),(通过 system()的返回值)是无法区分的

在最后两种情况中,system()的返回值与 waitpid()所返回的等待状态(wait status)形式相同。

在设置用户 ID(set-user-ID)和组 ID(set-group-ID)程序中避免使用 system()

  • 当设置了用户 ID 和组 ID 的程序在特权模式下运行时,绝不能调用system()。即便此类程序并未允许用户指定需要执行的命令文本,鉴于 shell 对操作的控制有赖于各种环境变量,故而使用 system()会不可避免地给系统带来安全隐患。
  • 应用需要加载其他程序时,为确保安全过关,应当直接调用 fork()和 exec()系函数(execlp()和 execvp()除外)之一。

实践

#include <stdlib.h>
int main(void)
{
    system("ls -al /etc/passwd /etc/shadow");
    return 0;
}

在这里插入图片描述

#include "apue.h"


int main(void)
{
    int		status;

    if ((status = system("date")) < 0)
        err_sys("system() error");

    pr_exit(status);

    if ((status = system("nosuchcommand")) < 0)
        err_sys("system() error");

    pr_exit(status);

    if ((status = system("who; exit 44")) < 0)
        err_sys("system() error");

    pr_exit(status);

    exit(0);
}

在这里插入图片描述
当然,我们可以自己实现一个system

// 一个缺乏信号处理的 system()实现 
#include	<sys/wait.h>
#include	<errno.h>
#include	<unistd.h>

int
system(const char *cmdstring)	/* version without signal handling */
{
	pid_t	pid;
	int		status;

	if (cmdstring == NULL)
		return(1);		/* always a command processor with UNIX */

	if ((pid = fork()) < 0) {
		status = -1;	/* probably out of processes */
	} else if (pid == 0) {				/* child */
		execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
		_exit(127);		/* execl error */
	} else {							/* parent */
		while (waitpid(pid, &status, 0) < 0) { //(使用 wait()并不合适,因为 wait()等待的是任一子进程,因而无意间所获取的子进程状态可能属于其他子进程。)
			if (errno != EINTR) {
				status = -1; /* error other than EINTR from waitpid() */
				break;
			}
		}
	}

	return(status);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值