Post History
This other answer tries to truncate each line to make it fit the width of the terminal. It's hard to do this reliably because e.g. a tab character counts as one, but it looks like several spaces; o...
Answer
#2: Post edited
- [This other answer][1] tries to truncate each line to make it fit the width of the terminal. It's hard to do this reliably because e.g. a tab character counts as one, but it looks like several spaces; on the other hand unprintable characters and escape sequences do not show at all.
- My answer is a completely different approach. The idea is to print to the alternate screen. The alternate screen is used by `less`, `vi`, `nano` and many similar programs. When such program exits, there is no trace of its output.
- The below script switches the terminal to the alternate screen and pipes its input there. Eventually it switches the terminal back. Then it prints (repeats) one last line to its stdout, this is the line that will remain.
- ```
- #!/bin/sh
- trap '' INT
- enter() { setterm --linewrap off; tput smcup; } >/dev/tty
- leave() { setterm --linewrap on; tput rmcup; } >/dev/tty
- trap leave QUIT TERM
lines=- n="${1:-1}"
- enter
- lines="$(tee /dev/tty | tail -n "$n"; echo x)"
- leave
- printf '%s' "${lines%x}"
- ```
- Notes:
- - The script uses at most one argument. If specified, the argument is passed to `tail -n`. This way you can get more than one last line in your normal screen. If not specified, `1` is used.
- - `tput smcup` and `tput rmcup` are responsible for entering and leaving the alternate screen respectively.
- - `setterm --linewrap off` tells the terminal not to wrap long lines. You may prefer not using `setterm --linewrap off` and `setterm --linewrap on` in the script. Delete them at will.
- - The last line(s) is printed (repeated) outside of the alternate screen in full, i.e. with wrapping.
- - Tools that read from the terminal (`sudo` asking for password, simple y/n prompts) should work in the alternate screen.
- - The script will not capture your input (like an answer to a y/n prompt) to print (repeat) it after leaving the alternate screen, even if the input is echoed inside the alternate screen and seems to belong to the last line(s).
- - Tools that print directly to the terminal (`/dev/tty`) or to their stdout (which happens to be the terminal) should work in the alternate screen, but such lines will not be among lines captured by the script and repeated after leaving the alternate screen. Inside the alternate screen such lines may seem out of sync with respect to stdout because only stdout is "delayed" and buffered around `tee`.
- - If you want, you can merge stderr with stdout and pipe the merged stream to the script: `something 2>&1 | ovr`. This way stdout and stderr will stay in sync.
- - The script works reasonably well when the pipeline gets interrupted with <kbd>Ctrl</kbd>+<kbd>c</kbd>.
- - <kbd>Ctrl</kbd>+<kbd>z</kbd> is somewhat troublesome because the script will stop without leaving the alternate screen. If you make it continue in the foreground (`fg`) then it will leave the alternate screen eventually like it normally would. If you kill it with SIGTERM (`kill %%`) then it will leave the alternate screen because of the trap.
- [1]: https://linux.codidact.com/posts/289869/289875#answer-289875
- [This other answer][1] tries to truncate each line to make it fit the width of the terminal. It's hard to do this reliably because e.g. a tab character counts as one, but it looks like several spaces; on the other hand unprintable characters and escape sequences do not show at all.
- My answer is a completely different approach. The idea is to print to the alternate screen. The alternate screen is used by `less`, `vi`, `nano` and many similar programs. When such program exits, there is no trace of its output.
- The below script switches the terminal to the alternate screen and pipes its input there. Eventually it switches the terminal back. Then it prints (repeats) one last line to its stdout, this is the line that will remain.
- ```
- #!/bin/sh
- trap '' INT
- enter() { setterm --linewrap off; tput smcup; } >/dev/tty
- leave() { setterm --linewrap on; tput rmcup; } >/dev/tty
- trap leave QUIT TERM
- n="${1:-1}"
- enter
- lines="$(tee /dev/tty | tail -n "$n"; echo x)"
- leave
- printf '%s' "${lines%x}"
- ```
- Notes:
- - The script uses at most one argument. If specified, the argument is passed to `tail -n`. This way you can get more than one last line in your normal screen. If not specified, `1` is used.
- - `tput smcup` and `tput rmcup` are responsible for entering and leaving the alternate screen respectively.
- - `setterm --linewrap off` tells the terminal not to wrap long lines. You may prefer not using `setterm --linewrap off` and `setterm --linewrap on` in the script. Delete them at will.
- - The last line(s) is printed (repeated) outside of the alternate screen in full, i.e. with wrapping.
- - Tools that read from the terminal (`sudo` asking for password, simple y/n prompts) should work in the alternate screen.
- - The script will not capture your input (like an answer to a y/n prompt) to print (repeat) it after leaving the alternate screen, even if the input is echoed inside the alternate screen and seems to belong to the last line(s).
- - Tools that print directly to the terminal (`/dev/tty`) or to their stdout (which happens to be the terminal) should work in the alternate screen, but such lines will not be among lines captured by the script and repeated after leaving the alternate screen. Inside the alternate screen such lines may seem out of sync with respect to stdout because only stdout is "delayed" and buffered around `tee`.
- - If you want, you can merge stderr with stdout and pipe the merged stream to the script: `something 2>&1 | ovr`. This way stdout and stderr will stay in sync.
- - The script works reasonably well when the pipeline gets interrupted with <kbd>Ctrl</kbd>+<kbd>c</kbd>.
- - <kbd>Ctrl</kbd>+<kbd>z</kbd> is somewhat troublesome because the script will stop without leaving the alternate screen. If you make it continue in the foreground (`fg`) then it will leave the alternate screen eventually like it normally would. If you kill it with SIGTERM (`kill %%`) then it will leave the alternate screen because of the trap.
- [1]: https://linux.codidact.com/posts/289869/289875#answer-289875
#1: Initial revision
[This other answer][1] tries to truncate each line to make it fit the width of the terminal. It's hard to do this reliably because e.g. a tab character counts as one, but it looks like several spaces; on the other hand unprintable characters and escape sequences do not show at all. My answer is a completely different approach. The idea is to print to the alternate screen. The alternate screen is used by `less`, `vi`, `nano` and many similar programs. When such program exits, there is no trace of its output. The below script switches the terminal to the alternate screen and pipes its input there. Eventually it switches the terminal back. Then it prints (repeats) one last line to its stdout, this is the line that will remain. ``` #!/bin/sh trap '' INT enter() { setterm --linewrap off; tput smcup; } >/dev/tty leave() { setterm --linewrap on; tput rmcup; } >/dev/tty trap leave QUIT TERM lines= n="${1:-1}" enter lines="$(tee /dev/tty | tail -n "$n"; echo x)" leave printf '%s' "${lines%x}" ``` Notes: - The script uses at most one argument. If specified, the argument is passed to `tail -n`. This way you can get more than one last line in your normal screen. If not specified, `1` is used. - `tput smcup` and `tput rmcup` are responsible for entering and leaving the alternate screen respectively. - `setterm --linewrap off` tells the terminal not to wrap long lines. You may prefer not using `setterm --linewrap off` and `setterm --linewrap on` in the script. Delete them at will. - The last line(s) is printed (repeated) outside of the alternate screen in full, i.e. with wrapping. - Tools that read from the terminal (`sudo` asking for password, simple y/n prompts) should work in the alternate screen. - The script will not capture your input (like an answer to a y/n prompt) to print (repeat) it after leaving the alternate screen, even if the input is echoed inside the alternate screen and seems to belong to the last line(s). - Tools that print directly to the terminal (`/dev/tty`) or to their stdout (which happens to be the terminal) should work in the alternate screen, but such lines will not be among lines captured by the script and repeated after leaving the alternate screen. Inside the alternate screen such lines may seem out of sync with respect to stdout because only stdout is "delayed" and buffered around `tee`. - If you want, you can merge stderr with stdout and pipe the merged stream to the script: `something 2>&1 | ovr`. This way stdout and stderr will stay in sync. - The script works reasonably well when the pipeline gets interrupted with <kbd>Ctrl</kbd>+<kbd>c</kbd>. - <kbd>Ctrl</kbd>+<kbd>z</kbd> is somewhat troublesome because the script will stop without leaving the alternate screen. If you make it continue in the foreground (`fg`) then it will leave the alternate screen eventually like it normally would. If you kill it with SIGTERM (`kill %%`) then it will leave the alternate screen because of the trap. [1]: https://linux.codidact.com/posts/289869/289875#answer-289875