歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

用C實現的一個Bash腳本

題目:設當前目錄下有三個二進制格式的可執行程序,文件名分別為cmd1,cmd2和cmd3.

(1).在Bash下的下列命令完成什麼功能?

  ./cmd1 2>&1 | ./cmd2 > r.txt; ./cmd3

(2).編寫C語言源程序完成與(1)相同的功能

提示:exec系統調用可用execlp("./cmd1","cmd1",(char *)0);

打開文件用 fd = open("r.txt", O_CREAT | O_TRUNC | O_WRONLY,0666);

dup2系統調用法為dup2(src_fd,dst_fd);

創建管道用pipe(int fds[2]);

等待子進程結束用wait(int staloc);

 

答:首先說明,0,1,2三個文件說明符分別代表stdin, stdout和stderr。

2>&1的意思是將cmd1的標准輸出流和標准錯誤流合並到標准輸出流中;

接下來通過管道將剛才的結果輸出到cmd2的輸入中;

cmd2>r.txt將cmd2的輸出結果重定向到文件r.txt中;

最後是執行cmd3,這與前面的一切都沒有關系。(有一點尚存疑問,那就是cmd3是否和cmd1一樣,都將stdout和stderr合體了,由於我沒有測試錯誤的用例,因此這裡不是標答。)

(2)這題才是重點。首先說明管道和重定向的一些知識。

 

  【 a. 左邊的命令應該有標准輸出 | 右邊的命令應該接受標准輸入

      左邊的命令應該有標准輸出 > 右邊只能是文件

      左邊的命令應該需要標准輸入 < 右邊只能是文件

   b. 管道觸發兩個子進程執行"|"兩邊的程序;而重定向是在一個進程內執行。】[1]

做這道題之前我接觸這方面比較少,下面這段代碼參考了不少東西,但是不知來源,沒法列舉了。這段代碼是在一個同學給我的源文件的基礎上修改的,因為之前我基本上不會用管道 ( ̄. ̄)

為了能測試,我把虛無缥缈的三個cmd換成了活生生的grep,wc和who   ( ̄0  ̄)y

  1. /******************************************************* 
  2.  使用fork(), exec(), dup2(), pipe() ,open()系統調用 
  3.  完成與下列shell命令等價的功能。 
  4.     cmd1:grep -v usr /etc/passwd 
  5.     cmd2:wc -l 
  6.     cmd3:who 
  7.     grep -v usr /etc/passwd 2>&1 | wc -l > r.txt;who 
  8. ********************************************************/ 
  9. #include <stdio.h> 
  10. #include <fcntl.h> 
  11. #include <unistd.h> 
  12. int main() 
  13.     int pfd[2]; 
  14.  
  15.     /* 建立管道 */ 
  16.     pipe(pfd); 
  17.      
  18.     if (fork()) /* parent */ 
  19.     { 
  20.         if(!fork()) /* child 2 */ 
  21.         {    
  22.             /* 將標准輸出和標准錯誤重定向到管道寫端口pfd[1] */ 
  23.             dup2(pfd[1], 1); 
  24.             dup2(pfd[1], 2); 
  25.             close(pfd[1]); 
  26.          
  27.             /* 關閉管道讀端口pfd[0] */ 
  28.             close(pfd[0]); 
  29.  
  30.             /* 執行grep */ 
  31.             execlp("/bin/grep", "grep", "-v", "usr","/etc/passwd", NULL); 
  32.         } 
  33.         else 
  34.         {   wait(NULL); 
  35.             execlp("/usr/bin/who","who",NULL); 
  36.             /* 這裡的who原本是ls,但是ls的結果不能進入stdout,不知為何。 */ 
  37.         } 
  38.     } 
  39.     else /* child 1 */ 
  40.     { 
  41.         /* 打開result.txt文件,若不存在該文件 */ 
  42.         /* 則創建一個新文件並命名為r.txt */ 
  43.         int f = open("r.txt", O_CREAT | O_TRUNC| O_WRONLY, 0666); 
  44.          
  45.         /* 將標准輸入重定向到管道讀端口pfd[0] */ 
  46.         dup2(pfd[0], 0); 
  47.         close(pfd[0]); 
  48.          
  49.         /* 將標准輸出重定向到r.txt */ 
  50.         dup2(f, 1); 
  51.         close(f); 
  52.  
  53.         /* 關閉管道寫端口pfd[1] */ 
  54.         close(pfd[1]); 
  55.         /* 執行wc */ 
  56.         execlp("/usr/bin/wc", "wc", "-l", NULL); 
  57.     } 

為防止以後忘記,我把這張圖[2]放在下面,也為後來者提供一些方便,這張圖很清晰地講解了dup,dup2以及管道的一些知識。想了解更詳細的相關知識可以參考文章末尾的參考列表。

650) this.width=650;" border=0> 

 

FAQ:為啥要再開一個進程來跑grep -v usr /etc/passwd ?父進程不也可以執行嗎?

A:啊螞蟻!在執行了execlp("/bin/grep", "grep", "-v", "usr","/etc/passwd", NULL);後,程序就會exit(0)了,如果是父進程在執行,那麼父進程就退出了,因此要在execlp執行完成後繼續執行,就必須f**k一個子進程出來,用子進程來運行execlp,好讓老爹活著繼續生娃。其他地方也是一樣的。

FAQ:這段程序有沒有考慮僵屍進程活著孤兒進程的情況啊?

A:沒有。。 ( ̄ε(# ̄)。。讓它們去吧。。。僵屍和孤兒們就交給init老大照看了。。。

PS:必須吐槽一下這道題的提示。給出exec的用法還不如告訴execlp的原型;dup2的用法提示還是錯的,或者說有誤導作用,明明是將第二個參數重定向到第一個參數,結果它的參數的意思是從dst到src,我的英語一般,但是dst是destination,src是source,我還是知道的,提示起到了南轅北轍的作用;等待子進程結束就更加坑爹了,直接告訴做題者用wait(NULL);不就行了。

說白了,這個提示,懂的人不看也懂,不懂的人看了也不懂。

Copyright © Linux教程網 All Rights Reserved