Tuesday, November 22, 2011

Shell Scripting errors. Part5. Handling User Input

Pitfalls of shell scripting. Part 5

Handling User Input.

  1. Command line parameters
  $ ./file 10 20 30
  •   The way to capture these parameters is use the variables $1, $2, $3, ... $9, ${10}, ${11}, ... in your code.
  •   Note: If the program expects 3 parameters and is passed less than 3, using $3 will cause a run-time error. Hence it is always better to check for a parameter before using it.
  if [ -n "$1" ]
    # Use $1

  •   The program name can be read with $0.
  •   The program name irrespective of its path from where it is invoked can be found by
  name=`basename $0`

  $ ./file  #name=file
  $ /home/user/dir/file   #name=file

  2. Counting parameters.
  •   $# provides the number of parameters passed. This can be used to check if the program was called with the required no of parameters, and further execution can be stopped otherwise.
  if [ $# -lt 4 ]
    echo "Usage file a b c d"

  3. Grabbing the last parameter.
  •   We cannot use echo "Last param= ${$#}" because $ cannot be inside the {} braces. Another bug point.
  •   To get it, we have to replace the $ with !.
  echo "Last param= ${!#}"

  Look HERE to see a way to grab all the parameters.

  4. Grabbing all parameters
  •   Shell also provides PERL like objects which store all the parameters passed in.
  •   $* and $@ are two such objects.
  •   $* stores the entire param set as a string.
  •   $@ handles each value passed as one element and it itself is a list, and can be used in an array.
  Look HERE to see their usage and differences.

  5. Playing with parameters.
  • shift is a command which can be used to use a parameter one at a time and then remove it from the set of parameters, shifting the remaining ones one place to the left. 
  • Hence the variable $1 will always hold the parameter, if we operate on the list of params in a loop.
  • shift can also move multiple parameters together.
  eg., echo "$1, $2, $3, $4"
       shift 4    # Now that we have used the 4 params, remove them all at once

  Look HERE to use shift and elegantly process all parameters.

  6. Options
  •   We can continue using the shift to work on the options and their parameters, if any.
  •   But this is rather tedious and we need to manually take care of each of the options.

  7. Separating options from parameters.
  • The standard technique of doing this is providing a "--" after the options have ended and then list the parameters. 
  • So the code knows to operate on the option list first using shift and grab their parameters, till it reaches --. 
  • After that we can use the $@ to find out how many parameters remain, and they are true params and operate on them as in example 4 above.
  Look HERE to see a piece of code doing this.

This can also handle the most complex positions, but again everything has to be manually checked and coded likewise. Which is not the practical way to go about.

  8. getopt command.
  •   getopt command is an inbuilt command to separate the options from the parameters. Internally we also force it to do the same thing as in #7.
  •   Syntax:
getopt options optstring parameters
  •   We list each option letter and place a colon(:) after the one that requires a parameter.
  eg.,   getopt ab:cd -a -b test1 -cd test2 test3
  •   Here test1 is the parameter of b and test2, test3 are program parameters.
  •   The way to use it in the code is
     set -- `getopt ab:cd: "$@"`
  •   We specify the list $@ as the set of parameters
  Look HERE to look at a piece of code doing this.
  •   But this fails to handle real-world cases where the parameter has spaces and has been enclosed in double quotes("") at the command line.

  9. getopts command
  •   getopts is the ultimate tool for processing the command line options, howsoever nested, complex and disparate in nature.
  •   It processes the parameters one by one as it detects them and thus can be used in a loop, thereby removing the need to write code for each option.
  •   Syntax:
getopts optstring variable
  • It places the current parameter in the "variable". It is powerful because the parameter of an option is available in an environment variable $OPTARG and the no of parameters it has evaluated at any point in another env variable $OPTIND.
  • After it has run out of options provided in the optstring, we are left with the parameters, and we know that we have moved over $OPTIND no of parameters. 
  • So we can shift those many values, and assume that the remaining parameter set (available with $@) is our set of true parameters meant for the program. 
  • They can be easily operated in another loop, as we have done above.
  Look HERE to see a piece of code doing this.

  Generally in UNIX world, there is a practice of standardizing the options, which can be found HERE.

  10. Getting user Input

    a)  read command can be used to read values and place them in a variable of our choice
        echo -n "Enter Name: "    # -n doesn't create a newline after the echo
        read name                 # $name now contains whatever the user typed till pressing Enter

    b)  Multiple values can be stored in multiple variables.
        echo -n "First, Last Name: "
        read firs last

    c)  Input process can be timed out using -t so that program does not wait forever for the user's input
        read -t 10 -p "Enter a no: "      # times out after 10 secs.

        -p allows to give a promt inline. Without a variable name the input is available in the environment variable $REPLY.
    d)  read can also be limited to a specific no of characters using -n option.
        This is useful for Y/N responses from the user.
        As soon as user presses "n" no of keys, the value is stored and input is finished.
        eg., read -n1 "Continue? (Y/N): " answer    # answer stores Y/N/any other value user entered. So it must be checked before using it.
    e)  Silent reading is useful when reading passwords, using the -s option
        eg., read -s -p "Enter password: " pass
        What shell does in these reads is it makes the text color same as the background color, making it impossible to read.
    f)  Reading from a file.
        cat file | while read line
          echo "Line $count: $line"
          count=$[ $count + 1 ]

No comments:

Post a Comment