HowTo: Use tput in a shell data entry system - Part 1
Novell Cool Solutions: Feature
By Stomfi
Reader Rating
from 3 ratings
|
Digg This -
Slashdot This
Posted: 31 Aug 2005 |
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.
Reader Comments
- Brilliant juggling with tput. I also like the Authors "deriving" of a solution
- This is a *great* series. I'm implementing an ASCII "Unix tools" database ala NOSQL, but such dbs don't supply screens for data entry. This is just what is needed to complete the Unix utility based databases which don't supply forms-based data entry. Error: in the file inwards.sh, a line is missing from the "scrnpaint()" function. The line: echo " Goods Code: $GOODSCODE" should appear after the line: echo " Carrier: $CARRIER" Otherwise, very odd behavior results. Also, the screen is identified by two names: inentry.sh and inwards.sh
Novell Cool Solutions (corporate web communities) are produced by WebWise Solutions. www.webwiseone.com
Learning to use Linux at Home and Work