一、system函数()
详情看《unix环境高级编程》,8.13system函数。
在程序中执行一个命令字符串很方便。例如,假定要将时间和日期放到一个文件中,则可使用6 . 9节中的函数实现这一点。调用time得到当前日历时间,接着调用localtime将日历时间变换为年、月、日、时、分、秒、周日形式,然后调用strftime对上面的结果进行格式化处理,最后将结果写到文件中。但是用下面的system函数则更容易做到这一点。
system(“date > file”);
ANSI C定义了system函数,但是其操作对系统的依赖性很强。
因为system不属于操作系统界面而是 shell界面,所以POSIX.1没有定义它,POSIX.2则正在对其进行标准化。下列说明与POSIX.2标准的草案11.2相一致。
#include <stdlib.h>
int system(const char *cmdstring);
Returns: (see below)
如果cmdstring是一个空指针,则仅当命令处理程序可用时, system返回非0值,这一特征可以决定在一个给定的操作系统上是否支持system函数。在UNIX中,system总是可用的。
因为system在其实现中调用了fork、exec和waitpid,因此有三种返回值:
(1) 如果fork失败或者waitpid返回除EINTR之外的出错,则system返回-1,而且errno中设置了错误类型。
(2) 如果exec失败(表示不能执行shell ),则其返回值如同shell执行了exit(127)一样。
(3) 否则所有三个函数( fork,exec和waitpid )都成功,并且system的返回值是shell的终止状态,其格式已在waitpid中说明。
code:demo27.c
CLC@Embed_Learn:~/LinuxLearn/PID$ cat demo27.c
#include <stdio.h>
#include <unistd.h>
/
int main()
{
printf("NOW TIME:\n");
char *argv[]={"data",NULL};
if(system("date") == -1){
printf("Not Find File\n");
perror("ERROR Reason:");
}
printf("SEEK!!!\n");//调用system会继续执行,而exec族不会
return 0;
}
CLC@Embed_Learn:~/LinuxLearn/PID$ gcc demo27.c
CLC@Embed_Learn:~/LinuxLearn/PID$ ./a.out
NOW TIME:
2021年 05月 19日 星期三 20:13:04 CST
SEEK!!!
二、popen函数
函数名:
popen, pclose − 与进程之间的管道流
原型:
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
glibc的特性测试宏要求(参见feature_test_宏(7)):
popen(), pclose():
_POSIX_C_SOURCE >= 2 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE
DESCRIPTION描述
- popen()函数通过创建管道、分叉和调用shell来打开进程。由于管道的定义是单向的,类型参数只能指定读或写,而不能同时指定读或写;相应的,产生的流是只读的或只写的。
- command参数是一个指针,指向包含shell命令行的以空结束的字符串。这个命令使用−c标志传递到/bin/sh;如果有解释,则由shell执行。type参数是一个指向以空结束的字符串的指针,该字符串必须包含字母’r’(用于读取)或字母’w’(用于写入)。因为glibc 2.9,这个参数可以额外包含字母’e’,这导致在底层文件描述符上设置执行时关闭标志(FD_CLOEXEC);的描述O_CLOEXEC标志在开放(2)的原因,这可能是有用的。
- popen()的返回值在各个方面都是一个正常的标准I/O流,除了它必须用pclose()而不是fclose(3)关闭。对这样一个流的写入写入命令的标准输入;该命令的标准输出与调用popen()的进程的输出相同,除非该命令本身改变了这一点。相反,从“popened”流读取命令的标准输出,并且命令的标准输入与调用popen()的进程的标准输入相同。
- 注意输出popen()流在默认情况下是完全缓冲的。
- pclose()函数等待相关进程终止,并返回wait4(2)返回的命令退出状态。
返回值:
- 如果fork(2)或pipe(2)调用失败,或者不能分配内存,popen()函数返回NULL。
- 如果wait4(2)返回错误,或者检测到其他错误,则pclose()函数返回−1。
错误:
- 如果内存分配失败,popen()函数不会设置errno。如果底层的fork(2)或pipe(2)失败,errno将被适当设置。如果类型参数无效,并且检测到此条件,errno将被设置为EINVAL。
- 如果pclose()不能获得子状态,errno被设置为ecchild。
符合(兼容于):
posix . 1的授权- 2001。
type的’e’值是一个Linux扩展
BUGS
- 由于打开用于读取的命令的标准输入与调用popen()的进程共享其seek偏移量,如果原始进程已经进行了缓冲读取,则命令的输入位置可能不像预期的那样。类似地,打开用于写入的命令的输出可能会与原始进程的输出混杂在一起。后者可以通过在popen()之前调用fflush(3)来避免
- shell执行失败与shell执行命令失败或命令立即退出没有区别。唯一的提示是退出状态为127。
参考
sh(1), fork(2), pipe(2), wait4(2), fclose(3), fflush(3), fopen(3), stdio(3), system(3)
code:demo28.c
#include <stdio.h>
#include <unistd.h>
// size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
// FILE *popen(const char *command, const char *type);
int main()
{
FILE *fp;
char ret[1024] = {0};
fp = popen("ps","r");
int read_byt = fread(ret,1,1024,fp);
printf("read %d byte,result:%s\n",read_byt,ret);
return 0;
}
运行结果:
CLC@Embed_Learn:~/LinuxLearn/PID$ gcc demo28.c
CLC@Embed_Learn:~/LinuxLearn/PID$ ./a.out
read 171 byte,result: PID TTY TIME CMD
46666 pts/4 00:00:01 bash
48141 pts/4 00:00:00 a.out
49185 pts/4 00:00:00 a.out
49186 pts/4 00:00:00 sh
49187 pts/4 00:00:00 ps
CLC@Embed_Learn:~/LinuxLearn/PID$ cat demo29.c
#include <stdio.h>
#include <unistd.h>
// size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
// FILE *popen(const char *command, const char *type);
int main()
{
FILE *fp;
char ret[1024] = {0};
fp = popen("ps","r");
int read_byt = fread(ret,1,1024,fp);
// printf("read %d byte,result:%s\n",read_byt,ret);
return 0;
}
CLC@Embed_Learn:~/LinuxLearn/PID$ gcc demo29.c
CLC@Embed_Learn:~/LinuxLearn/PID$ ./a.out
CLC@Embed_Learn:~/LinuxLearn/PID$
三、小结
- system 通过调用exec族实现,直接暴力执行,调用成功不返回。
- popen调用不打印,看demo29.c,存放在FILE *stread(流)中。