接管子程式的標準輸入輸出

liiinuuux發表於2016-06-24
有這樣一種需求,希望讓程式呼叫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/,如需轉載,請註明出處,否則將追究法律責任。

相關文章