Rust自定义终端命令执行成功但无回显,如何解决?
问题
现有一段Rust代码,用于在终端中通过按键触发自定义Shell命令,每个命令包含触发键、描述和实际执行的Shell命令。代码可正常执行,但Shell命令的输出未在终端回显,仅显示执行成功提示。
代码如下:
use std::io::{stdin, stdout, Write}; use std::process::Command; use termion::event::Key; use termion::input::TermRead; use termion::raw::IntoRawMode; struct CustomCommand { key: char, description: &'static str, shell_command: &'static str, } impl CustomCommand { fn execute_shell_command(&self) { let output = Command::new("sh") .arg("-c") .arg(self.shell_command) .output(); match output { Ok(output) => { if output.status.success() { println!("Command executed successfully\r\n"); } else { println!("Command execution failed\r\n"); } } Err(e) => { println!("Error executing command: {:?}\r\n", e); } } } } fn main() { // 开启终端原始模式 let mut stdout = stdout().into_raw_mode().unwrap(); // 定义可用命令 let commands = vec![CustomCommand { key: 'c', description: "This is a custom command", shell_command: "echo 'Custom command executed'", }]; // 向终端打印提示信息 write!(stdout, "Please select a command:\r\n").unwrap(); // 为用户列出自定义命令 for command in &commands { write!(stdout, "{} {}\r\n", command.key, command.description).unwrap(); } stdout.flush().unwrap(); // 从标准输入读取输入 let input = stdin().keys(); // 遍历按下的每个按键 for key in input { // 将按键与可用命令匹配 match key.unwrap() { // 如果按键匹配命令,执行并退出循环 Key::Char(k) if commands.iter().any(|c| c.key == k) => { let command = commands.iter().find(|c| c.key == k).unwrap(); command.execute_shell_command(); break; } // 输出其他字符 Key::Char(c) => { write!(stdout, "You pressed: {}\r\n", c).unwrap(); stdout.flush().unwrap(); } // 忽略其他按键 _ => {} } } }
当前执行后的终端输出:
Please select a command: c This is a custom command Command executed successfully alex@alex-M52AD-M12AD-A-F-K31AD:~/rust/cc$
原因分析
核心问题在于使用了Command::output()方法:
- 该方法会捕获子进程的标准输出(stdout)和标准错误(stderr),将其存储在返回的
Output结构体中,不会直接输出到终端。 - 现有代码仅判断了命令执行的成功状态,未将捕获到的子进程输出内容打印到终端。
- 终端处于
raw模式虽不是输出不回显的直接原因,但后续恢复终端模式能避免终端后续行为异常。
修复方案
提供两种可行的修复方式,可根据需求选择:
方式一:让子进程直接继承终端的IO(推荐)
如果不需要捕获子进程的输出,只是想让命令直接在终端执行并显示输出,可改用Command::status()方法,并配置子进程继承当前进程的标准输入、输出和错误流。
修改execute_shell_command函数如下:
fn execute_shell_command(&self) { let status = Command::new("sh") .arg("-c") .arg(self.shell_command) // 让子进程继承当前终端的stdin/stdout/stderr .stdin(std::process::Stdio::inherit()) .stdout(std::process::Stdio::inherit()) .stderr(std::process::Stdio::inherit()) .status(); match status { Ok(status) => { if status.success() { println!("\r\nCommand executed successfully\r\n"); } else { println!("\r\nCommand execution failed\r\n"); } } Err(e) => { println!("\r\nError executing command: {:?}\r\n", e); } } }
同时在main函数结束前添加终端模式恢复代码(RawTerminal的Drop会自动恢复模式,显式处理更稳妥):
// 恢复终端正常模式 drop(stdout);
方式二:捕获子进程输出并手动打印
如果需要对输出进行处理后再显示,可继续使用output()方法,将捕获到的stdout和stderr内容转换为字符串并打印。
修改execute_shell_command函数如下:
fn execute_shell_command(&self) { let output = Command::new("sh") .arg("-c") .arg(self.shell_command) .output(); match output { Ok(output) => { // 打印子进程的标准输出 if let Ok(stdout_str) = String::from_utf8(output.stdout) { print!("{}", stdout_str); } // 打印子进程的标准错误 if let Ok(stderr_str) = String::from_utf8(output.stderr) { eprint!("{}", stderr_str); } if output.status.success() { println!("\r\nCommand executed successfully\r\n"); } else { println!("\r\nCommand execution failed\r\n"); } } Err(e) => { println!("Error executing command: {:?}\r\n", e); } } }
同样建议在main函数末尾添加drop(stdout)来恢复终端正常模式。
内容的提问来源于stack exchange,提问作者wyc
相关产品推荐
相关产品推荐

