Shell Programming

From Wikibooks, the open-content textbooks collection

Jump to: navigation, search

Contents

[edit] Expansions

Expansions are required to deliver a value to script. We will discuss Arithmetic expansion and Parameter expansion here. Placing '$' prior to a variable expands its value.

Arithmetic expansion is done with $((operation)). Parameter expansion is done with ${parameter}.

Let's give an example of arithmetic expansion:

one=1
$(($one+1))

$(($one+1)) will have the value of 2.

Let's try to do some examples for parameter expansion. Let's say we want to remove a string 'Hello' from 'HelloBob'. The following will do the trick:

string='HelloBob' 
${string%Bob}

${string%Bob} portion cuts out 'Bob' from end of variable string, so we are left with Hello as its output.

If variable string had value of 'HelloBobBob', ${string%%Bob} would do the same trick. The '%%' portion is required because it signifies to remove 'Bob' from end of variable string and do so further away from it. So, we are also left with 'Hello'.


If '%' is replaced with '#' and '%%' replaced with '##', this signifies the same meaning but this time from beginning of parameter, not from end. For example, the following code would remove 'Hello' from 'HelloBobBob':

${string#Hello}

{param-value} replaces param's value with 'value' fi param's original value is null.

{param-?value} prints param: value if param doesn't exist or null. It effectively aborts the command. {param+value} sets param to value if not null.

[edit] Control statements

The test command can be shortened to '[' and when this is done in control statements it is customary to end it with an accompanying ']' like '[ ]'.

[edit] Condition types

Condition types are used inside '[ ]' in order to test for a condition. Conditon types are divided into three categories - String comparison, Arithmetic comparison and File conditional.

[edit] String comparison

  string1 = string2  equal
  string1 != string2  not equal
  -n string  not null
  -z string null

[edit] Arithmetic comparison

  expression1 -eq expression2  equal
  expression1 -ne expression2  not equal
  expression1 -gt expresssion2  greater than
  expression1 -ge expression2 greater than or equal to
  expresssion1 -lt expression2  less than
  expression1 -le expression2 less than or equal to

[edit] File conditional

  -d file  directory
  -e file  file exists
  -f file  file is regular file
  -g file  set-group-id on file
  -r file  readable file
  -s file  non-zero sized file
  -u file  set-user-id on file
  -w file  writable file
  -x file  executable file

[edit] If

The if statement has the following basic structure:

  if [ condition of expressions ]; then
  fi

[edit] while

The while statement has the following basic structure:

  while [ condition of expressions ]; do
  done

[edit] until

The until statement has the following basic structure:

  until [ condition of expressions ]; do
  done

[edit] for

The for statement has the following basic structure:

for variable_name in value1 value2 ...
do
done

Every loop replaces variable_name's value with value1-2 and beyond.

[edit] Using && and || in control statements

The && and || can be used in control statements to represent nested loops like:

  if [ condition1 ]; then
     if [ condition 2 ]; then
     fi
  fi

as:

  if [ condition1 ] && [ condition2 ]; then
  fi

[edit] Simple alarm clock - example code using control statements

The following code assumes bash environment with dialog utility present. $1 denotes value of first parameter. Infinite until loop in code line 18 drains memory after long time.

  #!/bin/bash
  #
  # Simple alarm clock
  # Author:mmmooonnnsssttteeerrr
  # GPL released
  time=`date +%H%M%S`
  default=060000
  quit=false
  alarm=$1
  help="Usage: alarm [%HH%MM%SS | default]\nDefault set to $default."
  if [ -z "$alarm" ]; then
      echo -e "$help"
      exit 1
  fi
  if [ "$1" = "default" ]; then
          alarm=$default
  fi
  until [ "$quit" = "true" ]; do
      dialog --title "alarm2" --infobox "Current time(%H%M%S)=$time\nAlarm set at $alarm. Ctrl+c to exit."  4 40
      sleep 1
      time=`date +%H%M%S`
      if [ "$time" = "$alarm" ]; then
          snooze=true
          until [ snooze = false ]; do
                  dialog --title "alarm2" --infobox "$alarm(%H%M%S) has arrived. Ctrl+c to exit." 3 70
                  echo -ne "\a"
                  sleep 1
          done
          quit=true
      fi
  done
  exit 0

[edit] Simple alarm clock2 - example code using control statements

This is yet another alarm program. This time code is more efficient because it uses sleep command instead of infinite loop, so there is less risk of memory drainage. The trap command at code line 7 traps signal INT (ctrl-c) and performs cleanUp function as after action.

#!/bin/bash
# Author: mmmooonnnsssttteeerrr
# GPL
cleanUp() {
        exit 1
}
trap cleanUp INT
help="Usage: alarm3 [%H] [%M]"
if [ -z $2 ]; then
        echo $help
        exit 1
fi
time=`date +%H`
alarm=$1
sleepfor=""
# Have to calculate how long to sleep.
# Hours
if [ $alarm -lt $time ]; then
        sleepfor=`expr 24 - $time + $alarm`
        sleepfor=`expr $sleepfor "*" 60`
else
        sleepfor=`expr $alarm - $time`
        sleepfor=`expr $sleepfor "*" 60`
fi
# Minutes
time=`date +%M`
alarm=$2
if [ $alarm -lt $time ]; then
        sleepfor=`expr $(($sleepfor + 60 - $time + $alarm)) "*" 60`
else
        sleepfor=`expr $(($sleepfor + $alarm - $time)) "*" 60`
fi
sleep $sleepfor
soundAlarm() {
        for beep in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
        do
                [ $beep = 20 ] &&  break
                echo -ne "\a"
                sleep 1
        done
}
soundAlarm
exit 0

[edit] Simple alarm clock 3

Actually, this code is the only one that does a reasonable job as an alarm clock. It is based on the following algorithm:

If a_h<T_h then (24-T_h+a_h)+a_m-T_m
If a_h>=T_h then (a_h-T_h)+a_m-T_m

where a_h is alarm_hour, T_h is Time_hour, a_m is alarm_minute, T_m is Time_minute.

#!/bin/bash
#
# Beeps at a given time accurate to +-1min
# mmmooonnnsssttteeerrr
#
# GPL
#
alarm_hour=$1
alarm_min=$2
alarm_min=`expr $alarm_min "*" 60`
time_hour=`date +%H`
time_min=`date +%M`
time_min=`expr $time_min "*" 60`
if [ -z $2 ]; then
       echo "alarm [%H] [%M]"
       exit 1
fi
trap `exit 1` INT
if [ $alarm_hour -lt $time_hour ]; then
       x=`expr $((24-$time_hour+$alarm_hour)) "*" 3600`
       sleepfor=`expr $x + $alarm_min - $time_min`
else
       y=`expr $((alarm_hour-time_hour)) "*" 3600`
       sleepfor=`expr $y + $alarm_min - $time_min`
fi
echo $sleepfor
sleep $sleepfor
for beep in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
do
       [ $beep = 20 ] && break
       echo -ne "\a"
       sleep 1
done
exit 0

[edit] expr command

[edit] expr 1 | expr 2

Expression 1 if expression 1 is non-zero, otherwise expression 2.

  expr 1 | expr 2
  1        0 -> Expression 1
  1        1 -> Expression 1
  0        0 -> Expression 2
  0        1 -> Expression 2

[edit] expr 1 & expr 2

Zero if either expression is zero, otherwise expression 1.

  expr 1 & expr 2
  0        0 -> 0
  1        0 -> 0
  0        1 -> 0
  1        1 -> Expression 1

[edit] Other operations in expr command

The expr command is useful in simple arithmetic operations using integers only.

  expr 1 + 1
  output:
  2

Similarly,

  expr 1 - 1
  expr 1 * 1
  expr 1 / 1
  expr 1 % 1

which have following results, respectively: 0; 1; 1; 0. Note that % is modulus.

[edit] eval command

The eval command is like an extra $ in shell scripting. It evaluates a string and attempts to expand it as a variable. For example the following evaluates the string as $horse and expands the value of variable horse.

  eval $"horse"

[edit] Regular expressions

Regular expressions are used generally to manipulate strings by external programs like find and grep.

[edit] Commonly used characters in regular expression

  ^ Anchor to beginning of line
  $ Anchor to end of line
  . Any single character
  [ ] Encloses pattern matching characters


[edit] Matching Characters

  [:alnum:] Alphanumeric characters
  [:alpha:] Letters
  [:ascii:] ASCII characters
  [:blank:] Space or tab
  [:cntrl:] ASCII control characters
  [:digit:] Digits
  [:graph:] Noncontrol, nonspace characters
  [:lower:] Lowercase letters
  [:print:] Printable characters
  [:punct:] Punctuation characters
  [:space:] Whitespace characters, including vertical tab
  [:upper:] Uppercase letters
  [:xdigit:] Hexadecimal digits

[edit] Extended grep mode

The -E mode may be used in grep with the following characters preceded by '\'.

  ? Match is optional but may be matched at most once
  "*" Must be matched zero or more times (without "")
  + Must be matched one or more times
  {n} Must be matched n times
  {n, } Must be matched n or more times
  {n,m} Must be matched between n and m times

[edit] Examples of regular expression in use

Look for text in file, example_text_file, ending with e:

  grep e$ example_text_file


Look for:

  Crazy Monkey
  Crazy Donkey
  Cranky Money

in another_text_file:

  grep -E Cra..\*[[:space:]][[:print:]]o... another_text_file

The above command tells grep to use extended mode, find "Cra", followed by any number of string, followed by space, followed by printable character, followed by "o", followed by three characters, and search this in another_text_file.

[edit] I/O Redirection

File descriptor (fd) 0 is stdin. Fd 1 is stdout. Fd 2 is stderr.

#!/bin/bash
tasklist=$1
exec 6<$tasklist
while read -u 6 raw
do
       # Do for each line
done
exec 6<&-

In the above example, exec globally opens $tasklist file for reading by assigning fd 6. The read command then reads fd 6 source line by line until end of file. The exec globally closes fd 6 in the last line.

[edit] Arrays

To assign a value to an element of an array simply do

ArrayName[2]=ValueForArrayNameIndex2

To expand the value of an index of an array do

${ArrayName[2]}   # This equals 'ValueForArrayNameIndex2'

The arrays don't need to be full or compact (ie. there can be null values between some interval of indexes).