58

I'd like to add a progress indicator to a command-line Java program.

For example, if I'm using wget, it shows:

71% [===========================>           ] 358,756,352 51.2M/s  eta 3s

Is it possible to have a progress indicator that updates without adding a new line to the bottom?

Thanks.

Tom Marthenal
  • 3,066
  • 3
  • 32
  • 47
  • @rfeak Sorry, http://stackoverflow.com/questions/1001290/console-based-progress-in-java – TheLQ Jan 03 '11 at 01:40

2 Answers2

72

I use following code:

public static void main(String[] args) {
    long total = 235;
    long startTime = System.currentTimeMillis();

    for (int i = 1; i <= total; i = i + 3) {
        try {
            Thread.sleep(50);
            printProgress(startTime, total, i);
        } catch (InterruptedException e) {
        }
    }
}


private static void printProgress(long startTime, long total, long current) {
    long eta = current == 0 ? 0 : 
        (total - current) * (System.currentTimeMillis() - startTime) / current;

    String etaHms = current == 0 ? "N/A" : 
            String.format("%02d:%02d:%02d", TimeUnit.MILLISECONDS.toHours(eta),
                    TimeUnit.MILLISECONDS.toMinutes(eta) % TimeUnit.HOURS.toMinutes(1),
                    TimeUnit.MILLISECONDS.toSeconds(eta) % TimeUnit.MINUTES.toSeconds(1));

    StringBuilder string = new StringBuilder(140);   
    int percent = (int) (current * 100 / total);
    string
        .append('\r')
        .append(String.join("", Collections.nCopies(percent == 0 ? 2 : 2 - (int) (Math.log10(percent)), " ")))
        .append(String.format(" %d%% [", percent))
        .append(String.join("", Collections.nCopies(percent, "=")))
        .append('>')
        .append(String.join("", Collections.nCopies(100 - percent, " ")))
        .append(']')
        .append(String.join("", Collections.nCopies((int) (Math.log10(total)) - (int) (Math.log10(current)), " ")))
        .append(String.format(" %d/%d, ETA: %s", current, total, etaHms));

    System.out.print(string);
}

The result: enter image description here

Mike Shauneu
  • 3,201
  • 19
  • 21
  • 1
    I tried that in Eclipse but it prints each progress on a new line. I added the following command before System.out.print(string); and it fixed the problem: System.out.println(new String(new char[70]).replace("\0", "\r\n")); Basically this clears Eclipse console so it looks like the line is being updated. – PhDeveloper Jan 22 '19 at 18:45
  • 2
    @PhDeveloper Yea, Eclipse is broken. In 100 articles about console, you'll find a note: "Works in every terminal but eclipse". – Danon May 02 '19 at 09:14
  • I had quite huge values (bytewise file upload) so instead of the `int percent = (int) (current * 100 / total);` I had to use `int percent = (int) (((float)current / (float)total) * 100);` in order to avoid integer overflow. – derHugo Jul 19 '19 at 15:37
  • 1
    Does it depend on size of terminal window?? – Saurav Kumar Oct 19 '19 at 17:00
  • 1
    @Danon Well it doesn't work by default in Eclipse but it does if you change the setting. Look here https://stackoverflow.com/a/64968642/6533028 – propatience Jan 26 '22 at 10:12
  • If you call it with current = 0, (no progress) the log10 of zero is undefined/infinity. Which causes a crash on the second to last append line. Other than that, super handy! – bhlowe Feb 28 '22 at 17:45
62

First when you write, don't use writeln(). Use write(). Second, you can use a "\r" to Carriage Return without using \n which is a New line. The carriage return should put you back at the beginning of the line.

rfeak
  • 8,124
  • 29
  • 28
  • 12
    But if the length of text can possibly shrink (for example, the number of digits required to display the ETA decreases), remember to write spaces over the old characters so that they don't show up any more. EDIT: In addition, remember to do System.out.flush() to make sure the text actually shows up (e.g. on a line-buffered terminal). – jstanley Jan 01 '11 at 13:55
  • 1
    I used `\r`, but it is acting like `\n`. I am using `print` instead of `println`. Any idea why it's not working? – Cardinal System Mar 22 '18 at 00:19
  • 3
    @CardinalSystem - Using eclipse console by any chance? Apparently it does not handle \r correctly. It will work in java cli though. – newsha Mar 23 '18 at 15:53
  • @newsha ah, that seems to be the case. Thank you! – Cardinal System Mar 24 '18 at 14:50