Presenting the output

The echo command

I've been glibly using the echo command without fully exploring it.

There are in fact, 2 different echo commands; the shell built-in echo and the external program /bin/echo.

If you type:

type echo 
                

this is the echo command that you have been using.

echo is a shell builtin
                

There is another on the system. It's /bin/echo and if you type:

/bin/echo --help 
                

you'll see that this is a different type of echo command. You could also say:

type /bin/echo 
                

to which the shell would respond:

/bin/echo is /bin/echo 
                

Obtaining help on the /bin/echo you should see:

Echo the STRING(s) to standard output
                

You can allow echo to print special characters:

\n newline
\t tab
\c suppress newline characters
\b the bell

Another point to note is that the following options are also available for use with the echo command:

-n do not print the trailing newline character
-e enable interpretation of these special character sequences

If you type:

/bin/echo -e "Hello\nWorld"
                

It would output:

Hello
World
                

Note, that it puts the two words on separate lines because of the \n character.

Also, we are forced to use the -e option for echo to enforce the interpretation of the backslash characters.

So let's modify a previous script to use this [other] echo:

while read rating type place tel
do
	/bin/echo -e "$type \t $rating \t $place \t $tel\c"
done < restaurants.txt
                

This would print all your restaurants on a single line because of the \c, while separating each by a TAB (\t).

Okay, so echo is one way of sending output to the screen, and if you do not use the bash builtin, you can use /bin/echo where you've got a couple more options.

You can do an info or man on echo to see what the options are that /bin/echo uses.

Exercises:

  1. Modify your eatout.sh script to accept no parameters. In this event, it should being in an interactive mode, allowing the user to enter their restaurant and rating.

  2. Ensure you do adequate error checking in this script such that if the user enters incorrect ratings or types, you inform them as such and request a new rating, type.

Mini challenge sequence

  1. Modify the password file to ensure that when the user "jeeves" logs in, they are presented with the eatout menu system created in 2 above. To achieve this, you will need to create the user jeeves, and modify his shell to reflect your script.

Maxi-challenge sequence:

  1. Write a script to create users on your system automatically. The script should read from a file of users that has the following format

    <FirstName> <LastName> <PrimaryGroup>
                                

The script should create the username from <LastName> <FirstName> sequence, to create the user called, for example whittal.hamish, if <Whittal> <Hamish> was the input from the file. The GECOS field (the comment field) in the password file should be their full name - i.e. <FirstName> <LastName> (e.g.Hamish Whittal). You may need to test whether the primary group already exists and create it if it does not. This script should illustrate how useful shell scripting really is, and how they can save you oodles of time in the long run!

The printf command

Echo is one way of dealing without output but in true Linux fashion, there's another way of doing this too:

echo is a little primitive in that in can't do formatting in any [really] nice ways. Perhaps you want to format all your restaurants and you want to put them in columns to make your script look like a professional restaurant directory service.

Printf has come down through the programming ages. It's available in C, Java and other similar languages.

printf takes the following format:

printf(%[flags][width][.precision]type)
                

Note that contents inside [ ] are optional. The types could be any one of the following:

s string
d decimal
o octal
x hexadecimal
u unsigned integers

We not going to use half these types, flags and options, but if you want more information you can look at the printf command in detail using info.

In it's simplest case, we could say:

printf '%s ' Hamish
                

That would print:

Hamish[hamish@defender ~]$		
                
[Note] Note

the "[hamish@defender ~]$" here is merely my prompt. So Hamish is printed, then a space then I'm returned to my prompt i.e. No newline is printed after Hamish.

This doesn't look any different to our echo command, but wait, there's more!

Another example:

printf '%.5d" 12
                

This would print:

00012$
                

Basically it's padding the number 12 up to a total of 5 digits. You'll notice that the prompt appeared directly afterwards. When you use printf, you have to explicitly tell it how it must display things. You have to tell it when you want to display a newline, or a TAB or a space, or anything else for that matter.

So we can modify the previous printf command to be:

printf '%.5d\n" 12
                

This would now print a newline character after it has printed the number 12, so that your prompt would appear on the next line. Suddenly, we're starting to see that the echo command, in comparison to printf, is looking like a complete wimp!

Now, you could add a flag on the front of that. I've decided to add a '+' flag.

printf '%+.5d %+.5d %+.3d\n" 9 12 -16
                

we end up with:

+00009 +00012 -016
                

So the '+' modifier tells the shell to precede each one of our numbers either with a '+' if it's a positive number or a '-' if it's negative. The .5 says to ensure that the total width takes up no more than 5 characters (or 3 in the case of '-16'). The number 6 will be padded with four zeroes preceding it. Also, note that each format inside the string relates to a single value outside the format string.

I can foresee we're going to use printf more often than echo! Using strings and preceding the string with a minus sign:

printf "%-10s,%-3s\n" Flying cows
                

That would left justify our text:

Flying    ,cows
                

Notice that the , (comma) is between the Flying and the cows. We are padding the Flying to 10 character width, the cows to 3 character width, both left justified.

If we left off the minus sign, it would right justify the text. The 10 says: "set aside a 10 character width string". Notice that 'Flying' is 6 characters in length, there will be an extra four spaces before the c of the cows starts. You will also notice that although I said the width of the second word should be 3 characters in width, the string that we used is longer than 3 characters so it ignores our specification.

If we changed it to:

printf "%10s,%10s\n" Flying cows
                

produces: (I have included underscores on the line below to indicate the 10 character widths)

Flying    ,      cows
----------,----------
                

So let's use this in our restaurant guide by formatting the output in a far more decent way, save this script into a file called formatrestaurants.sh:

IFS=','	
while read rating type place tel
do
	printf "%.3d,%-10s,%-20s,%12s\n" $rating $type $place $tel
done <restaurants.txt
                

Notice the formatting of the output. We're making the precision of our rating 3, padding it to the left with zeroes.

We're assuming the longest 'type' of restaurant we have is 'steakhouse' at 10 characters in length. So, we're left justifying the type of restaurant to a width of size 10.

Similarly we are left justifying the restaurant name to a width of 20.

However, our telephone number we are right justifying - by leaving out the minus sign, to a width of 12 characters.

We are separating each field using commas.

The above command will format the output of our restaurants.sh in a really professional looking manner.

Run the script:

chmod +x  formatrestaurants.sh

./formatrestaurants.sh |sort -rn
                

Everything is in nice columns the way we expected it to be.

So if your scripts are going to do lots of output, it's worth using the printf command to format it rather than using the echo command.

Exercises:

  1. Modify your eatout.sh script to format output in a standard way. Ensure that the output is justified as described above.

  2. Write a script that will use the output of the df command and reformat it in a way that makes it easy to read. Output should be as follows:

    <Mount Point>\t<% Free Space>\t<% Used Space>\t<Total Space>
                                
    1. Ensure that headings appear at the top of your output as illustrated here.

    2. The \t indicate TABs between each of these headings.Ensure that the output of this script, each time it is run, is appended to a log file /tmp/df.output.log

  3. Write a script similar to 2 above this time formatting the output of the memory in a similar manner.

  4. Combine the scripts in 2 and 3 to produce a single script that will do the memory and the disk space on the system at once.

Challenge sequence:

Modify your script above that will allow the user to supply a command line switch as a -h ot -t. The -h should produce the output in HTML format, while the -t should produce the output in text format.

You can, if you complete this timeously, add an additional switch (-d), which will produce the output using the dialog package.