Search code examples
javabashjava.util.scannerstdin

piping hot java --- some strange behavior when processing stdin with java from bash


Suppose I have the following (silly) java program which cycles back and forth between main and aux, whenever a y is typed:

import java.util.Scanner;

public class Minimal {

    public static void main(String[] args) {

        System.out.println("in main...");

        Scanner scanner = new Scanner(System.in);

        String s;
        while(scanner.hasNextLine()) {

            s = scanner.nextLine();
            System.out.println(s);
            if(s.equals("y")) {
                aux();
                System.out.println("in main...");
            }       
        }
    }

    public static void aux() {

        System.out.println("in aux...");

        Scanner scanner = new Scanner(System.in);

        String s;
        while(scanner.hasNextLine()) {

            s = scanner.nextLine();
            System.out.println(s);
            if(s.equals("y")) {
                return;
            }
        }
    }
}

But then, when I run

$ javac Minimal.java
$ printf "y\ny\ny\ny\ny\ny\ny\n" | java Minimal

in bash, I get a different answer than when running

$ java Minimal

and typing (interactively) y followed by <ENTER> --- seemingly equivalent to what was fed in automatically above with printf. Any ideas?


Solution

  • The difference is because of buffering. Scanner buffers its input, so it may consume more than one line at a time from System.in. When you use printf, the data is available immediately so it is all consumed by the Scanner instance in main(). As a result, there is no more data available for the Scanner created in each call to aux() to read.

    When you run the program interactively, the terminal sends the data line by line as you enter it, so there isn't any extra data available to get buffered at each read.

    To avoid this problem, don't create more than one instance of Scanner for the same InputStream.