Thumb

Bash programming


3/20/2016 12:00:00 AM

Bash pipe

A pipe takes the output from the command on the left-hand side of the pipe symbol and sends it to the input of the command on the right-hand side of the pipe symbol. A pipeline can consist of several pipes and this makes pipes a very powerful tool. It enables us to combine all the small and efficient UNIX commands in any thinkable way. If you want to count the number of people logged on, you could save the output of the command who in the temporary file tmp, use wc -1 to count the number of lines in tmp and finally remove the temporary file.

                                        $ who > tmp
                                        $ wc -1 tmp
                                        4 tmp
                                        $ rm    tmp

Using a pipe saves disk space and time: the stdout from who can be redirected to the stdin of wc -1 through a pipe and there is no need for temporarily storing the output from who.

                                      $ who | wc -1
                                            4

Most UNIX-commands am constructed with piping in mind and this makes it possible to solve complex tasks easily, by joining commands along a pipeline. Consider the following pipeline:

                                cat big.jpg | djpeg | pnmscale -pixels 150000 | cjpeg > small.jpg

The command cat sends the large JPEG-image to djpeg which decompresses it and sends the resulting bitmap to stdout. The stream of data floats through the next pipe to pnmscale which scales the bitmap image down to the given site. The scaled image is piped to the command cjpeg which compresses the standard input and finally produces a JPEG-image of reduced size which is stored in the file 'small jpg'.

Bash compare numbers /Arithmetic

In Bourne shell arithmetic is performed entirely `by proxy'. To evaluate an expression We call the `expr` command or the `bc` precision calculator. Here are some examples of expr'

                   a='expr $a+1'                         # increment a

                   a='expr 4 + 10 \* 5'                  # 4+10*5

                   check = 'expr $a \> $b'               # true=1, false=0. True if $a > $b

`expr` is very sensitive to spots and backslash characters and this makes it a hit awkward to do arithmetic under the Bourne shell.

Bash 2.0 provides a new and simpler way to do arithmetic using double parentheses. If you surround any integer arithmetic expression as in (( x = y + 1 )), you can perform most arithmetic operations with the same syntax as in Java and C.           

                              ( ( x=1 ) )
                              echo $x
                              1
                              ( ( m++ ))
                              ( ( y = 4*x ))
                              echo $y
                              8

Note that you do not need to use the dollar symbol to refer to a variable within the double parentheses (but you may do it) and that spaces are allowed.

                                   ( ( sum = 2 ))

                                   ( ( total - 4*$sum + sum )) echo $total

                                   10

The variables within double parentheses are throughout treated as integers. Assigning a float value like 2.5 to a variable results in an syntax error while assigning a string to a variable cause the string to be stored as zero.

Bash number arguments and Scripts

Scripts are created by making an executable file which begins with the sequence of characters

                  # ! /bin/bash

This construction is quite general: any executable file which begins with a sequence

             # !myprogram -option

will cause the shell to attempt to execute        

           myprogam -option filename

where filename is the name of the file.

If a script is to accept arguments then these can be referred to as $1 $2 $3.. $9'. There is a logical limit of nine arguments to a Bourne script, but Bash handles the next arguments as 1{101'. '$0' is the name of the script itself.

Here is a simple Bash script which prints out all its arguments.

                 #!/bin/bash
                 # Print all arguments (version 1)
                 for arg in $*
                 do
                    echo Argument $arg
                 done
                 echo Total number of arguments was $#

                             codefor loop in bash

The '$*' symbol stands for the entire list of arguments and '$#' is the total number of arguments.

Bash conditional structures

The conditional structures have the following syntax.

           if UNIX-command then
                command
           else
                commands
           fi

The 'else' clause is, of course, optional. As noted before, the first UNIX command could be anything, since every command has a return code. The result is TRUE if it evaluates to zero and false otherwise (in contrast to the conventions in mast languages). Multiple tests can be made using

           if UNIX-command then
              commands
           elif UNIX-command then
              commands
           elif UNIX-command then
              commands 
           else
              commands
           fi

where 'elif' means 'else-if.

The equivalent of the C-school's 'switch' statement is a more Pascal-like 'case' struc­ture.         

         case UNIX-command-or-variable in
           wildcardl) commands ;;
           wildcard2) commands ;;
           wildcard3) commands ;;
         esac

This structure uses the wildcards to match the output of the command or variable in the first line. The first pattern which matches gets executed.

Bash input from the user

In shell you can read the value of a variable using the 'read' command, with syntax read variable

This reads in a string from the keyboard and terminates on a newline character. Under the
old Bourne shell another way to do this is to use the 'input' command to access a particular
logical device. The keyboard device in the current terminal is ‘/dev/tty', so that one writes

              variable = 'line < /dev/tty`

which fetches a single line from the user. The command line is however not available in most GNU/Linux distributions.

Here are some examples of these commands. First a program which asks yes or no...

          #! /bin/bash
          # Yes or no echo "Please answer yes or no: "
          read answer
          case $answer in
               y*        Y* I j* I J* ) echo YES!! ;;
               *n | N* )          echo NO!! ;;
               *)
               echo "Can't you answer a simple question?"
           esac
               echo The end

Notice the use of pattern matching and the I' OR' symbol.

Bash do while loops

The loop structures in Bash and in the Bourne shell have the following syntax.

      while UNIX-command
      do
         commands
      done

The first command will most likely be a test but, as before, it could in principle be any UNIX command. The 'until' loop, reminiscent of BCPL, carries out a task until its argument evaluates to TRUE.

          until UNIX-command
          do
             commands
          done

Finally the 'for' structure has already been used above.

for variable in list

      do
             commands
      done

The best way to gain experience with these commands is through some examples.

    !#!/bin/bash
    #Get text from user repeatedly ft
    echo "Type away..."
    while read TEXT
      do
         echo You typed $TEXT
         if [ "$TEXT" = "quit" ; then echo "(So I quit !)"
          exit 0
         fi
      done
    echo "HELP!"

Procedures and traps

One of the worthy features of the Bourne shell is that it allows you to define subroutines or procedures. Subroutines work just like subroutines in any other programming language. They are executed in sane shell (not as a sub-process).

Here is an interesting program which demonstrates two useful things at the same time. First of all, it shows how to make a hierarchical subroutine structure using the Bonnie shell. Secondly, it shows how the 'trap' directive can be used to trap signals, so that Bourne shell programs can exit safely when they are killed or when CTRL-C is typed.

Bash function return / Subroutines

  • How to make a signal handler in Bourne Shell

#####################################################

  • Level 2

#####################################################

     #!/bin/bash
     ReallyQuit()
     {
      while true
        do
          echo "Do you really want to quit?" read answer
          case $answer in
               y* I Y* ) return 0;;
               *)         echo "Resuming..."
              return 1;;
          esac
        done
      }

#####################################################

# Level 1

#####################################################

        SignalHandler()
          {
              if ReallyQuit then
                  exit 0
              else
                  return 0
              fi
           } 

######################################

# Level 0 : main program

######################################

          trap SignalHandler 2 15          # Trap kill signals 2 and 15
          echo "Type some lines of text..."
          while read text
            do
               echo "$text - CTRL -C to exit"
            done

  note that the logical tree structure of this program is upside down (the highest level conies at the button). This is because till subroutine inns must be defined before they are used.

About Teacher

Reza Karim

Software Engineer

More about him