How to read user input again in pipeline

Hello, everyone.

I have a simple program that uses pipeline | on the command line to read from stdin and display it to the user for confirmation.

use std::io::{self, Read};
fn main() -> io::Result<()> {
    println!("reading contents");
    let contents = read_contents()?;
    println!("stdin contents: {}", contents);
    println!("Please make sure the input is correct:");
    let input = read_user_confirmation()?;
    println!("user input: {}", input);
    Ok(())
}


fn read_user_confirmation() -> io::Result<String> {
    let mut line = String::new();
    io::stdin().read_line(&mut line)?;
    Ok(line)
}

fn read_contents() -> io::Result<String> {
    let mut contents = String::new();
    io::stdin().read_to_string(&mut contents)?;
    Ok(contents)
}

However, stdin cannot read user input correctly when using pipes on the command line. The problem is that the second time you read from stdin, you can't block the user's input and exit. like this

$ echo 'test contents' | target/debug/pipe-test 
reading contents
stdin contents: test contents

Please make sure the input is correct:
user input: 

The reference to If this function returns Ok(0), the stream has reached EOF in std::io::BufRead::read_line may be read after the first EOF. I'm confused. Please help me

Use /dev/tty? This is a device file that's connected to the terminal where the current process is attached; you can read from/write to it just like you would with standard input/output (except you have to manually open it first).

Here's an example of opening /dev/tty as @cole-miller suggested. I didn't test the interaction between BufReader and reopening the TTY or anything; if you follow this example blindly you may lose inputs or similar. The termion crate may be more ergonomic or portable, but is also limited to Unixy systems. I don't know how this would be accomplished on other platforms like Windows.

use std::io::{self, Read, BufReader, BufRead};
use std::fs::OpenOptions;

fn main() -> io::Result<()> {
    println!("reading contents");
    let contents = read_contents()?;
    println!("stdin contents: {}", contents);
    println!("Please make sure the input is correct:");
    let input = read_user_confirmation()?;
    println!("user input: {}", input);
    Ok(())
}

fn read_user_confirmation() -> io::Result<String> {
    let file = OpenOptions::new().read(true).write(true).open("/dev/tty")?;
    let mut buf_reader = BufReader::new(file);
    let mut line = String::new();
    buf_reader.read_line(&mut line)?;
    Ok(line)
}

fn read_contents() -> io::Result<String> {
    let mut contents = String::new();
    io::stdin().read_to_string(&mut contents)?;
    Ok(contents)
}
1 Like

Thanks for your tips.

it's working. thank you