Sunday 27 April 2014

Shell - For Loop

A 'for loop' is a bash programming language statement which allows code to be repeatedly executed.  For example, you can run UNIX command or task 5 times or read and process list of files using a for loop.

Bash by default support three types of loops 
for loop, while loopuntil loop.

for variable in [list]; do 
          action item
 done

 OR

for variable in [list]
do
  action item
done

argument ---- any variable that we set

argument is equal to the first element or each element in the list as it cycles through the list. It assigns value to the argument.

Exam:

1. 
for contries in INDIA JAPAN MALI
   do
       echo $contries
   done

2.
for VARIABLE in 1 2 3 4 5 .. N
do
command1
command2
commandN
done

OR

3.
for VARIABLE in file1 file2 file3
do
command1 on $VARIABLE
command2
commandN
done

OR

4.
for OUTPUT in $(Linux-Or-Unix-Command-Here)
do
command1 on $OUTPUT
command2 on $OUTPUT
commandN

done

For printing numbers from 1 to 100

Latest bash version 3.0+ has inbuilt support for setting up ranges.  The following example illustrates the use of "ranges" in a for-loop.

for i in {1..10}
do
echo "$i"

done

The expression "{1..10}" is a shorthand for the list "1 2 3 4 5 6 7 8 9 10". 


Bash v4.0+ has inbuilt support for setting up a step value using {START..END..INCREMENT} 


#!/bin/bash
echo "Bash version ${BASH_VERSION}..."
for i in {0..10..2}
  do
     echo "Welcome $i times"
 done
Following is the example to display all the files starting with .bash and available in your home.

#!/bin/sh

for FILE in $HOME/.bash*
do
   echo $FILE
done
This will produce following result:
/root/.bash_history
/root/.bash_logout
/root/.bash_profile
/root/.bashrc

The for statement is used when you want to loop through a list of items. The body of the loop is put between do and done. Let's say that we want to write a program that will validate numbers in a given list. These numbers can be loaded from a file, hard coded, or manually entered by the user. For our example, we will ask the user for a list of numbers separated with spaces. We will validate each number and make sure that it is between 1 and 100. The best way to write a program like this would be to use a for loop. 

#!/bin/sh
# Validate numbers...

echo "Please enter a list of numbers between 1 and 100. "
read NUMBERS

for NUM in $NUMBERS
do
 if [ "$NUM" -lt 1 ] || [ "$NUM" -gt 100 ]; then
  echo "Invalid Number ($NUM) - Must be between 1 and 100!"
 else
  echo "$NUM is valid."
 fi
done

In some cases it is more suitable to use a while-loop instead of a for-loop. The following example does essentially the same the for-loop above:


#!/bin/bash
count=1
while [[ $count -le 9 ]]
do
    echo "$count"
    (( count++ ))
done

-------------------------------------------------------

[carol@octarine ~/html] cat html2php.sh

#!/bin/bash
# specific conversion script for my html files to php
LIST="$(ls *.html)"
for i in "$LIST"; do
     NEWNAME=$(ls "$i" | sed -e 's/html/php/')
     cat beginfile > "$NEWNAME"
     cat "$i" | sed -e '1,25d' | tac | sed -e '1,21d'| tac >> "$NEWNAME"
     cat endfile >> "$NEWNAME"
done

---------------------------------------------------------------

 Using Arguments

Example 10. Shell Script Arguments

#!/bin/bash
# example of using arguments to a script
echo "My first name is $1"
echo "My surname is $2"
echo "Total number of arguments is $#" 

----------------- ---------------- --------------- -------------
echo "$0 counts the lines of code" 
l=0
n=0
s=0
for f in $*
do
l=`wc -l $f | sed 's/^\([0-9]*\).*$/\1/'`
echo "$f: $l"
        n=$[ $n + 1 ]
        s=$[ $s + $l ]
done

   Index Of Commands

--> for a in $@ Every input

--> $1, $2, ... First input, second input, ... $0 Name of script (command you typed to run it.) $# Number of inputs

--> $@ All inputs (1-end) with a space between $? Return value of last command (tricky to use.)

--> # Comment. Must be first thing in line

--> $abc Look up value of abc variable

--> $(abc) Run command "abc". $($abc) looks up abc and runs it.

--> ${abc} {}'s are optional parenthesis. They are needed for parms past 9 (${10}).

--> [ ] Short cut for test

--> ${1:3} Substring of $1, starting at 3rd position. ${abc:2:4} Substring of abc, starting at 2nd position and using next 4 characters.


--> "read a" Read one line from keyboard and put in variable a

--> 'abc' (quotes near ENTER): Treat this as a bunch of letters.

--> "abc" (double quotes): Expend all $-rules, but otherwise treat as a bunch of letters.

--> `abc` (back-tic [upper-left]): same as $(abc). Runs command. eval abc: Evaluate. Also same as $(abc).

-------------------- -------------------       ---------------------------------

for a in 4 8 3 7 13 4
do
  echo -n $a"  "
  if test $a -ge 5
  then
    echo "big"
  else
    echo "small"
  fi
done

---------------------------------------- ------------------------------------- ------------------------------

if [ $# -ne 1 -o ! -f $1 ]
then
  echo "Usage: $0 FILENAME"
  exit 1
fi

words=0
for a in $(cat $1)
do
#  echo "["$a"]"
words=$((1+$words))
done

echo "Num of words is" $words

$(cat $1) simply "pastes" that file after for a in, which then looks at every word. 
Note the output of cat is automatically redirected (it does not go to the screen.) words is a standard counter, with one added for each word, in the awkward script manner. If you have a lot of math, use a more formal programming language.

The first part is a standard check: if there is not one input, or it is not a file (-o (letter O) is or, ! is not -- see %info test).
------------------------------------- ------------------------------- ----------------

  Three-expression bash for loops syntax

It is characterized by a three-parameter loop control expression; 
consisting of an initializer (EXP1), a loop-test or condition (EXP2), and a counting expression (EXP3). 

for (( EXP1; EXP2; EXP3 ))
do
command1
command2
command3
done

#!/bin/bash
for (( c=1; c<=5; c++ ))
do
   echo "Welcome $c times"
done

-------------  ----------------------- --------------

$ cat for4.sh
i=1
for username in `awk -F: '{print $1}' /etc/passwd`
do
 echo "Username $((i++)) : $username"
done

$ ./for4.sh
Username 1 : ramesh
Username 2 : john
Username 3 : preeti
Username 4 : jason
----------------------------- --------------------------- ----------- 

  Loop through files and directories in a for loop

To loop through files and directories under a specific directory, just cd to that directory, and give * in the for loop as shown below.

The following example will loop through all the files and directories under your home directory.

$ cat for5.sh
i=1
cd ~
for item in *
do
 echo "Item $((i++)) : $item"
done

$ ./for5.sh
Item 1 : positional-parameters.sh
Item 2 : backup.sh
Item 3 : emp-report.awk
Item 4 : item-list.sed
Item 5 : employee.db
Item 8 : storage
Item 9 : downloads
Usage of * in the bash for loop is similar to the file globbing that we use in the linux command line when we use ls command (and other commands).

For example, the following will display all the files and directories under your home directory. This is the concept that is used in the above for5.sh example.

cd ~
ls *