linux下的C开发14,可执行程序如何传递参数?模拟shell执行命令

上一节介绍了 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 传递参数给可执行程序。

阅读更多:   Linux笔记
已有 2 条评论
  1. 为啥通过execvp模拟shell没有把"./a.out"打印出来?

  2. 为啥通过execvp模拟shell没有把"./a.out"打印出来?我的邮箱是2455251605@qq.com,谢谢!

添加新评论

icon_redface.gificon_idea.gificon_cool.gif2016kuk.gificon_mrgreen.gif2016shuai.gif2016tp.gif2016db.gif2016ch.gificon_razz.gif2016zj.gificon_sad.gificon_cry.gif2016zhh.gificon_question.gif2016jk.gif2016bs.gificon_lol.gif2016qiao.gificon_surprised.gif2016fendou.gif2016ll.gif