3

I'm having trouble with stdin in Rust. I'm trying to process stdin comming from a pipe on a linux terminal, something like grep for example.

echo "lorem ipsum" | grep <text>

Im using this in rust:

fn load_stdin() -> Result<String> {
    let mut buffer = String::new();
    let stdin = stdin();
    stdin.read_line(&mut buffer)?;
    return Ok(buffer);
}

But the problem is that if I don't bring in any piped data I get prompted to write, I would instead like to return Err.

So basically, if I do something like:

ls | cargo run
user@machine: ~ $ 

All is good. But if I do not pipe any stdin:

cargo run

The program halts and waits for user input.

  • 1
    Possible duplicate: https://stackoverflow.com/questions/30012995/how-can-i-read-non-blocking-from-stdin. By default, you are doing a *blocking* read, in which case it is not an error to have no data; the read simply waits (potentially forever) until there *is* data. You need a *non-blocking* read, which will produce an error if it cannot read data *immediately*. I don't know Rust well enough yet to suggest how to do that. – chepner May 31 '21 at 17:03
  • Exactly what i needed. Thanks. I'm pretty new here when it comes to asking so do I mark the question as solved, remove it due to being a duplicate or? – DigitalCyan May 31 '21 at 17:19
  • Your question will just be marked as a duplicate - please do not remove it. In case someone searches for similar stuff, that someone will find your question and follow to the duplicate - so basically duplicate questions are "search links" that help to find the real issue. I think you might also mark it as a duplicate yourself. – KamilCuk May 31 '21 at 17:23
  • 1
    The proposed solution seems wrong for the OP's problem. The OP doesn't need a non-blocking read to produce an error if data isn't available _immediately_ - after all, the command that is piped into the Rust code might not do so immediately either. What the OP needs is to detect whether the standard input is a TTY or a pipe, e.g. as provided by [this crate](https://crates.io/crates/atty). – user4815162342 May 31 '21 at 18:06

1 Answers1

6

You can use the atty crate to test whether your standard input is redirected:

use std::io;

use atty::Stream;

fn load_stdin() -> io::Result<String> {
    if atty::is(Stream::Stdin) {
        return Err(io::Error::new(io::ErrorKind::Other, "stdin not redirected"));
    }
    let mut buffer = String::new();
    io::stdin().read_line(&mut buffer)?;
    return Ok(buffer);
}

fn main() -> io::Result<()> {
    println!("line: {}", load_stdin()?);
    Ok(())
}

This results in the desired behavior:

$ echo "lorem ipsum" | cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running `target/debug/playground`
line: lorem ipsum
$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running `target/debug/playground`
Error: Custom { kind: Other, error: "stdin not redirected" }
user4815162342
  • 141,790
  • 18
  • 296
  • 355