Returning the terminal cursor to start-of-line wit

2019-02-16 23:25发布

问题:

I'm writing a filter (in a pipe destined for a terminal output) that sometimes needs to "overwrite" a line that has just occurred. It works by passing stdin to stdout character-by-character until a \n is reached, and then invoking special behaviour. My problem regards how to return to the beginning of the line.

The first thing I thought of was using a \r or the ANSI sequence \033[1G. However, if the line was long enough to have wrapped on the terminal (and hence caused it to scroll), these will only move the cursor back to the current physical line.

My second idea was to track the length of the line (number of characters passed since previous \n), and then echo \b that many times. However, that goes wrong if the line contained control characters or escape sequences (and possibly Unicode?).

Short of searching for all special sequences and using this to adjust my character count, is there a simple way to achieve this?

回答1:

Even if there were a "magic sequence" that when written to a console would reliably erase the last written line, you would STILL get the line and the sequence on the output (though hidden on a console). Think what would happen if somebody wrote the output to a file, or passed it down the pipe to other filters? Would they know how to handle such input? And don't tell me you rule out the possibility of writing somewhere else than directly to a console. Sooner or later, somebody WILL want to redirect the output - maybe even you!

The Right Way to do this is to buffer each line in memory as it is processed, and then decide whether to output it or not. There's really no way around this.



回答2:

$ cat >test.sh <<'EOF'
> #!/bin/sh
> tput sc
> echo 'Here is a really long multi-line string: .............................................................................................'
> tput rc
> echo 'I went back and overwrote some stuff!!!!'
> echo
> EOF
$ sh test.sh
I went back and overwrote some stuff!!!! .......................................
......................................................

Look for the save_cursor and restore_cursor string capabilities in the terminfo database.



回答3:

You can query terminal dimensions with a simple ioctl:

#include <sys/types.h>
#include <sys/ioctl.h>

// ...

struct winsize ws;
ioctl(1, TIOCGWINSZ, &ws);

// ws.ws_col, ws.ws_row should now contain terminal dimensions

This way you can prevent printing anything beyond the end of line and simply use the \r method.