上一节介绍了 linux 中的文件类型,并在文章最后使用 C语言编写了程序,该程序能够接受一个文件名参数,并打印出该文件的类型。不知道大家如何,反正我当初学编程时,发现(编译后的)可执行程序居然也能像(编写代码阶段的)函数一样接收参数,觉得太神奇了。
小编刚学习 C语言时,是在 windows 中学习的,编译出的程序都是双击执行,从来没想过编译后的可执行程序还能接收参数。
可执行程序怎样接收参数的呢?
事实上,不仅仅是上一节的C语言程序能够接受参数,linux 中的大部分 shell 命令都是可以接收参数的,例如 ls 命令可以接收 -l 参数,输出更加详细的文件信息:
# ls -l
total 24
-rwxr-xr-x 1 root root 13205 Dec 22 18:26 a.out
-rw-r--r-- 1 root root 427 Dec 21 21:01 main.c
-rw-r--r-- 1 root root 1653 Dec 22 18:26 test.c
执行删除命令 rm 也需要指定文件名:
# rm a.out
ls 和 rm 本质上也是 linux 中的可执行程序,linux 中的大部分程序都是由 C语言编写的。C语言程序总是有个入口函数(常常是 main 函数),入口函数的原型如下:
int main(int argc, char* argv[]);
其中 argc 是命令参数的数目,argv 则是指向参数的各个指针构成的数组。在 shell 中输入命令(其实就是可执行程序)后,shell 会调用 exec 函数族执行该命令。输入 man 命令查询 exec 函数族的手册:
容易看出,exec 函数族在创建新进程执行命令时,允许传入若干参数给命令。
这就明白了,shell 也是一个进程,它会记录用户输入的命令和命令参数,在调用 exec 函数族执行命令时,把记录的参数传递给命令。
C语言模拟 shell 传递参数给可执行程序
知道了 linux 中可执行程序接收参数的原理后,编写程序模拟 shell 传递参数就不难了。我们先编写一个能够接受参数的C语言程序:
#include <stdio.h>
int main(int argc, char* argv[])
{
int i = 0;
printf("\n");
for(i=0; i<argc; i++)
printf("\t%s\n", argv[i]);
printf("\n");
return 0;
}
程序很简单,就是将接收到的参数打印出来。编译并执行之,得到如下结果:
# gcc t.c
# ./a.out
./a.out
# ./a.out hello embedTime
./a.out
hello
embedTime
#
因为规定所有有效参数之后 argv 指向 NULL,所以遍历 C语言程序接收到的所有参数时,上面的 for 循环也可以写成:for(i=0; NULL!=argv[i]; i++)。
现在再编写一个程序,在这个程序中,我们将使用 exec 函数族模拟 shell 执行由 t.c 编译而来的可执行程序 a.out,并向其传送指定的参数。
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int main()
{
char* filename = "./a.out";
printf("now run %s\n", filename);
char *argv[] = {"hello","embedTime",NULL};
int ret = execvp(filename, argv);
if(ret<0)
printf("ret: %d %s\n", ret, strerror( errno ));
printf("sim shell exit.\n\n");
return 0;
}
代码很简单,将“hello”和“embedTime”两个参数填入 argv 里,再调用 execvp 函数模拟 shell 执行 a.out 程序,编译执行:
模拟 shell 的 sim.out 程序的确成功把上面两个参数传递给 a.out 了,但是 sim.out 程序最后的 “sim shell exit.”信息却没有输出。这是因为 sim.out 进程被 a.out 进程替代了。
编写完美模拟 shell 的C语言程序
这样模拟 shell 并不完美,总不至于为了执行一个进程,shell 都得退出吧?还记得第 11 节介绍的多进程 C语言程序编写方法吗?为了完美模拟 shell,可以 fork 出一个子进程,在子进程中执行 a.out ,请看如下代码:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
int main()
{
char* filename = "./a.out";
pid_t pid = fork();
if(0==pid){
printf("now run %s\n", filename);
char *argv[] = {"hello","embedTime",NULL};
int ret = execvp(filename, argv);
if(ret<0)
printf("ret: %d %s\n", ret, strerror( errno ));
}else{
printf("\nsim shell running...\n");
int status;
wait(&status);
printf("sim shell exit.\n\n");
}
return 0;
}
现在再编译执行,发现我们不仅成功模拟了 shell 执行 a.out ,而且 a.out 执行时,sim.out 也没有被替换,终于较为完美的模拟了 shell 传递参数给可执行程序。
为啥通过execvp模拟shell没有把"./a.out"打印出来?
为啥通过execvp模拟shell没有把"./a.out"打印出来?我的邮箱是2455251605@qq.com,谢谢!