接管子进程的标准输入输出

有这样一种需求,希望让程序调用fdisk、mysql、sqlplus这种可以交互的命令,执行一些操作后分析输出结果,然后再根据这些结果决定如何执行下面的命令。这样就可以代替人工的判断和键盘输入。
这种需求可以用管道来接管子进程的标准输入输出,甚至标准错误来完成。
具体方法是
1 用pipe(int)创建两个管道,分别用于连接程序和要调用的命令的标准输入和标准输出。暂时叫他们“输入管道”和“输出管道”。
2 fork()一下,在父进程和子进程分别关闭两个管道的一端。
    输入管道对主进程相当于键盘,因此关闭管道的读端,而子进程关闭写端。
    输出管道对主进程相当于显示器,因此关闭管道的写端,而子进程关闭读端。
3 在子进程用dup2()。
    将子进程的标准输入复制为输入管道的读端。
    将子进程的标准输出复制为输出管道的写端。
4 在子进程调用execl来执行需要的命令。
5 在父进程写入输入管道的写端,然后读取输出管道的读端。

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <errno.h>
  4. #include <stdlib.h>
  5. #include <fcntl.h>
  6. #include <string.h>

  7. int lsexec2(int* in, int* out, char* path, char* cmd, char* arg)
  8. {
  9.     int fd_i[2];
  10.     int fd_o[2];
  11.     char buf[BUFSIZ];
  12.     int len = 0;

  13.     // 创建两个管道,fd_i用于接管子进程的标准输入,fd_o用于标准输出
  14.     pipe(fd_i);
  15.     pipe(fd_o);

  16.     pid_t p = fork();
  17.     if(p < 0) return -1;
  18.     if(p > 0)
  19.     { //parent
  20.         close(fd_i[0]); // 关闭输入管道的读端
  21.         *in = fd_i[1]; // 将写端暴露给调用者,这样调用者可以给子进程发送命令
  22.         close(fd_o[1]); // 关闭输出管道的写端
  23.         *out = fd_o[0]; // 将读端暴露给调用者,这样调用者可以读取子进程的输出
  24.         return 0;
  25.     }
  26.     else
  27.     { //child
  28.         close(fd_i[1]); // 关闭输入管道的写端
  29.         dup2(fd_i[0], STDIN_FILENO); // 将标准输入复制到输入管道的读端,这样就可以把父进程的输入转接到标准输入
  30.         close(fd_o[0]); // 关闭输出管道的读端
  31.         dup2(fd_o[1], STDOUT_FILENO); // 将标准输出复制到输出管道的写端,这样父进程就可以通过读端读取标准输出的内容
  32.         return execl(path, cmd, arg, (int*)0); // 执行需要的命令
  33.     }
  34. }

  35. int main(void)
  36. {
  37.     int in, out;
  38.     char cmd[BUFSIZ];
  39.     memset(cmd, '\0', BUFSIZ);

  40.     if(lsexec2(&in, &out, "/sbin/fdisk", "fdisk", "/dev/sda") == -1)
  41.     {
  42.         printf("%s\n", strerror(errno));
  43.         exit(1);
  44.     }
  45.     strcpy(cmd, "p\nm\n");
  46.     write(in, cmd, strlen(cmd)); // 向in写入指令,模拟人工键盘输入
  47.     usleep(10000); // 由于只是demo,简单等待10毫米,让子程序输出完整。正常应该是新开一个进程持续等待子进程输出。
  48.     char buf[BUFSIZ];
  49.     int len = read(out, buf, BUFSIZ); // 读取out的内容,让程序看到命令的输出内容
  50.     printf("%s", buf);
  51.     return 0;
  52. }




执行效果
[root@server2 c]# ./a

WARNING: DOS-compatible mode is deprecated. It's strongly recommended to
         switch off the mode (command 'c') and change display units to
         sectors (command 'u').

Command (m for help):
Disk /dev/sda: 34.4 GB, 34359738368 bytes
255 heads, 63 sectors/track, 4177 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x000ac07a

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1               1        1045     8388608   82  Linux swap / Solaris
/dev/sda2   *        1045        4178    25164800   83  Linux

Command (m for help): Command action
   a   toggle a bootable flag
   b   edit bsd disklabel
   c   toggle the dos compatibility flag
   d   delete a partition
   l   list known partition types
   m   print this menu
   n   add a new partition
   o   create a new empty DOS partition table
   p   print the partition table
   q   quit without saving changes
   s   create a new empty Sun disklabel
   t   change a partition's system id
   u   change display/entry units
   v   verify the partition table
   w   write table to disk and exit
   x   extra functionality (experts only)

Command (m for help): [root@server2 c]#





来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/26239116/viewspace-2120901/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/26239116/viewspace-2120901/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值