wang_utils/command/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
pub async fn execute_async(
    command: &str,
    folder: Option<&str>,
    stdout_fn: impl Fn(String) + Send + Sync + 'static,
    stderr_fn: impl Fn(String) + Send + Sync + 'static,
    complete_fn: impl Fn() + Send + Sync + 'static,
) -> anyhow::Result<bool> {
    use std::io::{BufRead, BufReader};
    use std::process::{Command, Stdio};

    std::env::set_var("IS_TTY", "true");
    let split = command.split(" ").collect::<Vec<&str>>();
    let args = split.iter().skip(1).map(|r| *r).collect::<Vec<&str>>();
    let args = args.as_slice();
    let mut child = Command::new(split[0]);
    let child = child.args(args);
    // .args(&["antora-playbook.yml"])

    let child = if folder.is_some() {
        let folder = folder.unwrap();
        child.current_dir(folder)
    } else {
        child
    };
    let child = child
        .stdout(Stdio::piped()) // 捕获子进程的标准输出
        .stderr(Stdio::piped()) // 捕获子进程的标准错误
        .spawn();

    if child.is_err() {
        let error = child.err().unwrap();
        stderr_fn(error.to_string());
        return Ok(false);
    }
    let mut child = child?;
    // 获取子进程的标准输出和标准错误
    let stdout = child.stdout.take().unwrap();
    let stderr = child.stderr.take().unwrap();

    // 在后台读取标准输出
    let handle1 = tokio::spawn(async move {
        let reader = BufReader::new(stdout);
        for line in reader.lines() {
            if let Ok(line) = line {
                stdout_fn(line);
            }
        }
    });
    // 在后台读取标准错误
    let handle2 = tokio::spawn(async move {
        let reader = BufReader::new(stderr);
        for line in reader.lines() {
            if let Ok(line) = line {
                stderr_fn(line);
            }
        }
    });
    tokio::try_join!(handle1, handle2)?;
    // 等待子进程结束
    let status = child.wait()?;
    // 检查子进程是否成功退出
    if status.success() {
        complete_fn();
        Ok(true)
    } else {
        Ok(false)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_command() {
        let command = "pwd";
        let result = execute_async(
            command,
            Some("/Users/wangbin/src/docs/docs-taiwu"),
            |line| {
                println!("stdout:{line}");
            },
            |line| {
                println!("stdout:{line}");
            },
            || {
                println!("complete");
            },
        )
        .await
        .unwrap();
        println!("result:{}", result);
    }
}