C:执⾏shell命令
1.system函数
函数说明:
system()会调⽤fork()产⽣⼦进程,由⼦进程来调⽤/bin/sh-cstring来执⾏参数string字符串所代表的命令,此命令执⾏完后随即返回原调⽤的进程。
在调⽤system()期间SIGCHLD 信号会被暂时搁置,SIGINT和SIGQUIT 信号则会被忽略。
返回值:
如果system()在调⽤/bin/sh时失败则返回127,其他失败原因返回-1。
若参数string为空指针(NULL),则返回⾮零值。
如果system()调⽤成功则最后会返回执⾏shell命令后的返回值,但是此返回值也有
可能为system()调⽤/bin/sh失败所返回的127,因此最好能再检查errno 来确认执⾏成功。
附加说明:
在编写具有SUID/SGID权限的程序时请勿使⽤system(),system()会继承环境变量,通过环境变量可能会造成系统安全的问题。
判断返回值:
system函数对返回值的处理,涉及3个阶段:
阶段1:创建⼦进程等准备⼯作。如果失败,返回-1。
阶段2:调⽤/bin/sh拉起shell脚本,如果拉起失败或者shell未正常执⾏结束(参见备注1),原因值被写⼊到status的低8~15⽐特位中。system的man中只说明了会写了127这个值,但实测发现还会写126等值。
阶段3:如果shell脚本正常执⾏结束,将shell返回值填到status的低8~15⽐特位中。
备注1:
只要能够调⽤到/bin/sh,并且执⾏shell过程中没有被其他信号异常中断,都算正常结束。
⽐如:不管shell脚本中返回什么原因值,是0还是⾮0,都算正常执⾏结束。即使shell脚本不存在或没有执⾏权限,也都算正常执⾏结束。 如果shell脚本执⾏过程中被强制kill掉等情况则算异常结束。
如何判断阶段2中,shell脚本是否正常执⾏结束呢?系统提供了宏:WIFEXITED(status)。如果WIFEXITED(status)为真,则说明正常结束。
如何取得阶段3中的shell返回值?你可以直接通过右移8bit来实现,但安全的做法是使⽤系统提供的宏:WEXITSTATUS(status)。
由于⼀般在shell脚本中会通过返回值判断本脚本是否正常执⾏,如果成功返回0,失败返回正数。
所以综上,判断⼀个system函数调⽤shell脚本是否正常结束的⽅法应该是如下3个条件同时成⽴:
(1)-1 != status
(2)WIFEXITED(status)为真
(3)0 == WEXITSTATUS(status)
注意:
根据以上分析,当shell脚本不存在、没有执⾏权限等场景下时,以上前2个条件仍会成⽴,此时WEXITSTATUS(status)为
127,126等数值。
所以,我们在shell脚本中不能将127,126等数值定义为返回值,否则⽆法区分中是shell的返回值,还是调⽤shell脚本异常的原因值。shell脚本中的返回值最好多1开始递增。
int ret = system("./reset_mt_fec.sh");
if (WIFEXITED(ret))
{
printf("subprocess exited, exit code: %d\n", WEXITSTATUS(ret));
if (0 == WEXITSTATUS(ret))
{
// if command returning 0 means succeed
// printf("command succeed");
printf("%s:the device(%d) is resetted\n", __func__,bbdevId);
getNowTime();
}
else
{
printf("command not found: %s\n",strerror(WEXITSTATUS(ret)));
if(127 == WEXITSTATUS(ret))
{
printf("command not found: %s\n",strerror(WEXITSTATUS(ret)));
// return WEXITSTATUS(ret);
}
else
{
printf("command failed: %s\n", strerror(WEXITSTATUS(ret)));
// return WEXITSTATUS(ret);
}
}
}
else
{
printf("subprocess exit failed");
}
2.popen(建⽴管道I/O)
函数说明:
popen()会调⽤fork()产⽣⼦进程,然后从⼦进程中调⽤/bin/sh -c来执⾏参数command的指令。参数type可使⽤“r”代表读
取,“w”代表写⼊。依照此type值,popen()会建⽴管道连到⼦进程的标准输出设备或标准输⼊设备,然后返回⼀个⽂件指针。随后进程便可利⽤此⽂件指针来读取⼦进程的输出设备或是写⼊到⼦进程的标准输⼊设备中。
此外,所有使⽤⽂件指针(FILE*)操作的函数也都可以使 ⽤,除了fclose()以外。
返回值:
若成功则返回⽂件指针,否则返回NULL,错误原因存于errno中。错误代码 EINVAL参数type不合法。
注意事项 在编写具SUID/SGID权限的程序时请尽量避免使⽤popen(),popen()会继承环境变量,通过环境变量可能会造成系统安全的问题。 这⾥只能看到管道是否建⽴成功,不能判断命令是否执⾏成功。
#include<stdio.h>
main()
{
FILE * fp;
char buffer[80];
fp=popen(“cat /etc/passwd”,”r”);
fgets(buffer,sizeof(buffer),fp);
printf(“%s”,buffer);
pclose(fp);
}
3. 使⽤vfork()新建⼦进程,然后调⽤exec函数族
函数说明:
execv会停⽌执⾏当前的进程,并且以progname应⽤进程替换被停⽌执⾏的进程,进程ID没有改变。 exec函数族⼀般没有返回值,如果有返回值表⽰执⾏失败shell命令属于什么语言
#include<unistd.h>
main()
{
char * argv[ ]={“ls”,”-al”,”/etc/passwd”,(char*) };
if(vfork() = =0)
{
execv(“/bin/ls”,argv);
}else{
printf(“This is the parent process\n”);
}
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论