歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> 用C實現的一個Bash腳本

用C實現的一個Bash腳本

日期:2017/3/1 10:40:57   编辑:Linux編程

題目:設當前目錄下有三個二進制格式的可執行程序,文件名分別為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. {
  14. int pfd[2];
  15. /* 建立管道 */
  16. pipe(pfd);
  17. if (fork()) /* parent */
  18. {
  19. if(!fork()) /* child 2 */
  20. {
  21. /* 將標准輸出和標准錯誤重定向到管道寫端口pfd[1] */
  22. dup2(pfd[1], 1);
  23. dup2(pfd[1], 2);
  24. close(pfd[1]);
  25. /* 關閉管道讀端口pfd[0] */
  26. close(pfd[0]);
  27. /* 執行grep */
  28. execlp("/bin/grep", "grep", "-v", "usr","/etc/passwd", NULL);
  29. }
  30. else
  31. { wait(NULL);
  32. execlp("/usr/bin/who","who",NULL);
  33. /* 這裡的who原本是ls,但是ls的結果不能進入stdout,不知為何。 */
  34. }
  35. }
  36. else /* child 1 */
  37. {
  38. /* 打開result.txt文件,若不存在該文件 */
  39. /* 則創建一個新文件並命名為r.txt */
  40. int f = open("r.txt", O_CREAT | O_TRUNC| O_WRONLY, 0666);
  41. /* 將標准輸入重定向到管道讀端口pfd[0] */
  42. dup2(pfd[0], 0);
  43. close(pfd[0]);
  44. /* 將標准輸出重定向到r.txt */
  45. dup2(f, 1);
  46. close(f);
  47. /* 關閉管道寫端口pfd[1] */
  48. close(pfd[1]);
  49. /* 執行wc */
  50. execlp("/usr/bin/wc", "wc", "-l", NULL);
  51. }
  52. }

為防止以後忘記,我把這張圖[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