Novell Home

HowTo: Use tput in a shell data entry system - Part 1

Novell Cool Solutions: Feature
By Stomfi

Digg This - Slashdot This

Posted: 31 Aug 2005
 

StomfiLearning to use Linux at Home and Work
Welcome to my ongoing series of HowTo articles designed to help Linux newbies get comfortable with Linux. Before trying any of these HowTos, take a few minutes to study the prerequisites so you can hit the ground running.
--Stomfi
This HowTo describes a shell terminal based data entry system using "tput" and other cursor and screen attribute commands to create a useful and sophisticated interface without using a GUI. A basic inwards/outwards goods system is used as the platform to show these and other shell features including the use of functions. The system is developed in a top down fashion starting with the basics and reappraising the design, adding new features identified, in an ordered and prioritised fashion.

I have spent a lot of time showing you how to use a Runtime Revolution GUI interface to create data entry systems, but for these exercises I am going to show you how "tput" and other shell tools can be used to create a similar system in a shell terminal.

There are many instances when a shell terminal without a GUI window manger running is sufficient to do the job at hand, saving a lot of money on resource costs. A system like the one I'm going to describe can be run on an old P1 or even a 486, with a very light Linux non-GUI distro. One can use the shell script as a front end to a back end on a server, and being Linux, several serial based terminals can be connected to one PC, saving even more resources.

The tput shell command uses the terminfo database to make the values of terminal dependent capabilities available to the shell. The man page for terminfo(5), which you can read with the command,

man 5 terminfo

lists all the capabilities available. The /etc/terminfo database lists those that the terminal command strings are known for listed terminal types.

We are going to use a simple subset of all the commands, sufficient to achieve our purposes.

The examples in "man tput" gives you a good idea how tput can be used. You will find that some of the tput commands work differently in a terminal invoked inside an X window manager than in a virtual terminal invoked by say "CTRL-ALT-F2". This is because the X terminal is of type "xterm" and the virtual terminal is of type "linux"

Here is a simple script for testing the capabilities of "tput".

#!/bin/bash

#testmenu.sh

#test for tput cursor movements

#colour the screen

tput setb 3 #Green in xterm and brown in linux terminal

tput clear

#paint menu onto the screen

echo ""

echo ""

echo "TEST MENU"

echo "1 ..... ECHO 1"

echo "2 ..... ECHO 2"

echo "3 ..... ECHO 3"

echo "4 ..... QUIT"

echo ""

echo "Select item: "

#loop around gathering input until QUIT is more than 0

QUIT=0

while [ $QUIT -lt 1 ]

