|
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 |