If you run this code and don't press a key, "Still running..." will be printed, then the command will finish (as expected).
If you run this code and press any key, "Still running..." will be printed, and the command will get stuck until you press a key again. Note: Sometimes this doesn't happen. Sometimes you have to run the code four or five times to see the issue.
use std::{process::Command, thread, time::Duration};
use termion::{event::Key, input::TermRead};
pub struct CmdRunner {}
impl CmdRunner {
pub fn run(&mut self) {
let mut command = Command::new("script");
command.arg("-qec").arg("sleep 4").arg("/dev/null");
let mut child = command.spawn().expect("failed to spawn command");
let mut stdin = termion::async_stdin().keys();
loop {
match child.try_wait() {
Ok(Some(_)) => {
print!("Child process has exited\r\n");
break;
}
Ok(None) => {
let input = stdin.next();
if let Some(Ok(key)) = input {
print!("A key was pressed!\r\n");
match key {
Key::Ctrl('c') => {
print!("Ctrl + C was pressed!")
}
_ => {}
}
}
print!("Still running...\r\n");
}
Err(e) => {
eprint!("Error while waiting for child process: {}", e);
break;
}
}
thread::sleep(Duration::from_millis(100));
}
}
}
fn main() {
let mut command = CmdRunner {};
command.run();
}
If you comment out this line:
let mut stdin = termion::async_stdin().keys();
And these lines:
if let Some(Ok(key)) = input {
print!("A key was pressed!\r\n");
match key {
Key::Ctrl('c') => {
print!("Ctrl + C was pressed!")
}
_ => {}
}
}
The command will finish as expected even if you press a key while it's running. Note: I can't just remove those lines because I have to listen to keys.
So I think something in termion::async_stdin is causing the loop (and command) to get stuck—which I find very strange.
I checked the source code, and I couldn't find any clues (or maybe I don't understand the code enough).
What could be causing this issue, and how to fix it?
Still running...
Still running...
Still running...
Still running...
Still running...
A key was pressed!
Still running...
Still running...
Still running...
Still running...
A key was pressed!
Still running...
It's kind of weird. It's most likely to happen when I press Enter (or some other key) around the 1.5 second mark (I added an incrementing integer counter to the "Still running..." line to help me time the test).
I also deleted the redirection of the script command's output to /dev/null so I could see what this command was doing. Here's the output from script on a normal run:
Script started on 2023-05-26 17:18:42+08:00 [COMMAND="sleep 4" TERM="xterm-256color" TTY="/dev/pts/26" COLUMNS="118" LINES="12"]
Script done on 2023-05-26 17:18:46+08:00 [COMMAND_EXIT_CODE="0"]
Here's the output from script on a run where I pressed Enter and then had to manually bail out after 8 seconds:
Script started on 2023-05-26 17:20:25+08:00 [COMMAND="sleep 4" TERM="xterm-256color" TTY="/dev/pts/26" COLUMNS="118" LINES="12"]
Script done on 2023-05-26 17:20:33+08:00 [COMMAND_EXIT_CODE="0"]
As you can see, pressing Enter seems to stop script from exiting normally for some reason. The newline also leaked into the output from script.
Not sure what to make of it yet. Just throwing it out there in case anyone else has some ideas.
I'm curious what you're using script for here. If it's just to sleep for a few seconds, you could just replace it directly with Command::new("sleep").arg("4"), which seems more robust.
There's an extra newline in the malfunctioning output.
OK, great! To be honest, I was a bit confused about what your program was supposed to do, as the snippet posted here isn't complete. Glad it didn't mess up your workflow.
I'm not sure exactly what the problem was, but it looks like sometimes when termion reads the key you press, it locks stdin and doesn't release the mutex, which causes script to block until you get termion to release the lock with your second (or third...) keypress.
Oh, I should have explained what my program does. It lets you create a menu with commands that can be triggered with a key (like in the photo I posted in my last reply).
So with this line:
command.stdin(std::process::Stdio::null());
Termion is no longer sharing stdin with my Rust code (the parent thread)? Which is why my Rust code's stdin doesn't block anymore?
You need to delete .arg("/dev/null"); from your command, or replace it with a filename. If you don't specify a filename, the output you want is written to a file called typescript in the folder where you run your program.