do

   #Move cursor to after select message

   tput cup 8 13

   #Delete from cursor to end of line

   tput el

   read SEL

   if [ ${#SEL} -lt 1 ]

   then

      continue

   fi

   if [ $SEL -eq 4 ]

   then

      QUIT=1

      continue

   fi

   #put message in middle of screen

   tput cup 15 20

   #Delete from cursor to end of line

   tput el

   case $SEL in

      *) echo "You selected $SEL";;

   esac

done

#reset the screen

#Find out if this is a "linux" virtual terminal

if [ $TERM ~ "linux" ]

then

     tput setb 0 #reset background to black

fi

tput reset

tput clear

The "tput" capabilities we have used in this test script are:

setb 3 which sets the screen background to the colour defined as the number 3. There are 8 colours.
clear which clears the screen effectively painting the new colour all over it.
cup 8 13 which puts the cursor at row 8 column 13 (which in this case is after the "select" message).
el which deletes to the end of the current line. (Deleting the previous selection, if any).
cup 15 20 which puts the cursor to line 15 row 20.
el which deletes to the end of the current line.
reset which resets the terminal to its previous state.
clear which in this case repaints the screen to its original colour.

It may be better to use "reset" which is a link to the "tset" command, rather than the "tput" version if funny things happen to your terminal. (I have found that "mc" doesn't like me using "tput reset" in a virtual terminal.)

The setb command works fine:

You can also use "setf" to change the foreground or text colour. The other tput commands we will use will "bold", "blink", "hide" and "show" text depending on what we are doing.

To really show off the capabilities of terminal based Linux, we will create the type of system where terminal functions are more productive. One that comes readily to mind is inwards and outwards goods for a small business, using a central server and inexpensive refurbished PCs, that can be recycled and replaced if they go wrong. These are something that you can pay $100 for a 20 to 50 PC lot at an auction. I find that P1s with 32MB and 1.3GB drives are easy to come by. You can install something like Damn Small Linux on this type of system using runlevel 3 which gives you a fully networked console based system. Then you can get a GUI interface by using startx if you need it. I find it best to install 3 Com 10/100 PCI Ethernet cards as these are well supported by all Linux distros.

This is a top down of the initial plan of the system. We shall add more to this picture as we discover more functions that are needed. It is a good idea to plan your system like this, prove this bit works, before getting more complex. This way you will minimise mistakes, and always know what you are trying to achieve.

Adjust is used if the entry is a mistake, or if a stock take finds discrepancies. Doing it this way is good practice as all entries and adjustments can be used as objective evidence.

Post is where the data is sent for keeping. This can be files on the local PC or on a networked file server.

Using the testmenu.sh example, we can copy and modify it and produce goods.sh, inwards.sh, and outwards.sh which look like this with a few variations.

Pretty psychedelic, eh!

The goods.sh script is different from the other two, as it has to exit back to the shell.

Here is its script.

#!/bin/bash

#goods.sh

#colour the screen

tput setb 3

tput clear

#loop around gathering input until QUIT is more than 0

QUIT=0

SEL=0

while [ $QUIT -lt 1 -o $SEL -ne 3 ]

do

   #paint menu onto the screen

   echo ""

   echo ""

   echo " GOODS MENU"

   echo " 1 ..... Inwards Goods"

   echo " 2 ..... Outwards Goods"

   echo " 3 ..... QUIT"

   echo ""

   echo "Enter Choice Number: "

   #Move cursor to after select message

   tput cup 7 22

   #Delete from cursor to end of line

   tput el

   read SEL

   if [ ${#SEL} -lt 1 ]

   then

      continue

   fi

   #call the required shell or set QUIT and continue the loop,
   or continue the loop on any other input

   case $SEL in

      1) exec $HOME/GOODS/bin/inwards.sh;;

      2) exec $HOME/GOODS/bin/outwards.sh;;

      3) QUIT=1

      continue;;

      *) continue;;

   esac

done

#reset the screen on exit

if [ $TERM = "linux" ]

then

   tput setb 0

fi

tput reset

tput clear

exit

This is the outwards goods menu, which doesn't have to exit, but goes back to the calling shell, so we can give this as one of the case options.

#!/bin/bash

#outwards.sh

#colour the screen

tput setb 5

tput clear

#paint menu onto the screen

echo ""

echo ""

echo " OUTWARDS GOODS MENU"

echo " 1 ..... Enter Outwards Goods"

echo " 2 ..... Adjust Outwards Goods"

echo " 3 ..... QUIT"

echo ""

echo "Enter Choice Number: "

#loop around gathering input until QUIT is selected

QUIT=0

while [ $QUIT -lt 1 ]

do

   #Move cursor to after select message

   tput cup 7 22

   #Delete from cursor to end of line

   tput el

   read SEL

   if [ ${#SEL} -lt 1 ]

   then

      continue

   fi

   #call the required shell

   case $SEL in

      1) exec $HOME/GOODS/bin/outentry.sh;;

      2) exec $HOME/GOODS/bin/outadjust.sh;;

      3) exec $HOME/GOODS/bin/goods.sh;;

      *) continue;;

   esac

done

#Fail safe point. Should never get here

exec $HOME/GOODS/bin/goods.sh

The exec replaces the current shell with the new one. This minimises memory usage in a small system.

I have made the point where I paint the screen of these two menus different on purpose so that you can decide which is more appropriate.

I'm going to use functions in the entry and adjust shell scripts, something I promised to introduce to you. The screen painting function is an obvious choice for this application as there is a lot of repetition.

Initially we'll be using simple shell entry with tput to move the cursor around.

As we progress we shall use some more sophisticated tools to retrofit things like selection lists.

This is the initial inwards goods data entry screen:

The date gets set automagically. Each field has to be filled before the next can be entered. After the last entry the message whether to post the entry appears. If y or Y response the entry is posted to a file.

Here is the script:

#!/bin/bash

#inentry.sh

#function to paint fields onto the screen

function scrnpaint()

