sttp/ult/Section_5.rst
changeset 0 27e1f5bd2774
equal deleted inserted replaced
-1:000000000000 0:27e1f5bd2774
       
     1 Module Objectives:
       
     2 ==================
       
     3 
       
     4 After successfully completing this module a participant will be able to: ::
       
     5 
       
     6   - Understand
       
     7     * What are archives and zipped files                              U
       
     8     * What are environment variables				      U
       
     9     * What are Shell Scripts					      U
       
    10   - Able to use file comparison commands like 			      Ap
       
    11     diff, cmp, comm
       
    12   - Create and extract archives(.tar files) and zipped files(.gz)     Ap
       
    13   - Set/Modify environment as per need	    	                      Ap
       
    14   - Create shell scripts to automate tasks.			      Ap
       
    15 
       
    16 tar:
       
    17 ====
       
    18 
       
    19 Introduction:
       
    20 -------------
       
    21 
       
    22 In world of Linux based distribution, *tarballs* is the term which pops up very often. It is part of the GNU project and comes as part of every distribution of GNU/Linux. Tarball is like defacto standard for releasing source code for free software. Some of common use of *tar* archives is to: *Store, backup, and transport*.
       
    23 
       
    24 GNU tar creates and manipulates archives which are actually collections of many other files; the program provides users with an organized and systematic method for controlling a large amount of data. It is basically form of creating archive by concatenating one or more files. 
       
    25 
       
    26 Getting Started(go go go!):
       
    27 ---------------------------
       
    28 
       
    29 As mentioned previously and if not, *The best way to get started with any command line tool of Linux is to use "man".* ::
       
    30 
       
    31    $ man tar
       
    32 
       
    33 or try these commands(the output may vary with different installations): ::
       
    34 
       
    35    $ tar --version
       
    36    tar (GNU tar) 1.20
       
    37    Copyright (C) 2008 Free Software Foundation, Inc.
       
    38    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
       
    39    This is free software: you are free to change and redistribute it.
       
    40    There is NO WARRANTY, to the extent permitted by law.
       
    41 
       
    42    Written by John Gilmore and Jay Fenlason.
       
    43 
       
    44    $ tar --help
       
    45    Usage: tar [OPTION...] [FILE]...
       
    46    GNU `tar' saves many files together into a single tape or disk archive, and can
       
    47    restore individual files from the archive.
       
    48    Examples:
       
    49    tar -cf archive.tar foo bar  # Create archive.tar from files foo and bar.
       
    50    tar -tvf archive.tar         # List all files in archive.tar verbosely.
       
    51    tar -xf archive.tar          # Extract all files from archive.tar.  
       
    52    ____________
       
    53 
       
    54 Creating a tar archive: 
       
    55 ~~~~~~~~~~~~~~~~~~~~~~~
       
    56 
       
    57 We will do some off-the road activity for this exercise. We will use an interesting command *fortune* for creating our practice files and then performing archives of those files and directories. Content of the files would vary for users, as fortune works like that. ::
       
    58 
       
    59    $ mkdir fortune-files 
       
    60    $ cd fortune-files/
       
    61    $ fortune > first.txt
       
    62    $ cat first.txt 
       
    63    Expect the worst, it's the least you can do.
       
    64    $ fortune > second.txt
       
    65    $ fortune > third.txt
       
    66    $ ls
       
    67    first.txt  second.txt  third.txt
       
    68 
       
    69 By now we have three txt files, with some random fortune content. To create a tar archive of these files we can use any of following commands according to ones convenience: ::
       
    70 
       
    71    $ tar --create --verbose --file=allfiles.tar first.txt second.txt third.txt
       
    72    first.txt
       
    73    second.txt
       
    74    third.txt
       
    75    $ ls
       
    76    allfiles.tar  first.txt  second.txt  third.txt
       
    77 
       
    78 allfiles.tar is our required tar archive of all the rest of files(or archive of files mentioned in command line). Other form of the previous command are: ::
       
    79 
       
    80    $ tar -c -v -f allfiles.tar first.txt second.txt third.txt
       
    81 
       
    82 or ::
       
    83 
       
    84    $ tar -cvf allfiles.tar first.txt second.txt third.txt
       
    85    
       
    86 The general format for creating a tar archive is: ::
       
    87    
       
    88    tar [OPTION...] [FILE]... 
       
    89 
       
    90 For our command are using these options:
       
    91 
       
    92    * -c to Create the archive.
       
    93    * -v for Verbose mode, to get the names of the files as they are archived.
       
    94    * -f mentioning the file name of the resulting tar archive.
       
    95 
       
    96 To create archive of folder itself try this: ::
       
    97    
       
    98    $ tar -cvf fortune.tar fortune/
       
    99 
       
   100 To add files to existing tar archive, option *`r`* is used: ::
       
   101 
       
   102    $ fortune > fourth.txt
       
   103    $ tar -r fourth.txt -vf allfiles.tar
       
   104    fourth.txt
       
   105 
       
   106 There are other options too available for explicitly mentioning the position of archive, use *tar --help* for getting all the details.
       
   107 
       
   108 Similarly to remove file from archive use *--delete* option: ::
       
   109 
       
   110    $ tar --delete second.txt -f allfiles.tar
       
   111    $ tar -tf allfiles.tar
       
   112    first.txt
       
   113    third.txt
       
   114    fourth.txt
       
   115 
       
   116 Listing the files of archive:
       
   117 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       
   118 
       
   119 Once files are archived, tar command have the *`t`* option, for Listing all files in the tar file: ::
       
   120 
       
   121    $ tar tf allfiles.tar
       
   122    first.txt
       
   123    second.txt
       
   124    third.txt
       
   125 
       
   126 **//this is not working for me in some cases :(**
       
   127 
       
   128 To locate a particular file among the archive mention its name after *t* option. ::
       
   129 
       
   130    $ tar t second.txt allfiles.tar
       
   131    second.txt
       
   132 
       
   133 one can also use elementary regex for locating the file, so in previous case even second.* will also return the same result.
       
   134 
       
   135 Extracting files from archive:
       
   136 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       
   137 
       
   138 To extract the content from a archive, use *`x`* option: ::
       
   139 
       
   140    $ mkdir extract
       
   141    $ cp allfiles.tar extract/
       
   142    $ cd extract
       
   143    $ tar -xvf allfiles.tar 
       
   144    first.txt
       
   145    second.txt
       
   146    third.txt
       
   147 
       
   148 To extract any particular file from archive, mention the name of file after *x* option: ::
       
   149 
       
   150    $ tar -x second.txt -vf allfiles.tar 
       
   151    second.txt
       
   152    
       
   153     
       
   154 Further Reading for this section:
       
   155 ---------------------------------	
       
   156 	
       
   157 	* http://en.wikipedia.org/wiki/Tar_(file_format)
       
   158 	* http://www.gnu.org/software/tar/manual/tar.html 
       
   159 	* http://linuxreviews.org/beginner/
       
   160 
       
   161 GZip:
       
   162 =====
       
   163 
       
   164 Tar creates archives but it does not compress data by itself unless specified explicitly. Hence all the archive we create using tar command, is simply of the size of total size of all individual files. With Linux there is a compression tool known as *gzip* which is used to reduce the size of files mentioned. Whenever possible, each file is replaced by one with the extension `.gz', so unlike `tar` this command would *replace the existing file*.
       
   165 
       
   166 Get going:
       
   167 ----------
       
   168 
       
   169 As usual first commands to check out with gzip are *man* and *help*, ::
       
   170 
       
   171     $ man gzip
       
   172     $ gzip --help
       
   173 
       
   174 Creating a zip of a bunch of files is fairly trivial, it can be done simply via: ::
       
   175 
       
   176     $ gzip [OPTION]... [FILE]...
       
   177     
       
   178 Creating zip files:
       
   179 ~~~~~~~~~~~~~~~~~~~
       
   180 
       
   181 Continuing from previous set of files and setup, we will like to zip them and hence the command would be: ::
       
   182 
       
   183     $ gzip first.txt fourth.txt second.txt third.txt
       
   184     $ ls
       
   185     allfiles.tar  first.txt.gz  fourth.txt.gz  second.txt.gz  third.txt.gz  zipped.tar.gz
       
   186 
       
   187 Hence, as mentioned above, all original files are replaced by .gz extension. The above task can also be restated and made easy with help of some common regex expression: ::
       
   188 
       
   189     $ gzip *.txt
       
   190 
       
   191 Similar to *tar* command, one can also use *`-v`* option here to see the output in *verbose* form. For the previous example, if we enable this option the result would be something like this: ::
       
   192 
       
   193     $ gzip -v *.txt
       
   194     first.txt:	  4.4% -- replaced with first.txt.gz
       
   195     fourth.txt:	 -7.1% -- replaced with fourth.txt.gz
       
   196     second.txt:	 -4.8% -- replaced with second.txt.gz
       
   197     third.txt:	  3.8% -- replaced with third.txt.gz    
       
   198 
       
   199 For files of very small sizes and some other cases, one might end up with a zipped file whose size is greater then original file, but compression is always performed(so don't be disheartened in the above case, as files are larger :P). So unlike tar, here all files are zipped separately by default, to make them part of one single chunk one can use some *pipes* and *redirections* ::
       
   200 
       
   201     $ gzip -c *.txt > all.gz 
       
   202 
       
   203 Now in this case, all files would be zipped, concatenated and then the output would be written to a file all.gz leaving back all the original files. In the command above *`-c`* option states to print the output to standard output(stdout) and following *`>`* would redirect the output to file all.gz. So when we decompress this file, we will get a single file named 'all' with all the content of each files concatenated one after the another. 
       
   204 
       
   205 For creating a zip archive of a complete directory, one has to use *`-r`* options which means recursive, it makes gzip to traverse through all directory tree/structure. By default it will create zip files of each file inside the directory, that is even with the -r flag, gzip still compresses one file at a time : ::
       
   206 
       
   207     $ gzip -r fortune-files/
       
   208     $ gzip -rv .
       
   209     ./first.txt:	  4.4% -- replaced with ./first.txt.gz
       
   210     ./second.txt:	 -4.8% -- replaced with ./second.txt.gz
       
   211     ./third.txt:	  3.8% -- replaced with ./third.txt.gz
       
   212     ./allfiles.tar:	 96.6% -- replaced with ./allfiles.tar.gz
       
   213     ./fourth.txt:	 -7.1% -- replaced with ./fourth.txt.gz
       
   214 
       
   215 Hence one always sees files like xxxxx.tar.gz, to create a zip of whole directory in a single file, first archive everything inside a folder and then use gzip on that. For zipping the files using tar itself, one has to use the option *`g`*. ::
       
   216 
       
   217     $ tar -cvzf zipped.tar.gz *.txt 
       
   218     first.txt
       
   219     fourth.txt
       
   220     second.txt
       
   221     third.txt
       
   222 
       
   223 *Thats why gzip is designed as a complement to tar, not as a replacement.*
       
   224 
       
   225 gzip command comes with a option *`-l`* to view the compressed file contents: ::
       
   226 
       
   227     $ gzip -l zipped.tar.gz
       
   228              compressed        uncompressed  ratio uncompressed_name
       
   229                 332               10240      97.0% zipped.tar
       
   230 
       
   231 Other feature of gzip is option for mentioning the kind of compression one wants. There is a option of *`-n`* where *n varies from 0 to 9* which regulate the speed/quality of compression. With *`-1`* or *`--fast`* option it means the fastest compression method (less compression) and *`--best`* or *`-9`* indicates the slowest compression method, default compression level is *`-6`*.
       
   232 
       
   233 To decompress a already compressed file there are two options, either use *`gunzip`* command or use *`-d`* option with gzip command: ::
       
   234 
       
   235     $ gzip -dv *.gz 
       
   236     all.gz:	-440.4% -- replaced with all
       
   237     first.txt.gz:	  4.4% -- replaced with first.txt
       
   238     fourth.txt.gz:	 -7.1% -- replaced with fourth.txt
       
   239     second.txt.gz:	 -4.8% -- replaced with second.txt
       
   240     third.txt.gz:	  3.8% -- replaced with third.txt
       
   241     zipped.tar.gz:	 97.0% -- replaced with zipped.tar
       
   242 
       
   243 or: ::
       
   244     
       
   245     $ gunzip -v *.gz
       
   246 
       
   247 Both of those commands will give the same result. So here one can notice the content of file "all" which we created earlier, it will have content of all the rest of four files concatenated one after another, but "zipped.tar.gz" is zip of tar of all files, will effectively have zip of archives of all files separately, and hence the usage and importance of *tar*.
       
   248 
       
   249 Further Reading for this section:
       
   250 ---------------------------------
       
   251 
       
   252 	* http://linuxreviews.org/beginner/
       
   253 	* http://lowfatlinux.com/linux-gzip-gunzip.html
       
   254 	* http://www.gnu.org/software/gzip/manual/gzip.html
       
   255 	* http://en.wikipedia.org/wiki/ZIP_(file_format)
       
   256 
       
   257 
       
   258 File Comparisons:
       
   259 =================
       
   260 
       
   261 Linux based distributions also have some utilities for checking the content of files, comparing them very quickly to other files. These operations can be looking for differences/similarities. Some of the commands which prove handy are: 
       
   262 
       
   263 cmp:
       
   264 ----
       
   265 
       
   266 If one wants to compare two files whether they are same or not, one can use this handy tool. Let us consider some situation, we run find/locate command to locate some file, and it turns out that we have a file with same name in different location, and in case we want to run a quick check on there content, cmp is the right tool. For my system I perform these tasks to illustrate the use of this command: ::
       
   267 
       
   268    $ find . -name quick.c
       
   269    ./Desktop/programs/quick.c
       
   270    ./c-folder/quick.c
       
   271    $ cmp Desktop/programs/quick.c c-folder/quick.c
       
   272    $
       
   273 
       
   274 For me it returns nothing, hence that means both the files are exact copy of each other, by default, cmp is silent if the files are the same. Make some changes in one of the file and rerun the command. For me it works like this: ::
       
   275 
       
   276    $ cmp Desktop/programs/quick.c c-folder/quick.c
       
   277    Desktop/programs/quick.c c-folder/quick.c differ: byte 339, line 24
       
   278 
       
   279 That is, if files differ, the byte and line number at which the first difference occurred is reported. 
       
   280  
       
   281 diff:
       
   282 -----
       
   283 
       
   284 Now there are situations when one wants to exactly know the differences among two files, for them, GNU diff can show whether files are different without detailing the differences. For simple and basic usage of this programs, consider following example: ::
       
   285 
       
   286     $ echo -e "quick\nbrown\nfox\njumped\nover\nthe\nlazy\ndog" > allcharacters.txt
       
   287     $ echo -e "quick\nbrown\nfox\njmuped\nover\nteh\nlzay\ndog" > problem.txt
       
   288     $ diff problem.txt allcharacters.txt 
       
   289     4c4
       
   290     < jmuped
       
   291     ---
       
   292     > jumped
       
   293     6,7c6,7
       
   294     < teh
       
   295     < lzay
       
   296     ---
       
   297     > the
       
   298     > lazy
       
   299 
       
   300 Looking at results above mentioned it is very trivial to deduce that, diff if used on two separate text files will result in line by line results for all the lines which are different. So most common use case scenario can be, got some files in various location of system with same name and size, just run diff through them and remove all the redundant files. Other similar command which one can find more effective for this can be *sdiff*, for the same files using sdiff will result in: ::
       
   301 
       
   302     $ sdiff problem.txt allcharacters.txt 
       
   303     quick								quick
       
   304     brown								brown
       
   305     fox									fox
       
   306     jmuped							      |	jumped
       
   307     over								over
       
   308     teh								      |	the
       
   309     lzay							      |	lazy
       
   310     dog								      	dog   
       
   311 
       
   312 Some exercise for a change:
       
   313  
       
   314      * Try using diff for any binary file, does it work? 
       
   315      * What are other equivalent for diff command based on needs/requirements?
       
   316      * Can we use diff to compare two directories? If yes how?
       
   317 
       
   318 comm:
       
   319 -----
       
   320 
       
   321 This is one more command which proves handy at times, the short and sweet man page states "compare two sorted files line by line". Or this it compares sorted files and selects or rejects lines common to two files. For ex: ::
       
   322 
       
   323    $ sort allcharacters.txt>sortedcharac.txt; sort problem.txt>sortedprob.txt
       
   324    $ comm sortedcharac.txt sortedprob.txt 
       
   325 		brown
       
   326 		dog
       
   327 		fox
       
   328 	jmuped
       
   329    jumped
       
   330    lazy
       
   331 	lzay
       
   332 		over
       
   333 		quick
       
   334 	teh
       
   335    the
       
   336 
       
   337 Environment Variables:
       
   338 ======================
       
   339 
       
   340 These variables like HOME, OSTYPE,Variables are a way of passing information from the shell to programs when you run them. Programs look "in the environment" for particular variables and if they are found will use the values stored. Standard UNIX variables are split into two categories, environment variables and shell variables. In broad terms, shell variables apply only to the current instance of the shell and are used to set short-term working conditions; environment variables have a farther reaching significance, and those set at login are valid for the duration of the session.By convention, environment variables have UPPER CASE and shell variables have lower case names.  
       
   341 
       
   342 Some of examples of Environment variables are(result may vary!): ::
       
   343 
       
   344    $ echo $OSTYPE 
       
   345    linux-gnu
       
   346    $ echo $HOME
       
   347    /home/baali 
       
   348 
       
   349 To see all the variables and there values use any of following commands: ::
       
   350 
       
   351    $ printenv | less
       
   352    $ env
       
   353 
       
   354 The most commonly used environment variable is "PATH", it defines a list of directories to search through when looking for a command to execute. If you decide to put your own programs in a bin directory under your home directory, you'll have to modify the path to include that directory, or the system will never find your programs (unless you happen to be in that directory when you enter the command). Here's how to change your PATH variable so it includes your personal bin directory: ::
       
   355 
       
   356    $ set PATH=$PATH:$HOME/bin
       
   357 
       
   358 See the difference in value of PATH variable before and after modifying it. One can also create its own variable to make things easier: ::
       
   359 
       
   360    $ set repo = $HOME/Desktop/random/code
       
   361    $ cd $repo
       
   362 
       
   363 *set* command is used to define a variable for the current shell. Try opening a new shell and use the above mentioned command, it wont work as expected. The other child process wont be able to see these variables unless we *export* them. Repeat the above mentioned activity with *export* command. Now with all new shells, *$repo* will work.
       
   364 
       
   365 Again these changes are limited to current session. To make them permanent or get loaded each time you log in, just add those lines to *.bashrc* file. 
       
   366 
       
   367 Further Reading:
       
   368 ----------------
       
   369 
       
   370 	* http://lowfatlinux.com/linux-environment-variables.html
       
   371 	* http://www.codecoffee.com/tipsforlinux/articles/030.html
       
   372 	* http://www.ee.surrey.ac.uk/Teaching/Unix/unix8.html
       
   373 	* http://en.wikipedia.org/wiki/Environment_variable	
       
   374 
       
   375 
       
   376 Shell Scripting:
       
   377 ================
       
   378 
       
   379 Basics:
       
   380 -------
       
   381 
       
   382 Shell program or shell script,a sequence of commands to a text file and tell the shell to execute the text file instead of entering the commands. The first *"Hello World"* sample for shell scripting is as easy as it sounds: ::
       
   383 
       
   384    $ echo '#!/bin/sh' > my-script.sh
       
   385    $ clear >> my-script.sh   
       
   386    $ echo 'echo Hello World' >> my-script.sh
       
   387    $ chmod 755 my-script.sh
       
   388    $ ./my-script.sh
       
   389    Hello World
       
   390 
       
   391 The #! syntax(also known as shebang) is used in scripts to indicate an interpreter for execution under UNIX / Linux operating systems. The chmod is required to make the script executable. This script will just execute two commands, *clear* and *echo* one after another. One can also do the same task using a one liner command *clear; echo 'Hello World';* but as number of lines grows using a script file is helpful. 
       
   392 
       
   393 So lets create a script which gives us all the filenames for given initial alphabet or string in a directory. Let the name of script be *initial.sh*, open it with text editor, and write: ::
       
   394 
       
   395    #!/bin/sh
       
   396    ls > temp
       
   397    grep ^$1 < temp
       
   398    rm temp
       
   399    $ chmod a+x initial.sh
       
   400    $ ./initial.sh s
       
   401 
       
   402 The $1 in the script is pertaining to command line argument. All arguments passed via command line are accessed via *$#* with name of script being first member, that is $0. Now lets write a script for finding a file, and then checking when was it last modified: ::
       
   403 
       
   404    #!/bin/sh
       
   405    name=`find . -name $1 -print`
       
   406    echo $name
       
   407    last_modified=`stat -c %y $name| cut -f 1 -d " "`
       
   408    echo "Last modified: $last_modified"    
       
   409    $ ./search.sh fname
       
   410 
       
   411 Try giving some file you want to search in place of fname. Please note in second line *`* its a back-quote(other key mapped with tilda), it is specifically used to get the output of one command into a variable. In this particular case name is a User defined variables (UDV) which stores the value. We access value stored in any variable using *$* symbol before name of variable.
       
   412 
       
   413 naming conventions for variables?? do we need them??
       
   414 
       
   415 Shell Arithmetic:
       
   416 -----------------
       
   417 
       
   418 Shell also provides support for basic arithmetic operations. The syntax is: ::
       
   419 
       
   420    $ expr op1 math-operator op2
       
   421 
       
   422 Some of example which can be tried handily: ::
       
   423    
       
   424    $ expr -3 + 5
       
   425    2
       
   426    $ expr 10 % 3
       
   427    1
       
   428 
       
   429 These spaces in between operator and operands is important, without them shell interpreter will raise the syntax error. ::
       
   430 
       
   431    $ expr 2*3
       
   432    expr: syntax error
       
   433    
       
   434 One can use back-quotes(`) also to get value of expr. ::
       
   435 
       
   436    $ echo `expr 6 + 3`
       
   437    9
       
   438    $ result=`expr 6 + 3`
       
   439    $ echo $result
       
   440    9
       
   441 
       
   442 Shell uses three kinds of quotes. Double quotes("), anything enclosed among them except from variable trailing after $, and characters after \ would be printed as it is. Single quotes('), anything enclosed within them is just same, no formulation/interpretation. Back quotes(`), anything inclosed is considered as command, or is executed. ::
       
   443 
       
   444    $ echo "Today is date"
       
   445    Today is date
       
   446    $ echo "Today is `date`"
       
   447    Today is Wed Sep 16 17:32:22 IST 2009
       
   448    $ echo 'Today is `date`'
       
   449    Today is `date`
       
   450    $ echo "Today is \n `date`"
       
   451    Today is \n Wed Sep 16 17:40:13 IST 2009
       
   452    $ echo -e "Today is \n `date`"
       
   453    Today is 
       
   454     Wed Sep 16 17:41:13 IST 2009 
       
   455 
       
   456 if else construct:
       
   457 ------------------
       
   458 
       
   459 One can have simple *if else if* constructs in shell scripts to check conditions. Lets take simple example of writing a script which returns back whether the argument passed is positive or not: ::
       
   460 
       
   461    #!/bin/sh
       
   462    if test $1 -gt 0
       
   463    then
       
   464      echo "number is positive"
       
   465    else
       
   466      echo "number is negative"
       
   467    fi
       
   468    $ ./sign.sh -11
       
   469    number is negative
       
   470 
       
   471 This script will compare the first value passed as argument with 0 *if test var -gt val*, var being $1 and val being 0, gt meaning greater then. Now this program has some flaw, it will give same result for following input: (-11) and (-1, 5), as we are checking just $1 which is first argument and hence the result. For handling such situation we can include *if-else* clause which will warn user of correct usage of script. ::
       
   472 
       
   473    #this is the case when no argument is passed  
       
   474    if [ $# -eq 0 ]
       
   475    then
       
   476      echo "$0 : You must give/supply one integers"
       
   477      exit 1
       
   478    else 
       
   479      if [ $# -gt 1 ]
       
   480      then
       
   481        echo "$0 : You must give one integer"
       
   482        exit 1
       
   483      fi
       
   484    fi
       
   485 
       
   486 One important thing to not in shell script is spacing, with many comparison and evaluation operation a wrongly placed space will spoil all the fun. So in previous example the expression *[ $# -eq 0 ]* will work properly, but if we remove those leading or trailing spaces like *[ $# -eq 0]*, it wont work as expected, or rather throw a warning. Both *test* and *[]* do the same task of testing a expression and returning true or false.
       
   487 
       
   488 Lets create something interesting using these if-else clause. Now we will create a script which will greet the user when he opens the shell. We will create the script, change the permission to make it executable and append the *.bashrc* file with *./greet.sh* line and we are done. The script is: ::
       
   489 
       
   490    #!/bin/sh
       
   491    #Script to greet the user according to time of day
       
   492    temph=`date | cut -c12-13`
       
   493    dat=`date +"%A %d in %B of %Y (%r)"`
       
   494    if [ $temph -lt 12 ]
       
   495    then
       
   496      mess="Good Morning $LOGNAME, Have a nice day!"
       
   497    fi
       
   498 
       
   499    if [ $temph -gt 12 -a $temph -le 16 ]
       
   500    then
       
   501      mess="Good Afternoon $LOGNAME"
       
   502    fi
       
   503 
       
   504    if [ $temph -gt 16 -a $temph -le 18 ]
       
   505    then
       
   506      mess="Good Evening $LOGNAME"
       
   507    fi
       
   508    echo -e "$mess\nThis is $dat"
       
   509 
       
   510 For me when I open the shell the output is something like: ::
       
   511 
       
   512    Good Morning baali, Have a nice day!
       
   513    This is Wednesday 16 in September of 2009 (11:54:47 AM IST) 
       
   514 
       
   515 Loops
       
   516 -----
       
   517 
       
   518 Bash has three different commands for looping -- ``for``, ``while`` and ``until``. 
       
   519 
       
   520 ``for`` loop
       
   521 ~~~~~~~~~~~~
       
   522 
       
   523 Suppose we have a set of files, that have names beginning with numbers followed by their names - ``08 - Society.mp3``. We would like to rename these files to remove the numbering. How would we go about doing that? It is clear from the problem statement that we could use a ``for`` loop, to loop through the list of files and rename each of the files.  
       
   524 
       
   525 Let's first look at a simple ``for`` loop, to understand how it works. 
       
   526 ::
       
   527 
       
   528   for animal in rat cat dog man
       
   529   do 
       
   530     echo $animal
       
   531   done
       
   532 
       
   533 We just wrote a list of animals, each animal's name separated by a space and printed each name on a separate line. The variable ``animal`` is a dummy variable and has no significance. You could use something as lame as ``i`` in place of ``animal``.  
       
   534 
       
   535 Now, we use a simple ``for`` loop to list the files that we are interested in. 
       
   536 ::
       
   537 
       
   538   ls *.mp3 > list
       
   539   for i in `cat list`
       
   540   do
       
   541     echo "$i"
       
   542   done
       
   543 
       
   544 If your filenames contain spaces, ``for`` assumes each space separated word to be a single item in the list and prints it in a separate line. We could change the script slightly to overcome this problem. 
       
   545 ::
       
   546 
       
   547   for i in *.mp3
       
   548   do
       
   549     echo "$i"
       
   550   done
       
   551 
       
   552 Now, we have each file printed on a separate line. Depending on the files that we have we could use grep to get the relevant portion of the filenames and rename the files. 
       
   553 ::
       
   554 
       
   555   for i in *.mp3
       
   556   do 
       
   557     j=$(echo "$i"|grep -o "[A-Za-z'&. ]*.mp3")
       
   558     echo "$i -> $j"
       
   559   done
       
   560 
       
   561 Now we just replace the echo command with a ``mv`` or a ``cp`` command. 
       
   562 ::
       
   563 
       
   564   for i in *.mp3
       
   565   do 
       
   566     j=$(echo "$i"|grep -o "[A-Za-z'&. ]*.mp3")
       
   567     cp "$i" "$j"
       
   568   done
       
   569 
       
   570 As an exercise, you could try sorting the files in reverse alphabetical order and then prefix numbers to each of the filenames.  
       
   571 
       
   572 ``while``
       
   573 ~~~~~~~~~
       
   574 
       
   575 The ``while`` command allows us to continuously execute a block of commands until the command that is controlling the loop is executing successfully. 
       
   576 
       
   577 Let's start with the lamest example of a while loop.
       
   578 ::
       
   579 
       
   580   while true
       
   581   do
       
   582     echo "True"
       
   583   done
       
   584 
       
   585 This, as you can see, is an infinite loop that prints the ``True``. 
       
   586 
       
   587 Say we wish to write a simple program that takes user input and prints it back, until the input is ``quit``, which quits the program. 
       
   588 ::
       
   589 
       
   590   while [ "$variable" != "quit" ]
       
   591   do
       
   592     read variable
       
   593     echo "Input - $variable"
       
   594   done
       
   595   exit 0
       
   596 
       
   597 ``until``
       
   598 ~~~~~~~~~
       
   599 
       
   600 The ``until`` loop is similar to the ``while`` loop, except that it executes until the conditional command does not execute properly. 
       
   601 
       
   602 The infinite loop changes to the following, when ``until`` is used.
       
   603 ::
       
   604 
       
   605   until false
       
   606   do
       
   607     echo "True"
       
   608   done
       
   609 
       
   610 Now lets try and use these above mentioned options provided by shell to write a utility. Until now, when we try find or locate it looks through directories and files for result. But they wont search through tar archives and zipped files. Lets create a shell script for especially looking through these files
       
   611 ::
       
   612 
       
   613   #!/bin/sh
       
   614 
       
   615   #To check number of arguments being passed.
       
   616   if [ $# -eq 0 ] ; then
       
   617   echo "Correct usage: $0 tar-archive filename \nOr $0 filename"
       
   618   exit 1
       
   619   else
       
   620     if [ $# -eq 1 ] ; then
       
   621       tar_archive=`find $PWD -name "*.tar*"`
       
   622     else
       
   623       tar_archive=`find $PWD -name $1`
       
   624     fi
       
   625   fi
       
   626 
       
   627   #Search of particular file inside archives.
       
   628   for archive in $tar_archive
       
   629   do
       
   630     echo $archive
       
   631     variable=`tar -tf $archive`
       
   632     for word in $variable
       
   633     do
       
   634       if [ $# -eq 1 ] ; then
       
   635         echo "$word" | grep -q ".*$1"
       
   636       else
       
   637 	echo "$word" | grep -q ".*$2"
       
   638       fi
       
   639     if [ $? -eq 0 ] ; then 
       
   640       echo "File present in $archive!" 
       
   641     fi  
       
   642     done
       
   643   done
       
   644 
       
   645 
       
   646 Functions
       
   647 ---------
       
   648 
       
   649 When a group of commands are repeatedly being used within a script, it is convenient to group them as a function. This saves a lot of time and you can avoid retyping the code again and again. Also, it will help you maintain your code easily. Let's see how we can define a simple function, ``hello-world``. Functions can be defined in bash, either using the ``function`` built-in followed by the function name or just the function name followed by a pair of parentheses. 
       
   650 ::
       
   651 
       
   652   function hello-world
       
   653   { 
       
   654   echo "Hello, World."; 
       
   655   }
       
   656 
       
   657   hello-world () {
       
   658     echo "Hello, World.";
       
   659   }
       
   660 
       
   661   $ hello-world
       
   662   Hello, World.
       
   663 
       
   664 Passing parameters to functions is similar to passing them to scripts. 
       
   665 ::
       
   666 
       
   667   function hello-name
       
   668   { 
       
   669   echo "Hello, $1."; 
       
   670   }
       
   671 
       
   672   $ hello-name 9
       
   673   Hello, 9.
       
   674 
       
   675 Any variables that you define within a function, will be added to the global namespace. If you wish to define variables that are restricted to the scope of the function, define a variable using the ``local`` built-in command of bash.
       
   676 
       
   677 We shall now write a function for the word frequency generating script that we had looked at in the previous session. 
       
   678 
       
   679 ::
       
   680 
       
   681   function word_frequency {
       
   682     if [ $# -ne 1 ]
       
   683     then
       
   684       echo "Usage: $0 file_name"
       
   685       exit 1
       
   686     else 
       
   687       if [ -f "$1" ]
       
   688       then
       
   689         grep  "[A-Za-z]*" -o "$1" | tr 'A-Z' 'a-z' | sort | uniq -c | sort -nr | less
       
   690       fi
       
   691     fi
       
   692   }
       
   693 
       
   694 As an exercise, modify the function to accept the input for the number of top frequency words to be shown (if none is given, assume 10).
       
   695 
       
   696 
       
   697 Further Reading:
       
   698 ---------------- 
       
   699 	* http://www.freeos.com/guides/lsst/ 
       
   700 	* http://bash.cyberciti.biz/guide/Main_Page
       
   701 	* http://tldp.org/LDP/abs/html/
       
   702 	* http://tldp.org/LDP/Bash-Beginners-Guide/html/Bash-Beginners-Guide.html
       
   703