User Tools

Site Tools


haas:spring2024:unix:projects:usf0

Corning Community College

CSCS1730 UNIX/Linux Fundamentals

Project: UNIX SCRIPTING FUN (usf0)

Errata

  • any bugfixes or project updates will be posted here

Reference

For additional information on the printf(1) tool recommended for use in this project, please check out this paper:

NOTE that while it focuses on the C and Perl variants of printf, the conceptual underpinnings are the same, and many of the examples are likely portable to the UNIX variant of the tool.

Objective

To create a script that can, with or without information provided by the user, display useful reference data.

Background

We've encountered many things in our UNIX journey so far, and have been readily building upon past experiences as we achieve newer and better successes. Of particular focus, we've been looking at shell scripting, and that will be the primary focus of this week's project.

There are collections of information quite useful to anyone journeying in a discipline, and computing is no different. We often encounter information represented in certain units more than others, or various quantities, or even particular number bases.

binary/octal/hex

One particular collection of information involves that of binary, octal, decimal (in signed and unsigned capacities) and hexadecimal numbers. We may encounter a tool that gives us information in octal and we need to feed it into another tool as binary or decimal; these sorts of transactions are common and crop up time and time again.

Having a strong handle on this information is vital for on-going success in the computing field.

As such, it is important to have close at hand the knowledge of the following number bases:

  • base 2 (binary)
  • base 8 (octal)
  • base 10 (decimal, specifically signed and unsigned quantities)
  • base 16 (hexadecimal)

While we don't need to have hundreds of values memorized, it IS a good idea to have a rather quick recollection of enough values that can be used for debugging purposes. The first 16 values of each of these is such a preferred range. Not only does it give us a range of information, but for the powers-of-two bases, can actually fit in and be propagated quite nicely with little added effort.

Process

It is your task to write a script that, when run without any arguments, produces the following table of information (and displayed/formatted in this very manner):

lab46:~/src/SEMESTER/unix/usf0$ ./usf0.sh
  base 2  |  base 8  |  base10  |  base16 
----------+----------+----------+----------
     0000 |      000 |        0 |     0x00
     0001 |      001 |        1 |     0x01
     0010 |      002 |        2 |     0x02
     0011 |      003 |        3 |     0x03
     0100 |      004 |        4 |     0x04
     0101 |      005 |        5 |     0x05
     0110 |      006 |        6 |     0x06
     0111 |      007 |        7 |     0x07
     1000 |      010 |        8 |     0x08
     1001 |      011 |        9 |     0x09
     1010 |      012 |       10 |     0x0A
     1011 |      013 |       11 |     0x0B
     1100 |      014 |       12 |     0x0C
     1101 |      015 |       13 |     0x0D
     1110 |      016 |       14 |     0x0E
     1111 |      017 |       15 |     0x0F
lab46:~/src/SEMESTER/unix/usf0$ 

Additionally, your script should take the following arguments (in this order):

  • $1- starting value (some value greater than or equal to 0, and below ending value)
  • $2- ending value (some value higher than starting value)
  • $3- display signed decimal (with the parameter “signed”)

Numerical arguments are to be given in decimal.

If provided, they should adjust the script's output accordingly.