{

   tput clear

   echo ""

   echo ""

   echo " INWARDS GOODS ENTRY"

   echo ""

   echo ""

   echo " Entry Date: $EDATE"

   echo " Docket Number: $DOCNUM"

   echo " Supplier Code: $SUPPCODE"

   echo " Carrier: $CARRIER"

   echo " Goods Name: $GOODSNAME"

   echo " Quantity: $GQUANT"

   echo " Measure: $GMEAS"

   echo " Received by: $RECDBY"

   echo ""

}

#function to post entry data

function dopost()

{

   echo "$EDATE#$DOCNUM#$SUPPCODE#$CARRIER#$GOODSCODE#$GOODSNAME#$GQUANT#$GMEAS#$RECDBY" >>
   $HOME/GOODS/data/ingoods.txt

   }

#Main program

#colour the screen

tput setb 6

tput clear

#Initialise variables

EDATE=`date +%d/%m/%y`

DOCNUM=""

SUPPCODE=""

CARRIER=""

GOODSCODE=""

GOODSNAME=""

GQUANT=""

GMEAS=""

RECDBY=""

#Loop round each entry until filled, moving cursor to correct entry point, and

#repainting the screen to show filled in variables

while [ ${#DOCNUM} -lt 1 ]

do

   scrnpaint

   tput cup 6 30

   read DOCNUM

done

while [ ${#SUPPCODE} -lt 1 ]

do

   scrnpaint

   tput cup 7 30

   read SUPPCODE

done

while [ ${#CARRIER} -lt 1 ]

do

   scrnpaint

   tput cup 8 30

   read CARRIER

done

while [ ${#GOODSCODE} -lt 1 ]

do

   scrnpaint

   tput cup 9 30

   read GOODSCODE

done

while [ ${#GOODSNAME} -lt 1 ]

do

   scrnpaint

   tput cup 10 30

   read GOODSNAME

done

while [ ${#GQUANT} -lt 1 ]

do

   scrnpaint

   tput cup 11 30

   read GQUANT

done

while [ ${#GMEAS} -lt 1 ]

do

scrnpaint

tput cup 12 30

read GMEAS

done

while [ ${#RECDBY} -lt 1 ]

do

   scrnpaint

   tput cup 13 30

   read RECDBY

done

#Request post OK

tput cup 15 10

echo "OK to Post Entry (Y or N):"

tput cup 15 37

read OKPOST

POSTOK=`echo $OKPOST | tr a-z A-Z`

if [ "$POSTOK" = "Y" ]

then

   dopost

fi

#return to calling shell

exec $HOME/GOODS/bin/inwards.sh


That was very straight forward. You could probably see that the dopost function wasn't necessary as it is only used once and could have been written where it was called.

We can get more sophistication by trapping the user in this screen until the correct responses are given, making sure that arrow keys and the like do not mess up the screen, and allowing the user to re edit the entries after a negative response to the OK to Post question, but at this stage we are just proving that the concept will work, so will leave these additions for later.

An important entry detail that I have left out of the inwards screen is the type of inwards goods. Whether it is stock, or whether returned outwards goods. You can insert all the lines to cover this information.

The outwards goods script will be pretty similar and you should be able to work out something adequate by copying and modifying the ingoods.sh script.

Here is the dopost function from that script to give you a clue to the necessary changes. Don't forget to change the last line exec to point to the outwards.sh script.

#function to post entry data

function dopost()

{

   echo "$EDATE#$DOCNUM#$CUSTCODE#$CARRIER#$GOODSCODE#$GOODSNAME#$GQUANT#$GMEAS#$DESPBY" >>
   $HOME/GOODS/data/outgoods.txt

}

In a practical goods system, one of the biggest problems is: Where did it go after it was received? Connecting a label printer to our system, which prints a sticky destination label for immediate attachment to the goods would minimise location errors and identify wrongly placed items. If you can think of a way that is just as effective, let me know and I'll include it. I expect RFID will eventually alleviate the problem, but small business needs a cheap solution.

Now we come to the adjustment scripts. These scripts read and select the relevant line from the data file, display the data and allow it to be edited. The result is posted as a new record for objective evidence of the modification.

You can immediately see that we have a problem with this. How do we know how the modified entry relates to the other one, and which one does it relate to anyway?

I'm going to leave you to think about it till next time. I wonder if we come up with a similar solution.


Novell Cool Solutions (corporate web communities) are produced by WebWise Solutions. www.webwiseone.com

© 2014 Novell