/std/process.md

https://github.com/rustcc/RustPrimer · Markdown · 121 lines · 99 code · 22 blank · 0 comment · 0 complexity · 2ab24462060fc0598afd7b4928ec1709 MD5 · raw file

  1. # 系统命令:调用grep
  2. 我们知道Linux系统中有一个命令叫grep他能对目标文件进行分析并查找相应字符串并该字符串所在行输出
  3. 今天我们先来写一个Rust程序来调用一下这个 grep 命令
  4. ```rust
  5. use std::process::*;
  6. use std::env::args;
  7. // 实现调用grep命令搜索文件
  8. fn main() {
  9. let mut arg_iter = args();
  10. // panic if there is no one
  11. arg_iter.next().unwrap();
  12. let pattern = arg_iter.next().unwrap_or("main".to_string());
  13. let pt = arg_iter.next().unwrap_or("./".to_string());
  14. let output = Command::new("/usr/bin/grep")
  15. .arg("-n")
  16. .arg("-r")
  17. .arg(&pattern)
  18. .arg(&pt)
  19. .output()
  20. .unwrap_or_else(|e| panic!("wg panic because:{}", e));
  21. println!("output:");
  22. let st = String::from_utf8_lossy(&output.stdout);
  23. let lines = st.split("\n");
  24. for line in lines {
  25. println!("{}", line);
  26. }
  27. }
  28. ```
  29. 看起来好像还不错但是以上的程序有一个比较致命的缺点因为Output是同步的因此一旦调用的目录下有巨大的文件grep的分析将占用巨量的时间这对于一个高可用的程序来说是不被允许的
  30. 那么如何改进呢
  31. 其实在上面的代码中我们隐藏了一个 `Child` 的概念子进程
  32. 下面我来演示怎么操作子进程
  33. ```rust
  34. use std::process::*;
  35. use std::env::args;
  36. // 实现调用grep命令搜索文件
  37. fn main() {
  38. let mut arg_iter = args();
  39. // panic if there is no one
  40. arg_iter.next();
  41. let pattern = arg_iter.next().unwrap_or("main".to_string());
  42. let pt = arg_iter.next().unwrap_or("./".to_string());
  43. let child = Command::new("grep")
  44. .arg("-n")
  45. .arg("-r")
  46. .arg(&pattern)
  47. .arg(&pt)
  48. .spawn().unwrap();
  49. // 做些其他的事情
  50. std::thread::sleep_ms(1000);
  51. println!("{}", "计算很费时间……");
  52. let out = child.wait_with_output().unwrap();
  53. let out_str = String::from_utf8_lossy(&out.stdout);
  54. for line in out_str.split("\n") {
  55. println!("{}", line);
  56. }
  57. }
  58. ```
  59. 但是这个例子和我们预期的并不太一样
  60. ```
  61. ./demo main /home/wayslog/rust/demo/src
  62. /home/wayslog/rust/demo/src/main.rs:5:fn main() {
  63. /home/wayslog/rust/demo/src/main.rs:9: let pattern = arg_iter.next().unwrap_or("main".to_string());
  64. 计算很费时间……
  65. ```
  66. 为什么呢
  67. 很简单我们知道在Linux中`fork`出来的函数会继承父进程的所有句柄因此子进程也就会继承父进程的标准输出也就是造成了这样的问题这也是最后我们用out无法接收到最后的输出也就知道了因为在前面已经被输出出来了呀
  68. 那么怎么做呢给这个子进程一个pipeline就好了
  69. ```rust
  70. use std::process::*;
  71. use std::env::args;
  72. // 实现调用grep命令搜索文件
  73. fn main() {
  74. let mut arg_iter = args();
  75. // panic if there is no one
  76. arg_iter.next();
  77. let pattern = arg_iter.next().unwrap_or("main".to_string());
  78. let pt = arg_iter.next().unwrap_or("./".to_string());
  79. let child = Command::new("grep")
  80. .arg("-n")
  81. .arg("-r")
  82. .arg(&pattern)
  83. .arg(&pt)
  84. // 设置pipeline
  85. .stdout(Stdio::piped())
  86. .spawn().unwrap();
  87. // 做些其他的事情
  88. std::thread::sleep_ms(1000);
  89. println!("{}", "计算很费时间……");
  90. let out = child.wait_with_output().unwrap();
  91. let out_str = String::from_utf8_lossy(&out.stdout);
  92. for line in out_str.split("\n") {
  93. println!("{}", line);
  94. }
  95. }
  96. ```
  97. 这段代码相当于给了`stdout`一个缓冲区这个缓冲区直到我们计算完成之后才被读取因此就不会造成乱序输出的问题了
  98. 这边需要注意的一点是一旦你开启了一个子进程那么无论你程序是怎么处理的最后一定要记得对这个`child`调用`wait`或者`wait_with_output`除非你显式地调用`kill`因为如果父进程不`wait`它的话它将会变成一个僵尸进程
  99. ** 以上问题为Linux下Python多进程的日常问题已经见怪不怪了