Some additional constraints/assumptions you can make:

  • octal values MUST have at least 1 leading zero; that is the standard convention for displaying numbers in that base (to help distinguish them from decimal)
  • hexadecimal values start with a leading “0x” prefix, this is among the more common ways of showing a hex value; also, the “lettered” digits for this project must be UPPERCASE.
  • the maximum upper limit you can implement for is 255 (base 10), or 0xFF hexadecimal. This will keep your output tidy (you're welcome).

Only specifying starting value

In the event only the starting value is specified, assume a count of 16 (so display 16 values, starting at the starting value). Note that for binary and octal, all numbers should be displayed in a block, so if an additional place value comes into the picture, leading zeros must be visible on the preceding values.

For example:

lab46:~/src/SEMESTER/unix/usf0$ ./usf0.sh 12
  base 2  |  base 8  |  base10  |  base16 
----------+----------+----------+----------
    01100 |      014 |       12 |     0x0C
    01101 |      015 |       13 |     0x0D
    01110 |      016 |       14 |     0x0E
    01111 |      017 |       15 |     0x0F
    10000 |      020 |       16 |     0x10
    10001 |      021 |       17 |     0x11
    10010 |      022 |       18 |     0x12
    10011 |      023 |       19 |     0x13
    10100 |      024 |       20 |     0x14
    10101 |      025 |       21 |     0x15
    10110 |      026 |       22 |     0x16
    10111 |      027 |       23 |     0x17
    11000 |      030 |       24 |     0x18
    11001 |      031 |       25 |     0x19
    11010 |      032 |       26 |     0x1A
    11011 |      033 |       27 |     0x1B
lab46:~/src/SEMESTER/unix/usf0$ 

Specifying starting and ending value

We can adjust the range displayed by specifying an ending value as well:

lab46:~/src/SEMESTER/unix/usf0$ ./usf0.sh 37 42
  base 2  |  base 8  |  base10  |  base16 
----------+----------+----------+----------
   100101 |      045 |       37 |     0x25
   100110 |      046 |       38 |     0x26
   110001 |      047 |       39 |     0x27
   101000 |      050 |       40 |     0x28
   101001 |      051 |       41 |     0x29
   101010 |      052 |       42 |     0x2A
lab46:~/src/SEMESTER/unix/usf0$ 

With a specified starting/ending value pair, we can display as little as 1 value and (theoretically) as many as we want.

Specifying signed column

With the word “signed” given as the third parameter (ie starting and ending values are required), the following will occur:

lab46:~/src/SEMESTER/unix/usf0$ ./usf0.sh 2 14 signed
  base 2  |  base 8  |  base10  |  signed  |  base16 
----------+----------+----------+----------+----------
     0010 |      002 |        2 |       +2 |     0x02
     0011 |      003 |        3 |       +3 |     0x03
     0100 |      004 |        4 |       +4 |     0x04
     0101 |      005 |        5 |       +5 |     0x05
     0110 |      006 |        6 |       +6 |     0x06
     0111 |      007 |        7 |       +7 |     0x07
     1000 |      010 |        8 |       -8 |     0x08
     1001 |      011 |        9 |       -7 |     0x09
     1010 |      012 |       10 |       -6 |     0x0A
     1011 |      013 |       11 |       -5 |     0x0B
     1100 |      014 |       12 |       -4 |     0x0C
lab46:~/src/SEMESTER/unix/usf0$ 

If you have chosen not to implement the signed option, you script instead must simply display the following (to STDOUT) and terminate:

lab46:~/src/SEMESTER/unix/usf0$ ./usf0.sh 2 14 signed
SIGNED FUNCTIONALITY NOT IMPLEMENTED
lab46:~/src/SEMESTER/unix/usf0$ 

Signed values are how the computer represents negative numbers. The convention in common use is to reference the most significant bit (the left-most): if it is a 1, it is considered negative, and 0, it is positive.

The actual process of representing a number as a negative value is known as two's complement. In it, we invert the bits and add one.

For instance, if we had 4-bit values and we wanted to display a 1 as a negative value:

   0001   <-- value
   1110   <-- inversion of value's bits
     +1   <-- adding 1
   ====
   1111   <-- -1

Specifications

Evaluation will be based on correctness of values as well as on formatting/spacing.

You'll notice that everything lines up and is positioned similarly:

  • each column exists within 8 characters of on-screen real estate, with one additional space padding the left and the right margins of the column.
  • The values are right justified with each column, with all bases but decimal being exactly the same length (prepending leading zeroes if necessary)
  • The dashes and vertical bars set up an ASCII art-like table pattern that helps make the data more readable.
  • You are to manually print out no more than 2 consecutive spaces at a time in your output statements. If you need more, use one of the formatting tools to generate spaces for you.
    • Obviously you'll need end results that occupy some sequence of spaces. The distinction I'm making is you're not to do stuff like: echo “ $value”
    • I suggest getting cozy with the printf(1) tool
  • The signed functionality is optional; I will consider its implementation for extra credit. Implemented or otherwise, however, your script should not generate any errors should a third argument in proper form be given.
  • You need to check your arguments to ensure they are present and valid. Some examples:
    • starting value is a valid (whole) number, and it is greater than or equal to 0
    • ending value is a valid (whole) number, and it is:
      • greater than or equal to the starting value, AND
      • less than or equal to 255
    • signed third argument is just that, the word, present or not present
    • any invalid arguments (invalid number, NOT the word signed as an optional third argument), should result in an error message being generated.

Output Formatting with printf(1)

You might be wondering how you can full off some of these output formatting feats. The echo command, after all, is rather rudimentary.

Fear not! The printf(1) tool comes to your rescue!

Like echo, printf displays information to the screen (STDOUT). In fact, various programming languages (like C and C++) that want more powerful output formatting implement some form of printf into their vast libraries.

Be sure to check the manual page for options and functionality; following will be a few usage examples.

Basic operation

Like echo, printf in its simplest form takes as an argument a string to display to STDOUT:

lab46:~/src/SEMESTER/unix/usf0$ printf "Hello, World"
Hello, Worldlab46:~/src/SEMESTER/unix/usf0$ 

However, we notice one difference between the default behavior of printf vs. echo: printf does not automatically issue a newline by default, as echo does. So, we'll need to specify it manually with the \n character:

lab46:~/src/SEMESTER/unix/usf0$ printf "Hello, World\n"
Hello, World
lab46:~/src/SEMESTER/unix/usf0$ 

Those various escape characters we've learned about? Super useful here:

  • \n - newline
  • \t - tab

Be sure to check the manual page for additional escape characters.

Format specifiers

Now, we know from using echo we can utilize shell variable and command expansions to make our output more dynamic.

But, printf adds additional formatting capability that echo lacks. If the string we are displaying contains % symbols, substitutions can be made and behaviors invoked; functionality that echo lacks.

Some common format specifiers:

  • %d - substitute integer (whole number) value
  • %s - substitute string value
  • %c - substitute single character
  • %f - substitute floating point value
  • %o - substitute integer (whole number) value and represent in octal
  • %x - substitute integer (whole number) value and represent in hexadecimal

First up, a simple example:

lab46:~/src/SEMESTER/unix/usf0$ printf "%d\n" 175
175
lab46:~/src/SEMESTER/unix/usf0$ 

Then, adding to it:

lab46:~/src/SEMESTER/unix/usf0$ printf "The number: %d\n" 175
The number: 175
lab46:~/src/SEMESTER/unix/usf0$ 

That 175 can also be in a variable:

lab46:~/src/SEMESTER/unix/usf0$ value=175
lab46:~/src/SEMESTER/unix/usf0$ printf "The number: %d\n" ${value}
The number: 175
lab46:~/src/SEMESTER/unix/usf0$ 

You may want to wrap the variable in double quotes, to avoid cases where it might be NULL and otherwise generate an error (I generally quote all my variables to be on the safe side).

How about substitutions with strings?

lab46:~/src/SEMESTER/unix/usf0$ printf "Your username is: %s\n" "${USER}"
Your username is: username
lab46:~/src/SEMESTER/unix/usf0$ 

And in general just crafting super effective format strings:

lab46:~/src/SEMESTER/unix/usf0$ value=175
lab46:~/src/SEMESTER/unix/usf0$ printf "%d %s %X\n" "${value}" "is hexadecimal" "${value}"
175 is hexadecimal AF
lab46:~/src/SEMESTER/unix/usf0$ 

Please take note, where there are multiple format specifiers, substitution is in order of specification… %d was the first, %s was the second, and %X was the third in that particular string, so the first value following the string is grabbed by the %d, the next the %s, and the third the %X. Ordering matters (which should make sense).

Output formatting

So now, getting to where printf really excels, formatting your output.

It turns out, that between the % and whatever format option you specify, you can provide a numeric value which will impact how that number appears on the screen, commonly in the form of preallocated space to display within.

For instance:

lab46:~/src/SEMESTER/unix/usf0$ printf "Testing: >>%4d<<\n" 47
Testing: >>  47<<
lab46:~/src/SEMESTER/unix/usf0$ 

See what happened there? 47 was displayed, but WITHIN a block of 4 characters. Also of note, by default output is right justified.

To left justify, simply make it negative:

lab46:~/src/SEMESTER/unix/usf0$ printf "Testing: >>%-4d<<\n" 47
Testing: >>47  <<
lab46:~/src/SEMESTER/unix/usf0$ 

We can also pad with zeros, that is represented by decimal values:

lab46:~/src/SEMESTER/unix/usf0$ printf "Testing: >>%.3d<<\n" 49
Testing: >>049<<
lab46:~/src/SEMESTER/unix/usf0$ 

This can be combined with justify and space allocation:

lab46:~/src/SEMESTER/unix/usf0$ printf "Testing: >>%-6.3d<<\n" 49
Testing: >>049   <<
lab46:~/src/SEMESTER/unix/usf0$ 

Other neat tricks? We can variable-ize the padding by using a *:

lab46:~/src/SEMESTER/unix/usf0$ spacing=4
lab46:~/src/SEMESTER/unix/usf0$ printf "Testing: >>%*d<<\n" "${spacing}" 49
Testing: >>  49<<
lab46:~/src/SEMESTER/unix/usf0$ 

The printf tool is super-powerful and useful for output, so mastering its use adds an impressive capability to your repertoire.

Play with printf and experiment… you'll find it can accomplish some impressive output feats that previously may have been more complicated.

Submission

By successfully performing this project, you should have a fully functioning script by the name of usf0.sh, which is all you need to submit for project completion.

To submit this project to me using the submit tool, run the following command at your lab46 prompt:

$ submit unix usf0 usf0.sh
Submitting unix project "usf0":
    -> usf0.sh(OK)

SUCCESSFULLY SUBMITTED

You should get some sort of confirmation indicating successful submission if all went according to plan. If not, check for typos and or locational mismatches.

I'll be looking for the following:

104:usf0:final tally of results (104/104)
*:usf0:usf0.sh is bash shell script with appropriate logic [13/13]
*:usf0:usf0.sh only displays desired information [13/13]
*:usf0:usf0.sh operates in accordance with specifications [13/13]
*:usf0:script output matches specifications [13/13]
*:usf0:base columns are correct and formatted to specifications [13/13]
*:usf0:starting and ending values are properly handled [13/13]
*:usf0:script options are properly handled and implemented [13/13]
*:usf0:signed option is properly handled [13/13]

Spirit of the project implies an algorithmic backend, no hardcoded sets of data creating the illusion of functionality.

Additionally:

  • Solutions not abiding by SPIRIT of project will be subject to a 50% overall deduction
  • Solutions not utilizing descriptive why and how COMMENTS will be subject to a 25% overall deduction
  • Solutions not utilizing INDENTATION to promote scope and clarity will be subject to a 25% overall deduction
  • Solutions lacking ORGANIZATION and are not easy to read (within 90 char width) are subject to a 25% overall deduction
haas/spring2024/unix/projects/usf0.txt · Last modified: 2023/10/29 16:40 by 127.0.0.1