Using the "Alternate Screen" in a Bash Script

Using the alternate screen in a bash script

You can switch to the alternate screen using this command:

$ tput smcup

And back with:

$ tput rmcup

These commands just output the appropriate escape sequences for your terminal. If it is an XTERM they will be equivalent to the (more known but less elegant or portable):

$ echo -e "\e[?1049h"

And:

$ echo -e "\e[?1049l"

For more terminal control commands see man 5 terminfo.

after running a .sh file go back to the prompt i was before

vim and less and many other tools access the terminal's alternate screen and then restore the original screen when they are done. You can use tput to access the alternate screen from your shell script:

#!/bin/sh

tput smcup # begin using the alternate screen

# ...

tput rmcup # stop using alternate screen (restore terminal to original state)

Note that you don't want to remain in the alternate screen when the script ends, so you may prefer to do:

#!/bin/sh

trap 'tput rmcup' 0
tput smcup

Populating a variable from a script that needs to write to the terminal

Don't rule out result=$(inner.sh) just yet. If you want to display interactive prompts or dialogs in the script, do those on stderr, and have it write only the answer to stdout. Then you can have your cake and eat it too: interactive prompts and the result saved to a variable.

For example, dialog does exactly this if you use --output-fd 1 to tell it to write its answer to stdout. It uses curses to draw a dialog to the alternate screen but does it all on stderr.

$ value=$(dialog --keep-tite --output-fd 1 --inputbox title 10 40)
<dialog box shown>
<type "hello">
hello

(via Ask Ubuntu: How to get dialog box input directed to a variable?)

The script you posted can be made to do the same thing. It currently writes to stdout. Put exec 3>&1 1>&2 at the top so it'll write to stderr instead, and change the final echo ${options[$selected]} to echo ${options[$selected]} >&3 to write the answer to stdout. That'd get rid of the need for the caller to juggle file descriptors.


That said, prompts are not very UNIX-y. Consider eschewing interactivity entirely in favor of command-line arguments, configuration files, or environment variables. These options are better for power users who know how to use your script and want to automate it themselves.

My main purpose here is to commit a latest-stable-config from a selection of my last backups, which in my opinion needs the human judgement of when to consider a backup as appropriately stable.

The way I'd personally handle it is by writing a script with a couple of modes. Let's call it backups. backups --list would display a list of backups. You pick one and then call backups --commit <id> which would commit the named config. backups with no arguments would display usage for the unfamiliar user.

$ backups
Usage: backups --list
or: backups --commit <id>

Manages a selection of backups. Use --list to list available backups
and --commit to commit the latest stable config.
$ backups --list
4ac6 10 minutes ago
18f2 1 day ago
3019 7 days ago
$ backups --commit 4ac6

screen: how to turn on alternate screen?

UPDATE : I had forgotten about a feature of screen that disables alternate screen support by default.

Add altscreen on to your $HOME/.screenrc to enable it. This is probably all you have to do; all the messing about with $TERM, $TERMCAP, and $TERMINFO is probably unnecessary. I'll leave the rest of this answer here anyway.

I've had this in my own .screenrc so long it slipped my mind.

To enable this feature for the current session, type your screen escape character (Cntrl-A by default) followed by :altscreen on Enter.

Without this, screen won't respond to the escape sequences, even if they're defined in terminfo or termcap.


vim (and less, and most other curses-based full-screen programs) use the smcup and rmcup strings defined by the terminfo entry for your terminal. (These are referred to as ti and te, respectively, in the older termcap system). smcup switches to the alternate screen, and rmcup switches back to the primary screen.

If your terminfo entry doesn't have these strings, programs won't be able to do that.

The screen command, by default, sets your $TERM to "screen". Apparently the "screen" terminfo entry doesn't have smcup and rmcup.

If you feel like hacking terminfo entries, you can modify your existing information for "screen" to add smcup and rmcup. You'll need the infocmp command to convert your terminfo to a text, and tic to convert it back to the binary form used by terminfo.

A simpler solution is to set your $TERM environment variable to whatever it was before you invoked the screen command. Almost all terminal emulators these days are based on the old DEC VT100, so they should be sufficiently compatible.

On my system, for example, when I login I have TERM=xterm; it's likely something else on MacOS. Within a screen session, by default, I'd have:

 $ echo $TERM
screen

I could change it to xterm like this:

$ export TERM=xterm

(Again, replace "xterm" by whatever $TERM is on your system.)

You can also add

term xterm

to your $HOME/.screenrc. In fact screen has a number of options for tweaking your termcap/terminfo settings; man screen and search for "terminfo" for the gory details.

How to setup screen using a bash script

Try the following, it will open a new screen session with a bash which will change the directory and open a new bash with this directory as current:

screen -S work bash -c 'cd myDir && exec bash'

Adding -d -m to run it in detached mode. And after reattaching you will be in myDir:

screen -S work -d -m bash -c 'cd myDir && exec bash'

Better solution

The following code will create a detached screen with 3 screens each running myCommand1/2/3 in directory myDir1/2/3.

cd myDir1
screen -S work -d -m
screen -S work -X exec myCommand1
screen -S work -X chdir myDir2
screen -S work -X screen
screen -S work -X exec myCommand2
screen -S work -X chdir myDir3
screen -S work -X screen
screen -S work -X exec myCommand3
cd -

Note the last cd - that will return you back to your original working directory.

Finally just use screen -r work to attach your running screen session.

How do programs like man, screen, and vim create temporary overlays?

How do these programs accomplish that behavior?

ANSI escape sequences. Try running this script:

#/bin/bash -
tput smcup
echo 'Hello world!'
sleep 3
tput rmcup

Using infocmp, you can see underlying sequences that create this overlaying effect, e.g:

$ infocmp -1 | grep 'rmcup\|smcup'
rmcup=\E[?1049l\E[23;0;0t,
smcup=\E[?1049h\E[22;0;0t,

is this behavior shell-dependent or system-dependent?

None, it depends on whether the terminal emulator supports save/restore operations.



Related Topics



Leave a reply



Submit