3

I've written a Java-console application that repeatedly prints its status to the console using carriage-returns (\r) at the end rather than line-feeds (\n) to keep the output on one screen. I also want to pipe that output to a file, like

java -jar my-jar.jar | tee /tmp/my-jar.log

However, it doesn't make sense to write everything to that file, just what's visible at the end of the application. In other words I don't need all the "lines" ending with \r there.

Example: I code

System.out.print("hello\r")
Thread.sleep(2000);
System.out.println("world")

"hello" appears on the screen and after two seconds it's replaced by "world". Therefore, after the program ends the user only sees "world" on the screen. Fine, that's correct.

However, if I tee that to a file it holds "hello\rworld\n", but I only want it to contain "world\n".

How do I do that on the command line rather than coding that in Java (which is why the question is here and not on SO)?

2 Answers 2

4

Using sed

By default, sed reads newline-separated input. This command will remove everything before the last \r on a line:

sed 's/.*\r//'

For example:

$ echo $'line1\n\hi\rhello\rworld' | sed 's/.*\r//'
line1
world

To use this with tee, we can use process substitution:

echo $'line1\n\hi\rhello\rworld' | tee >(sed 's/.*\r//' >Outfile)

Under bash, the construct >(...) is called process substitution. It runs the commands in the parens and puts them in a file-like object. tee write to the file-like object and the commands process the input.

Using awk

Similarly, this removes everything before the last \r on a newline-separated line:

awk -F'\r' '{print $NF}'

The option -F'\r' sets the field separator to a carriage return. Consequently, we only want to print the last field, $NF, on each newline-separated line. Thus, print $NF.

For example:

$ echo $'line1\n\hi\rhello\rworld' | awk -F'\r' '{print $NF}'
line1
world

To use this with tee:

echo $'line1\n\hi\rhello\rworld' | tee >(awk -F'\r' '{print $NF}' >Outfile)
3
  • 1
    Thanks for that thorough explanation! I really admire those who understand all that redirection-subshell-stuff and know their syntax... I tried your last example and it seems to work better than sed due to the large amount of characters in such a "line".
    – sjngm
    Commented Sep 14, 2016 at 7:17
  • I think I have to come back on this. I went with your last example and I learned that this somehow buffers the output and writes it all in one step to Outfile at the end of the program. Is there a way to enforce writing after each \n found? As in "write each line as soon as you have it" to Outfile?
    – sjngm
    Commented Sep 23, 2016 at 19:21
  • 1
    Sometimes, it is tricky to figure out where the troublesome buffer is. For starters, try: awk -F'\r' '{print $NF; fflush()}' >outfile
    – John1024
    Commented Sep 23, 2016 at 19:34
1

I was able to solve similar issue with col (filter reverse line feeds from input).

java -jar my-jar.jar | tee >(col -pb >/tmp/my-jar.log)

EDIT: Refined answer to better match the question.

5
  • Really? Using this I don't see "hello" on the screen: (echo -en "hello\r"; sleep 2; echo "world") | col -pb
    – sjngm
    Commented Apr 12, 2017 at 13:56
  • I think that's because of the brackets executing the whole code inside them before piping it to the next one. If you take the brackets out, it seems to behave as expected echo -en "hello\r"; sleep 2; echo "world" | col -pb. The previous also can be logged to a file by extending the pipe with tee resulting just "world" in the log file.
    – Jaakko
    Commented Apr 13, 2017 at 6:17
  • Well, that's what the code was supposed to do. So either you were looking for a solution for some other problem or didn't understand my question.
    – sjngm
    Commented Apr 13, 2017 at 6:22
  • I see what you mean, I modified the answer to use bash process substitution. This way you will see hello in the console and world in the log file.
    – Jaakko
    Commented Apr 13, 2017 at 7:18
  • Yep, that works :)
    – sjngm
    Commented Apr 13, 2017 at 7:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .