Show persistent status message on console
Asked Answered
H

4

9

I am working on a program that sends a lot of output to stdout, but I want to be able to easily tell what the program is doing. I've seen some programs show the output of a script or program, yet the last line on the console is "reserved", meaning the output from the commands are shown on the screen, but the last line (or two, or however many) are reserved for a static or occasionally changing status text (e.g. Building <xxxxxx>...). This way, one can see output from the script/program for debug/troubleshooting purposes, but also quickly glance over and see the status of the program. Is there any way to achieve this with a shell script? I don't mind using built-in system tools, such as awk, to format the text, as long as the tools used are commonly found on most systems. If possible, I'd also like to avoid ncurses and other libraries as well.

If you still don't understand what I'm saying, I'll try to illustrate it here:

make -gcc -NOFLAGS -someotherGibberishHere Component1afdjadfjfadkladfjk
make -gcc -NOFLAGS -someotherGibberishHere Component2afdjadfjfadkladfjk
make -gcc -NOFLAGS -someotherGibberishHere Component3afdjadfjfadkladfjk
make -gcc -NOFLAGS -someotherGibberishHere Component4afdjadfjfadkladfjk
make -gcc -NOFLAGS -someotherGibberishHere Component5afdjadfjfadkladfjk
make -gcc -NOFLAGS -someotherGibberishHere Component6afdjadfjfadkladfjk
make -gcc -NOFLAGS -someotherGibberishHere Component7afdjadfjfadkladfjk
make -gcc -NOFLAGS -someotherGibberishHere Component8afdjadfjfadkladfjk
make -gcc -NOFLAGS -someotherGibberishHere Component9afdjadfjfadkladfjk
make -gcc -NOFLAGS -someotherGibberishHere Component10afdjadfjfadkladfjk
make -gcc -NOFLAGS -someotherGibberishHere Component11afdjadfjfadkladfjk

.
. (etc.)
.

------------------------------------------------------------------------
Installing component 11/134...

In this particular example, I'd like for the line "Installing component 11/134..." to remain there even once the console output starts to scroll. Preferably, I'd also like to keep that line right above it too.

That way, once we get into the thick of the program and normally the shell would've started scrolling, we'd still see:

.
. (more lines of output here)
.
make -gcc -NOFLAGS -someotherGibberishHere Component58afdjadfjfadkladfjk
make -gcc -NOFLAGS -someotherGibberishHere Component59afdjadfjfadkladfjk
make -gcc -NOFLAGS -someotherGibberishHere Component60afdjadfjfadkladfjk
make -gcc -NOFLAGS -someotherGibberishHere Component61afdjadfjfadkladfjk
make -gcc -NOFLAGS -someotherGibberishHere Component62afdjadfjfadkladfjk
make -gcc -NOFLAGS -someotherGibberishHere Component63afdjadfjfadkladfjk
make -gcc -NOFLAGS -someotherGibberishHere Component64afdjadfjfadkladfjk
make -gcc -NOFLAGS -someotherGibberishHere Component65afdjadfjfadkladfjk
make -gcc -NOFLAGS -someotherGibberishHere Component66afdjadfjfadkladfjk
------------------------------------------------------------------------
Installing component 66/134...
Highhanded answered 3/2, 2014 at 22:41 Comment(4)
The easiest and most robust solution would be to automatically start screen or tmux in split screen mode, and run two separate commands in each.Cochard
I've looked into those approaches, but so far, they're all for the end user to do. Is there any way to institute it programmatically?Highhanded
There may be a (probably optional) dedicated status line. Look for ways to enable it and write to it.Jurist
@CalebXu Yes, they both have features for sending window split commands and such programmatically.Cochard
S
3

It might be worth having a look into the capabilities of tput.

Something like the following could form the beginning of a solution to always print the status line at the bottom of the screen:

numlines=$(tput lines)
numcols=$(tput cols)
numcols=$(expr $numcols - 1)
separator_line=$(for i in $(seq 0 $numcols);do printf "%s" "-";done;printf "\n")
tput cup $numlines
echo $separator_line
echo <your status line>

The intention of this logic is to:

  • work out how many lines on the screen and move to the bottom

  • work out how many columns and build the separator line to span that many columns

  • print the separator line and then your status line

Having said that, I feel certain there must be a more elegant way to achieve what you want to do...

Spoliate answered 3/2, 2014 at 23:2 Comment(4)
tput looks like a possible tool to use for the task. The only problem with the solution mentioned above is that there's no way to figure out how often to run that code block you have. If I run it after every command, it will show up a billion times in the output - not quite what I wanted. If I only run it once at the end (or beginning), the output is only shown when I'm done (or very quickly buried in output).Highhanded
Yes, you're right. I don't think it's the correct solution. Back to the drawing board.Spoliate
I wonder if ncurses might be the way to go. I'm not familiar with it, so I can't propose a solution, but it might be worth reading the man page or a tutorial?Spoliate
I'm pretty familiar with ncurses, but I was trying to avoid that. I'm writing only a shell script as a piece of software, and to list ncurses as a dependency would be a bit strange (and I'm on OS X, but trying to stay with shell script here, and ncurses acts a bit weird sometimes). It also seems a bit overkill to initialize ncurses just for this job (it involves initializing the output into "ncurses mode", and output formatting is extremely strict).Highhanded
R
3

While it would require having a client-side dependency, there is a way to do just this with screen.

#!/bin/bash

# Check if script was started in our screen session
if [ -z "$INTERNAL_INIT_SCRIPT" ]; then
  # Create temporary screen config file to avoid conflicts with
  # user's .screenrc
  screencfg=$(mktemp)
  # Show status line at bottom of terminal
  echo hardstatus alwayslastline > "$screencfg"
  # Start script in a new screen session
  INTERNAL_INIT_SCRIPT=1 screen -mq -c "$screencfg" bash -c "$0"
  # Store screen return code
  ret=$?
  # Remove temporary screen config file
  rm "$screencfg"
  # Exit with the same return code that screen exits with
  exit $ret
fi

total=134

function set_status {
  screen -X hardstatus string "[$1/$total] $2"
}

# Prints "[1/134] Installing jq..." to the status line
set_status 1 'Installing jq...'

# Install some component

I'm using this in a script I made to initialize Fedora servers I spin up on DigitalOcean. Here's what it looks like in practice:

Screenshot

Again, while it does involve installing screen, it is a commonly available tool, and using it for this purpose is exceedingly easy.

Roddy answered 18/7, 2017 at 15:24 Comment(0)
M
1

You can do this by sending special console codes to your terminal emulator. Those are fairly standard, should hopefully be supported everywhere and are described in console_codes(4) and elsewhere.

That man page tells you to use utilities like ncurses or tput, but for small hacks like this I don't bother.

#!/bin/bash

numlines=$(tput lines)

# reset screen and limit scrolling
echo -e "\ec\e[2;$((numlines-1))r" 

# go to first line and output a header
echo -e "\e[0HWelcome to this little demo"

for x in $(seq 100); do
    # save position, go to last line, write status and restore position
    echo -ne "\e7\e[$((numlines))Hnow doing $x\e8"

    sleep 0.5
    echo $x
done 

You should probably revert to simple, dumb output if your stdout isn't a tty, and handle a window resize by trapping SIGWINCH to update $numlines and the scroll region.

Methodius answered 8/7, 2023 at 9:6 Comment(0)
J
0

As Lindsay Winkler's answer suggests, tput is likely to do what you want if your terminal (emulator) supports it.

The capabilities available to tput are defined by terminfo, listed in the terminfo(5) man page.

Something like this should work (if anything does):

#!/bin/bash

if tput hs ; then
    tput tsl
    echo -n This is the status line
    tput fsl
else
    echo Sorry, there is no status line
fi

(It prints the error message for me.)

You can use tput to print text on the bottom line, but then normal output will just overwrite it (unless you carefully control what's written to the screen).

The GNU screen command also makes some use of a status line, possibly emulating one if the terminal doesn't support it. man screen for details. But screen tends to use it for its own messages if it's available, and you don't seem to want to impose requirements on your users.

Jurist answered 4/2, 2014 at 0:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.