{"id":3276,"date":"2014-06-29T01:36:11","date_gmt":"2014-06-28T17:36:11","guid":{"rendered":"http:\/\/rmohan.com\/?p=3276"},"modified":"2014-06-29T01:36:11","modified_gmt":"2014-06-28T17:36:11","slug":"unix-bourne-shell-scripting","status":"publish","type":"post","link":"https:\/\/mohan.sg\/?p=3276","title":{"rendered":"UNIX Bourne Shell Scripting"},"content":{"rendered":"<p>UNIX Bourne Shell Scripting<\/p>\n<p>These are the workshop notes I used to use for teaching a course on Bourne shell (sh) scripting. I haven&#8217;t taught the class for some time now, but the material is all still current since the Bourne shell hasn&#8217;t changed. Often you&#8217;ll find that these notes are a bit short of description. This is because they were intended by be a lecture aid.<\/p>\n<p>What do you need to know to follow this course? These notes were originally written as a second class in UNIX. The first class taught how to use the basic UNIX commands (like sed, grep and find) and this class teaches how to combine these tools to accomplish bigger tasks.<\/p>\n<p>In addition to the material in this course you might be interested in the Korn shell (ksh) and the Bourne again shell (bash), both of which are excellent shells that enchance the original Bourne shell. These alternate shells are upwardly-compatible with the original Bourne shell, meaning that a script written for sh can run in ksh or bash. However, there are additional features in bash and ksh that are not available in the Bourne shell.<\/p>\n<p>The focus of this guide is to get you to understand and run some Bourne shell scripts. On several pages there are example scripts for you to run. On most of these pages there is a link you can click on (with the right mouse button) and download the script to your computer and run it.<\/p>\n<p>Shell scripting skills have many applications, including:<\/p>\n<p>Ability to automate tasks, such as<br \/>\nBackups<br \/>\nAdministration tasks<br \/>\nPeriodic operations on a database via cron<br \/>\nAny repetetive operations on files<br \/>\nIncrease your general knowledge of UNIX<br \/>\nUse of environment<br \/>\nUse of UNIX utilities<br \/>\nUse of features such as pipes and I\/O redirection<br \/>\nFor example, I recently wrote a script to make a backup of one of the subdirectories where I was developing a project. I quickly wrote a shell script that uses \/bin\/tar to create an archive of the entire subdirectory and then copy it to one of our backup systems here at SDSC and store it under a subdirectory named according to today&#8217;s date.<\/p>\n<p>As another example, I have some software that runs on UNIX that I distribute and people were having trouble unpacking the software and getting it running. I designed and wrote a shell script that automated the process of unpacking the software and configuring it. Now people can get and install the software without having to contact me for help, which is good for them and good for me, too!<\/p>\n<p>For shell script experts one of the things to consider is whether to use the Bourne shell (or ksh or bash), the C shell, or a richer scripting language like perl or python. I like all these tools and am not especially biased toward any one of them. The best thing is to use the right tool for each job. If all you need to do is run some UNIX commands over and over again, use a Bourne or C shell script. If you need a script that does a lot of arithmetic or string manipulation, then you will be better off with perl or python. If you have a Bourne shell script that runs too slowly then you might want to rewrite it in perl or python because they can be much faster.<\/p>\n<p>Historically, people have been biased toward the Bourne shell over the C shell because in the early days the C shell was buggy. These problems are fixed in many C shell implementations these days, especially the excellent &#8216;T&#8217; C shell (tcsh), but many still prefer the Bourne shell.<\/p>\n<p>There are other good shells available. I don&#8217;t mean to neglect them but rather to talk about the tools I am familiar with.<\/p>\n<p>If you are interested also in learning about programming in the C shell I also have a comparison between features of the C shell and Bourne shell.<\/p>\n<p>Table of Contents:<\/p>\n<p>Review of a few Basic UNIX Topics (Page 1)<br \/>\nStoring Frequently Used Commands in Files: Shell Scripts (Page 6)<br \/>\nMore on Using UNIX Utilities (Page 9)<br \/>\nPerforming Search and Replace in Several Files (Page 11)<br \/>\nUsing Command-line Arguments for Flexibility (Page 14)<br \/>\nUsing Functions (Page 30)<br \/>\nMiscellaneous (Page 38)<br \/>\nTrapping Signals (Page 43)<br \/>\nUnderstanding Command Translation (Page 50)<br \/>\nWriting Advanced Loops (Page 59)<br \/>\nCreating Remote Shells (Page 67)<br \/>\nMore Miscellaneous (Page 73)<br \/>\nUsing Quotes (Page 75)<\/p>\n<p>Section 1: Review of a few Basic UNIX Topics <\/p>\n<p>Shell scripting involves chaining several UNIX commands together to accomplish a task. For example, you might run the &#8216;date&#8217; command and then use today&#8217;s date as part of a file name. I&#8217;ll show you how to do this below.<\/p>\n<p>Some of the tools of the trade are variables, backquotes and pipes. First we&#8217;ll study these topics and also quickly review a few other UNIX topics.<\/p>\n<p>Variables<\/p>\n<p>Topics covered: storing strings in variables<br \/>\nUtilities covered: echo, expr<br \/>\nTo try the commands below start up a Bourne shell:<br \/>\n\/bin\/sh<br \/>\nA variable stores a string (try running these commands in a Bourne shell)<br \/>\nname=&#8221;John Doe&#8221;<br \/>\necho $name<br \/>\nThe quotes are required in the example above because the string contains a special character (the space)<br \/>\nA variable may store a number<br \/>\nnum=137<br \/>\nThe shell stores this as a string even though it appears to be a number<br \/>\nA few UNIX utilities will convert this string into a number to perform arithmetic<br \/>\nexpr $num + 3<br \/>\nTry defining num as &#8216;7m8&#8217; and try the expr command again<br \/>\nWhat happens when num is not a valid number?<br \/>\nNow you may exit the Bourne shell with<br \/>\nexit<\/p>\n<p>Page 1<\/p>\n<p>I\/O Redirection<\/p>\n<p>Topics covered: specifying the input or capturing the output of a command in a file<br \/>\nUtilities covered: wc, sort<br \/>\nThe wc command counts the number of lines, words, and characters in a file<br \/>\nwc \/etc\/passwd<br \/>\nwc -l \/etc\/passwd<br \/>\nYou can save the output of wc (or any other command) with output redirection<br \/>\nwc \/etc\/passwd > wc.file<br \/>\nYou can specify the input with input redirection<br \/>\nwc < \/etc\/passwd\nMany UNIX commands allow you to specify the input file by name or by input redirection\nsort \/etc\/passwd\nsort < \/etc\/passwd\nYou can also append lines to the end of an existing file with output redirection\nwc -l \/etc\/passwd >> wc.file<\/p>\n<p>Page 2<\/p>\n<p>Backquotes<\/p>\n<p>Topics covered: capturing output of a command in a variable<br \/>\nUtilities covered: date<br \/>\nThe backquote character looks like the single quote or apostrophe, but slants the other way<br \/>\nIt is used to capture the output of a UNIX utility<br \/>\nA command in backquotes is executed and then replaced by the output of the command<br \/>\nExecute these commands<br \/>\ndate<br \/>\nsave_date=`date`<br \/>\necho The date is $save_date<br \/>\nNotice how echo prints the output of &#8216;date&#8217;, and gives the time when you defined the save_date variable<br \/>\nStore the following in a file named backquotes.sh and execute it (right click and save in a file)<br \/>\n#!\/bin\/sh<br \/>\n# Illustrates using backquotes<br \/>\n# Output of &#8216;date&#8217; stored in a variable<br \/>\nToday=&#8221;`date`&#8221;<br \/>\necho Today is $Today<br \/>\nExecute the script with<br \/>\nsh backquotes.sh<br \/>\nThe example above shows you how you can write commands into a file and execute the file with a Bourne shell<br \/>\nBackquotes are very useful, but be aware that they slow down a script if you use them hundreds of times<br \/>\nYou can save the output of any command with backquotes, but be aware that the results will be reformated into one line. Try this:<br \/>\nLS=`ls -l`<br \/>\necho $LS<\/p>\n<p>Page 3<\/p>\n<p>Pipes<\/p>\n<p>Topics covered: using UNIX pipes<br \/>\nUtilities covered: sort, cat, head<br \/>\nPipes are used for post-processing data<br \/>\nOne UNIX command prints results to the standard output (usually the screen), and another command reads that data and processes it<br \/>\nsort \/etc\/passwd | head -5<br \/>\nNotice that this pipe can be simplified<br \/>\ncat \/etc\/passwd | head -5<br \/>\nYou could accomplish the same thing more efficiently with either of the two commands:<br \/>\nhead -5 \/etc\/passwd<br \/>\nhead -5 < \/etc\/passwd\nFor example, this command displays all the files in the current directory sorted by file size\nls -al | sort -n -r +4\nThe command ls -al writes the file size in the fifth column, which is why we skip the first four columns using +4.\nThe options -n and -r request a numeric sort (which is different than the normal alphabetic sort) in reverse order\n\nPage 4\n\n\n\nawk\n\nTopics covered: processing columnar data\nUtilities covered: awk\nThe awk utility is used for processing columns of data\nA simple example shows how to extract column 5 (the file size) from the output of ls -l\nls -l | awk '{print $5}'\nCut and paste this line into a Bourne shell and you should see a column of file sizes, one per file in your current directory.\nA more complicated example shows how to sum the file sizes and print the result at the end of the awk run\nls -al | awk '{sum = sum + $5} END {print sum}'\nIn this example you should see printed just one number, which is the sum of the file sizes in the current directory.\n\nPage 5\n\n\n\n\n\n\n\n\nSection 2: Storing Frequently Used Commands in Files: Shell Scripts \n\nShell Scripts\n\nTopics covered: storing commands in a file and executing the file\nUtilities covered: date, cal, last (shows who has logged in recently)\nStore the following in a file named simple.sh and execute it\n#!\/bin\/sh\n# Show some useful info at the start of the day\ndate\necho Good morning $USER\ncal\nlast | head -6\nShows current date, calendar, and a six of previous logins\nNotice that the commands themselves are not displayed, only the results\nTo display the commands verbatim as they run, execute with\nsh -v simple.sh\nAnother way to display the commands as they run is with -x\nsh -x simple.sh\nWhat is the difference between -v and -x? Notice that with -v you see '$USER' but with -x you see your login name\nRun the command 'echo $USER' at your terminal prompt and see that the variable $USER stores your login name\nWith -v or -x (or both) you can easily relate any error message that may appear to the command that generated it\nWhen an error occurs in a script, the script continues executing at the next command\nVerify this by changing 'cal' to 'caal' to cause an error, and then run the script again\nRun the 'caal' script with 'sh -v simple.sh' and with 'sh -x simple.sh' and verify the error message comes from cal\nOther standard variable names include: $HOME, $PATH, $PRINTER. Use echo to examine the values of these variables\n\nPage 6\n\n\n\nStoring File Names in Variables\n\nTopics covered: variables store strings such as file names, more on creating and using variables\nUtilities covered: echo, ls, wc\nA variable is a name that stores a string\nIt's often convenient to store a filename in a variable\nStore the following in a file named variables.sh and execute it\n#!\/bin\/sh\n# An example with variables\nfilename=\"\/etc\/passwd\"\necho \"Check the permissions on $filename\"\nls -l $filename\necho \"Find out how many accounts there are on this system\"\nwc -l $filename\nNow if we change the value of $filename, the change is automatically propagated throughout the entire script\n\nPage 7\n\n\n\nScripting With sed\n\nTopics covered: global search and replace, input and output redirection\nUtilities covered: sed\nHere's how you can use sed to modify the contents of a variable:\necho \"Hello Jim\" | sed -e 's\/Hello\/Bye\/'\nCopy the file nlanr.txt to your home directory and notice how the word 'vBNS' appears in it several times\nChange 'vBNS' to 'NETWORK' with\nsed -e 's\/vBNS\/NETWORK\/g' < nlanr.txt\nYou can save the modified text in a file with output redirection\nsed -e 's\/vBNS\/NETWORK\/g' < nlanr.txt > nlanr.new<br \/>\nSed can be used for many complex editing tasks, we have only scratched the surface here<\/p>\n<p>Page 8<\/p>\n<p>Section 3: More on Using UNIX Utilities <\/p>\n<p>Performing Arithmetic<\/p>\n<p>Topics covered: integer arithmetic, preceding &#8216;*&#8217; with backslash to avoid file name wildcard expansion<br \/>\nUtilities covered: expr<br \/>\nArithmetic is done with expr<br \/>\nexpr 5 + 7<br \/>\nexpr 5 \\* 7<br \/>\nBackslash required in front of &#8216;*&#8217; since it is a filename wildcard and would be translated by the shell into a list of file names<br \/>\nYou can save arithmetic result in a variable<br \/>\nStore the following in a file named arith.sh and execute it<br \/>\n#!\/bin\/sh<br \/>\n# Perform some arithmetic<br \/>\nx=24<br \/>\ny=4<br \/>\nResult=`expr $x \\* $y`<br \/>\necho &#8220;$x times $y is $Result&#8221;<\/p>\n<p>Page 9<\/p>\n<p>Translating Characters<\/p>\n<p>Topics covered: converting one character to another, translating and saving string stored in a variable<br \/>\nUtilities covered: tr<br \/>\nCopy the file sdsc.txt to your home directory<br \/>\nThe utility tr translates characters<br \/>\ntr &#8216;a&#8217; &#8216;Z&#8217; < sdsc.txt\nThis example shows how to translate the contents of a variable and display the result on the screen with tr\nStore the following in a file named tr1.sh and execute it\n#!\/bin\/sh\n# Translate the contents of a variable\nCat_name=\"Piewacket\"\necho $Cat_name | tr 'a' 'i'\nThis example shows how to change the contents of a variable\nStore the following in a file named tr2.sh and execute it\n#!\/bin\/sh\n# Illustrates how to change the contents of a variable with tr\nCat_name=\"Piewacket\"\necho \"Cat_name is $Cat_name\"\nCat_name=`echo $Cat_name | tr 'a' 'i'`\necho \"Cat_name has changed to $Cat_name\"\nYou can also specify ranges of characters.\nThis example converts upper case to lower case\ntr 'A-Z' 'a-z' < file\nNow you can change the value of the variable and your script has access to the new value\n\nPage 10\n\n\n\n\n\n\n\n\nSection 4: Performing Search and Replace in Several Files \n\nProcessing Multiple Files\n\nTopics covered: executing a sequence of commands on each of several files with for loops\nUtilities covered: no new utilities\nStore the following in a file named loop1.sh and execute it\n#!\/bin\/sh\n# Execute ls and wc on each of several files\n# File names listed explicitly\nfor filename in simple.sh variables.sh loop1.sh\ndo\n\techo \"Variable filename is set to $filename...\"\n\tls -l $filename\n\twc -l $filename\ndone\nThis executes the three commands echo, ls and wc for each of the three file names\nYou should see three lines of output for each file name\nfilename is a variable, set by \"for\" statement and referenced as $filename\nNow we know how to execute a series of commands on each of several files\n\nPage 11\n\n\n\nUsing File Name Wildcards in For Loops\n\nTopics covered: looping over files specified with wildcards\nUtilities covered: no new utilities\nStore the following in a file named loop2.sh and execute it\n#!\/bin\/sh\n# Execute ls and wc on each of several files\n# File names listed using file name wildcards\nfor filename in *.sh\ndo\n\techo \"Variable filename is set to $filename...\"\n\tls -l $filename\n\twc -l $filename\ndone\nYou should see three lines of output for each file name ending in '.sh'\nThe file name wildcard pattern *.sh gets replaced by the list of filenames that exist in the current directory\nFor another example with filename wildcards try this command\necho *.sh\n\nPage 12\n\n\n\nSearch and Replace in Multiple Files\n\nTopics covered: combining for loops with utilities for global search and replace in several files\nUtilities covered: mv\nSed performs global search and replace on a single file\nsed -e 's\/application\/APPLICATION\/g' sdsc.txt > sdsc.txt.new<br \/>\nThe original file sdsc.txt is unchanged<br \/>\nHow can we arrange to have the original file over-written by the new version?<br \/>\nStore the following in a file named s-and-r.sh and execute it<br \/>\n#!\/bin\/sh<br \/>\n# Perform a global search and replace on each of several files<br \/>\n# File names listed explicitly<br \/>\nfor text_file in sdsc.txt nlanr.txt<br \/>\ndo<br \/>\n\techo &#8220;Editing file $text_file&#8221;<br \/>\n\tsed -e &#8216;s\/application\/APPLICATION\/g&#8217; $text_file > temp<br \/>\n\tmv -f temp $text_file<br \/>\ndone<br \/>\nFirst, sed saves new version in file &#8216;temp&#8217;<br \/>\nThen, use mv to overwrite original file with new version<\/p>\n<p>Page 13<\/p>\n<p>Section 5: Using Command-line Arguments for Flexibility <\/p>\n<p>What&#8217;s Lacking in the Scripts Above?<\/p>\n<p>Topics covered: looping over files specified with wildcards<br \/>\nUtilities covered: no new utilities<br \/>\nFile names are hard-coded inside the script<br \/>\nWhat if you want to run the script but with different file names?<br \/>\nTo execute for loops on different files, the user has to know how to edit the script<br \/>\nNot simple enough for general use by the masses<br \/>\nWouldn&#8217;t it be useful if we could easily specify different file names for each execution of a script?<\/p>\n<p>Page 14<\/p>\n<p>What are Command-line Arguments?<\/p>\n<p>Topics covered: specifying command-line arguments<br \/>\nUtilities covered: no new utilities<br \/>\nCommand-line arguments follow the name of a command<br \/>\nls -l .cshrc \/etc<br \/>\nThe command above has three command-line arguments<br \/>\n-l\t(an option that requests long directory listing)<br \/>\n.cshrc\t(a file name)<br \/>\n\/etc\t(a directory name)<br \/>\nAn example with file name wildcards:<br \/>\nwc *.sh<br \/>\nHow many command-line arguments were given to wc? It depends on how many files in the current directory match the pattern *.sh<br \/>\nUse &#8216;echo *.sh&#8217; to see them<br \/>\nMost UNIX commands take command-line arguments. Your scripts may also have arguments<\/p>\n<p>Page 15<\/p>\n<p>Accessing Command-line Arguments<\/p>\n<p>Topics covered: accessing command-line arguments<br \/>\nUtilities covered: no new utilities<br \/>\nStore the following in a file named args1.sh<br \/>\n#!\/bin\/sh<br \/>\n# Illustrates using command-line arguments<br \/>\n# Execute with<br \/>\n#\tsh args1.sh On the Waterfront<br \/>\necho &#8220;First command-line argument is: $1&#8221;<br \/>\necho &#8220;Third argument is: $3&#8221;<br \/>\necho &#8220;Number of arguments is: $#&#8221;<br \/>\necho &#8220;The entire list of arguments is: $*&#8221;<br \/>\nExecute the script with<br \/>\nsh args1.sh -x On the Waterfront<br \/>\nWords after the script name are command-line arguments<br \/>\nArguments are usually options like -l or file names<\/p>\n<p>Page 16<\/p>\n<p>Looping Over the Command-line Arguments<\/p>\n<p>Topics covered: using command-line arguments in a for loop<br \/>\nUtilities covered: no new utilities<br \/>\nStore the following in a file named args2.sh and execute it<br \/>\n#!\/bin\/sh<br \/>\n# Loop over the command-line arguments<br \/>\n# Execute with<br \/>\n#\tsh args2.sh simple.sh variables.sh<br \/>\nfor filename in &#8220;$@&#8221;<br \/>\ndo<br \/>\n\techo &#8220;Examining file $filename&#8221;<br \/>\n\twc -l $filename<br \/>\ndone<br \/>\nThis script runs properly with any number of arguments, including zero<br \/>\nThe shorter form of the for statement shown below does exactly the same thing<br \/>\nfor filename<br \/>\ndo<br \/>\n&#8230;<br \/>\nDon&#8217;t use<br \/>\nfor filename in $*<br \/>\nFails if any arguments include spaces<br \/>\nAlso, don&#8217;t forget the double quotes around $@<\/p>\n<p>Page 17<\/p>\n<p>If Blocks<\/p>\n<p>Topics covered: testing conditions, executing commands conditionally<br \/>\nUtilities covered: test (used by if to evaluate conditions)<br \/>\nThis will be covered on the whiteboard<br \/>\nSee Chapter 8 of the book<\/p>\n<p>Page 18<\/p>\n<p>The read Command<\/p>\n<p>Topics covered: reading a line from the standard input<br \/>\nUtilities covered: no new utilities<br \/>\nstdin is the keyboard unless input redirection used<br \/>\nRead one line from stdin, store line in a variable<br \/>\nread variable_name<br \/>\nAsk the user if he wants to exit the script<br \/>\nStore the following in a file named read.sh and execute it<br \/>\n#!\/bin\/sh<br \/>\n# Shows how to read a line from stdin<br \/>\necho &#8220;Would you like to exit this script now?&#8221;<br \/>\nread answer<br \/>\nif [ &#8220;$answer&#8221; = y ]<br \/>\nthen<br \/>\n\techo &#8220;Exiting&#8230;&#8221;<br \/>\n\texit 0<br \/>\nfi<\/p>\n<p>Page 19<\/p>\n<p>Command Exit Status<\/p>\n<p>Topics covered: checking whether a command succeeds or not<br \/>\nUtilities covered: no new utilities<br \/>\nEvery command in UNIX should return an exit status<br \/>\nStatus is in range 0-255<br \/>\nOnly 0 means success<br \/>\nOther statuses indicate various types of failures<br \/>\nStatus does not print on screen, but is available thru variable $?<br \/>\nExample shows how to examine exit status of a command<br \/>\nStore the following in a file named exit-status.sh and execute it<br \/>\n#!\/bin\/sh<br \/>\n# Experiment with command exit status<br \/>\necho &#8220;The next command should fail and return a status greater than zero&#8221;<br \/>\nls \/nosuchdirectory<br \/>\necho &#8220;Status is $? from command: ls \/nosuchdirectory&#8221;<br \/>\necho &#8220;The next command should succeed and return a status equal to zero&#8221;<br \/>\nls \/tmp<br \/>\necho &#8220;Status is $? from command: ls \/tmp&#8221;<br \/>\nExample shows if block using exit status to force exit on failure<br \/>\nStore the following in a file named exit-status-test.sh and execute it<br \/>\n#!\/bin\/sh<br \/>\n# Use an if block to determine if a command succeeded<br \/>\necho &#8220;This mkdir command fails unless you are root:&#8221;<br \/>\nmkdir \/no_way<br \/>\nif [ &#8220;$?&#8221; -ne 0 ]<br \/>\nthen<br \/>\n\t# Complain and quit<br \/>\n\techo &#8220;Could not create directory \/no_way&#8230;quitting&#8221;<br \/>\n\texit 1  # Set script&#8217;s exit status to 1<br \/>\nfi<br \/>\necho &#8220;Created directory \/no_way&#8221;<br \/>\nExit status is $status in C shell<\/p>\n<p>Page 20<\/p>\n<p>Regular Expressions<\/p>\n<p>Topics covered: search patterns for editors, grep, sed<br \/>\nUtilities covered: no new utilities<br \/>\nZero or more characters: .*<br \/>\ngrep &#8216;provided.*access&#8217; sdsc.txt<br \/>\nsed -e &#8216;s\/provided.*access\/provided access\/&#8217; sdsc.txt<br \/>\nSearch for text at beginning of line<br \/>\ngrep &#8216;^the&#8217; sdsc.txt<br \/>\nSearch for text at the end of line<br \/>\ngrep &#8216;of$&#8217; sdsc.txt<br \/>\nAsterisk means zero or more the the preceeding character<br \/>\na*     zero or more a&#8217;s<br \/>\naa*    one or more a&#8217;s<br \/>\naaa*   two or more a&#8217;s<br \/>\nDelete all spaces at the ends of lines<br \/>\nsed -e &#8216;s\/ *$\/\/&#8217; sdsc.txt > sdsc.txt.new<br \/>\nTurn each line into a shell comment<br \/>\nsed -e &#8216;s\/^\/# \/&#8217; sdsc.txt<\/p>\n<p>Page 21<\/p>\n<p>Greed and Eagerness<\/p>\n<p>Attributes of pattern matching<br \/>\nGreed: a regular expression will match the largest possible string<br \/>\nExecute this command and see how big a string gets replaced by an underscore<br \/>\necho &#8216;Big robot&#8217; | sed -e &#8216;s\/i.*o\/_\/&#8217;<br \/>\nEagerness: a regular expression will find the first match if several are present in the line<br \/>\nExecute this command and see whether &#8216;big&#8217; or &#8216;bag&#8217; is matched by the regular expression<br \/>\necho &#8216;big bag&#8217; | sed -e &#8216;s\/b.g\/___\/&#8217;<br \/>\nContrast with this command (notice the extra &#8216;g&#8217;)<br \/>\necho &#8216;big bag&#8217; | sed -e &#8216;s\/b.g\/___\/g&#8217;<br \/>\nExplain what happens in the next example<br \/>\necho &#8216;black dog&#8217; | sed -e &#8216;s\/a*\/_\/&#8217;<br \/>\nHint: a* matches zero or more a&#8217;s, and there are many places where zero a&#8217;s appear<br \/>\nTry the example above with the extra &#8216;g&#8217;<br \/>\necho &#8216;black dog&#8217; | sed -e &#8216;s\/a*\/_\/g&#8217;<\/p>\n<p>Page 22<\/p>\n<p>Regular Expressions Versus Wildcards<\/p>\n<p>Topics covered: clarify double meaning of asterisk in patterns<br \/>\nUtilities covered: no new utilities<br \/>\nAsterisk used in regular expressions for editors, grep, sed<br \/>\nDifferent meaning in file name wildcards on command line and in find command and case statement (see below)<br \/>\nregexp  wildcard  meaning<\/p>\n<p>.*      *         zero or more characters, any type<br \/>\n.       ?         exactly one character, any type<br \/>\n[aCg]   [aCg]     exactly one character, from list: aCg<br \/>\nRegexps can be anchored to beginning\/ending of line with ^ and $<br \/>\nWildcards automatically anchored to both extremes<br \/>\nCan use wildcards un-anchored with asterisks<br \/>\nls *bub*<\/p>\n<p>Page 23<\/p>\n<p>Getting Clever With Regular Expressions<\/p>\n<p>Topics covered: manipulating text matched by a pattern<br \/>\nUtilities covered: no new utilities<br \/>\nCopy the file animals.txt to your home directory<br \/>\nTry this sed command, which changes the first line of animals.txt<br \/>\nsed -e &#8220;s\/big \\(.*\\) dog\/small \\1 cat\/&#8221; animals.txt<br \/>\nBracketing part of a pattern with \\( and \\) labels that part as \\1<br \/>\nBracketing additional parts of a pattern creates labels \\2, \\3, &#8230;<br \/>\nThis sed command reverses the order of two words describing the rabbit<br \/>\nsed -e &#8220;s\/Flopsy is a big \\(.*\\) \\(.*\\) rabbit\/A big \\2 \\1 rabbit\/&#8221; < animals.txt\n\nPage 24\n\n\n\nThe case Statement\n\nTopics covered: choosing which block of commands to execute based on value of a string\nUtilities covered: no new utilities\nThe next example shows how to use a case statement to handle several contingencies\nThe user is expected to type one of three words\nA different action is taken for each choice\nStore the following in a file named case1.sh and execute it\n#!\/bin\/sh\n# An example with the case statement\n# Reads a command from the user and processes it\necho \"Enter your command (who, list, or cal)\"\nread command\ncase \"$command\" in\n\twho)\n\t\techo \"Running who...\"\n\t\twho\n\t\t;;\n\tlist)\n\t\techo \"Running ls...\"\n\t\tls\n\t\t;;\n\tcal)\n\t\techo \"Running cal...\"\n\t\tcal\n\t\t;;\n\t*)\n\t\techo \"Bad command, your choices are: who, list, or cal\"\n\t\t;;\nesac\nexit 0\nThe last case above is the default, which corresponds to an unrecognized entry\nThe next example uses the first command-line arg instead of asking the user to type a command\nStore the following in a file named case2.sh and execute it\n#!\/bin\/sh\n# An example with the case statement\n# Reads a command from the user and processes it\n# Execute with one of\n#\tsh case2.sh who\n#\tsh case2.sh ls\n#\tsh case2.sh cal\necho \"Took command from the argument list: '$1'\"\ncase \"$1\" in\n\twho)\n\t\techo \"Running who...\"\n\t\twho\n\t\t;;\n\tlist)\n\t\techo \"Running ls...\"\n\t\tls\n\t\t;;\n\tcal)\n\t\techo \"Running cal...\"\n\t\tcal\n\t\t;;\n\t*)\n\t\techo \"Bad command, your choices are: who, list, or cal\"\n\t\t;;\nesac\nThe patterns in the case statement may use file name wildcards\n\nPage 25\n\n\n\nThe while Statement\n\nTopics covered: executing a series of commands as long as some condition is true\nUtilities covered: no new utilities\nThe example below loops over two statements as long as the variable i is less than or equal to ten\nStore the following in a file named while1.sh and execute it\n#!\/bin\/sh\n# Illustrates implementing a counter with a while loop\n# Notice how we increment the counter with expr in backquotes\ni=\"1\"\nwhile [ $i -le 10 ]\ndo\n\techo \"i is $i\"\n\ti=`expr $i + 1`\ndone\n\nPage 26\n\n\n\nExample With a while Loop\n\nTopics covered: Using a while loop to read and process a file\nUtilities covered: no new utilities\nCopy the file while2.data to your home directory\nThe example below uses a while loop to read an entire file\nThe while loop exits when the read command returns false exit status (end of file)\nStore the following in a file named while2.sh and execute it\n#!\/bin\/sh\n# Illustrates use of a while loop to read a file\ncat while2.data |   \\\nwhile read line\ndo\n\techo \"Found line: $line\"\ndone\n\nThe entire while loop reads its stdin from the pipe\nEach read command reads another line from the file coming from cat\nThe entire while loop runs in a subshell because of the pipe\nVariable values set inside while loop not available after while loop\n\nPage 27\n\n\n\nInterpreting Options With getopts Command\n\nTopics covered: Understand how getopts command works\nUtilities covered: getopts\ngetopts is a standard UNIX utility used for our class in scripts getopts1.sh and getopts2.sh\nIts purpose is to help process command-line options (such as -h) inside a script\nIt handles stacked options (such as -la) and options with arguments (such as -P used as -Pprinter-name in lpr command)\nThis example will help you understand how getopts interprets options\nStore the following in a file named getopts1.sh and execute it\n#!\/bin\/sh\n\n# Execute with\n#\n#\tsh getopts1.sh  -h  -Pxerox  file1  file2\n#\n# and notice how the information on all the options is displayed\n#\n# The string 'P:h' says that the option -P is a complex option\n# requiring an argument, and that h is a simple option not requiring\n# an argument.\n#\n\n# Experiment with getopts command\nwhile getopts 'P:h' OPT_LETTER\ndo\n\techo \"getopts has set variable OPT_LETTER to '$OPT_LETTER'\"\n\techo \"\tOPTARG is '$OPTARG'\"\ndone\n\nused_up=`expr $OPTIND - 1`\n\necho \"Shifting away the first \\$OPTIND-1 = $used_up command-line arguments\"\n\nshift $used_up\n\necho \"Remaining command-line arguments are '$*'\"\n\nLook over the script\ngetopts looks for command-line options\nFor each option found, it sets three variables: OPT_LETTER, OPTARG, OPTIND\nOPT_LETTER is the letter, such as 'h' for option -h\nOPTARG is the argument to the option, such as -Pjunky has argument 'junky'\nOPTIND is a counter that determines how many of the command-line arguments were used up by getopts (see the shift command in the script)\nExecute it several times with\nsh getopts1.sh -h -Pjunky\nsh getopts1.sh -hPjunky\nsh getopts1.sh -h -Pjunky \/etc \/tmp\nNotice how it interprets -h and gives you 'h' in variable OPT_LETTER\nNow you can easily implement some operation when -h is used\nNotice how the second execution uses stacked options\nNotice how the third execution examines the rest of the command-line after the options (these are usually file or directory names)\n\nPage 28\n\n\n\nExample With getopts\n\nTopics covered: interpreting options in a script\nUtilities covered: getopts\nThe second example shows how to use if blocks to take action for each option\nStore the following in a file named getopts2.sh and execute it\n#!\/bin\/sh\n#\n# Usage:\n#\n#\tgetopts2.sh [-P string] [-h] [file1 file2 ...]\n#\n# Example runs:\n#\n#\tgetopts2.sh -h -Pxerox file1 file2\n#\tgetopts2.sh -hPxerox file1 file2\n#\n# Will print out the options and file names given\n#\n\n# Initialize our variables so we don't inherit values\n# from the environment\nopt_P=''\nopt_h=''\n\n# Parse the command-line options\nwhile getopts 'P:h' option\ndo\n\tcase \"$option\" in\n\t\"P\")\topt_P=\"$OPTARG\"\n\t\t;;\n\t\"h\")\topt_h=\"1\"\n\t\t;;\n\t?)\techo \"getopts2.sh: Bad option specified...quitting\"\n\t\texit 1\n\t\t;;\n\tesac\ndone\n\nshift `expr $OPTIND - 1`\n\nif [ \"$opt_P\" != \"\" ]\nthen\n\techo \"Option P used with argument '$opt_P'\"\nfi\n\nif [ \"$opt_h\" != \"\" ]\nthen\n\techo \"Option h used\"\nfi\n\nif [ \"$*\" != \"\" ]\nthen\n\techo \"Remaining command-line:\"\n\tfor arg in \"$@\"\n\tdo\n\t\techo \"\t$arg\"\n\tdone\nfi\n\nExecute it several times with\nsh getopts2.sh -h -Pjunky\nsh getopts2.sh -hPjunky\nsh getopts2.sh -h -Pjunky \/etc \/tmp\nCan also implement actions inside case statement if desired\n\nPage 29\n\n\n\n\n\n\n\n\nSection 6: Using Functions \n\nFunctions\n\nSequence of statements that can be called anywhere in script\nUsed for\nGood organization\nCreate re-usable sequences of commands\n\nPage 30\n\n\n\nDefine a Function\n\nDefine a function\necho_it () {\n  echo \"In function echo_it\"\n}\nUse it like any other command\necho_it\nPut these four lines in a script and execute it\n\nPage 31\n\n\n\nFunction Arguments\n\nFunctions can have command-line arguments\necho_it () {\n  echo \"Argument 1 is $1\"\n  echo \"Argument 2 is $2\"\n}\necho_it arg1 arg2\nWhen you execute the script above, you should see\nArgument 1 is arg1\nArgument 2 is arg2\nCreate a script 'difference.sh' with the following lines:\n#!\/bin\/sh\necho_it () {\n\techo Function argument 1 is $1\n}\necho Script argument 1 is $1\necho_it Barney\nExecute this script using\nsh difference.sh Fred\nNotice that '$1' is echoed twice with different values\nThe function has separate command-line arguments from the script's\n\nPage 32\n\n\n\nExample With Functions\n\nUse functions to organize script\nread_inputs () { ... }\ncompute_results () { ... }\nprint_results () { ... }\nMain program very readable\nread_inputs\ncompute_results\nprint_results\n\nPage 33\n\n\n\nFunctions in Pipes\n\nCan use a function in a pipe\nls_sorter () {\n  sort -n +4\n}\nls -al | ls_sorter\nFunction in pipe executed in new shell\nNew variables forgotten when function exits\n\nPage 34\n\n\n\nInherited Variables\n\nVariables defined before calling script available to script\nfunc_y () {\n  echo \"A is $A\"\n  return 7\n}\nA='bub'\nfunc_y\nif [ $? -eq 7 ] ; then ...\nTry it: is a variable defined inside a function available to the main program?\n\nPage 35\n\n\n\nFunctions -vs- Scripts\n\nFunctions are like separate scripts\nBoth functions and scripts can:\nUse command-line arguments\necho First arg is $1\nOperate in pipes\necho \"test string\" | ls_sorter\nReturn exit status\nfunc_y arg1 arg2\nif [ $? -ne 0 ] ...\n\nPage 36\n\n\n\nLibraries of Functions\n\nCommon to store definitions of favorite functions in a file\nThen execute file with\n. file\nPeriod command executes file in current shell\nCompare to C shell's source command\n\nPage 37\n\n\n\n\n\n\n\n\nSection 7: Miscellaneous \n\nHere Files\n\nData contained within script\ncat << END\nThis script backs up the directory\nnamed as the first command-line argument,\nwhich in your case in $1.\nEND\nTerminator string must begin in column one\nVariables and backquotes translated in data\nTurn off translation with \\END\n\nPage 38\n\n\n\nExample With Here File\n\nSend e-mail to each of several users\nfor name in login1 login2 login3\ndo\n  mailx -s 'hi there' $name << EOF\n  Hi $name, meet me at the water\n  fountain\nEOF\ndone\nUse <<- to remove initial tabs automatically\n\nPage 39\n\n\n\nSet: Shell Options\n\nCan change Bourne shell's options at runtime\nUse set command inside script\nset -v\nset +v\nset -xv\nToggle verbose mode on and off to reduce amount of debugging output\n\nPage 40\n\n\n\nSet: Split a Line\n\nCan change Bourne shell's options\nset -- word1 word2\necho $1, $2\n  word1, word2\nDouble dash important!\nWord1 may begin with a dash, what if word1 is '-x'?\nDouble dash says \"even if first word begins with '-', do not treat it as an option to the shell\n\nPage 41\n\n\n\nExample With Set\n\nRead a line from keyboard\nEcho words 3 and 5\nread var\nset -- $var\necho $3 $5\nBest way to split a line into words\n\nPage 42\n\n\n\n\n\n\n\n\nSection 8: Trapping Signals \n\nWhat are Signals?\n\nSignals are small messages sent to a process\nProcess interrupted to handle signal\nPossibilities for managing signal:\nTerminate\nIgnore\nPerform a programmer-defined action\n\nPage 43\n\n\n\nCommon Signals\n\nCommon signals are\nSIGINTR\tsent to foreground process by ^C\nSIGHUP\tsent when modem line gets hung up\nSIGTERM\tsent by kill -9\nSignals have numeric equivalents\n2 SIGINTR\n9 SIGTERM\n\nPage 44\n\n\n\nSend a Signal\n\nSend a signal to a process\nkill -2 PID\nkill -INTR PID\n\nPage 45\n\n\n\nTrap Signals\n\nHandling Signals\ntrap \"echo Interrupted; exit 2\" 2\nIgnoring Signals\ntrap \"\" 2 3\nRestoring Default Handler\ntrap 2\n\nPage 46\n\n\n\nWhere to Find List of Signals\n\nSee file\n\/usr\/include\/sys\/signal.h\n\nPage 47\n\n\n\nUser Signals\n\nSIGUSR1, SIGUSR2 are for your use\nSend to a process with\nkill -USR1 PID\nDefault action is to terminate process\n\nPage 48\n\n\n\nExperiment With Signals\n\nScript that catches USR1\nEcho message upon each signal\ntrap 'echo USR1' 16\nwhile : ; do\n  date\n  sleep 3\ndone\nTry it: does signal interrupt sleep?\n\nPage 49\n\n\n\n\n\n\n\n\nSection 9: Understanding Command Translation \n\nCommand Translation\n\nCommon translations include\nSplitting at spaces, obey quotes\n$HOME -> \/users\/us\/freddy<br \/>\n`command` -> output of command<br \/>\nI\/O redirection<br \/>\nFile name wildcard expansion<br \/>\nCombinations of quotes and metacharacters confusing<br \/>\nResolve problems by understanding order of translations<\/p>\n<p>Page 50<\/p>\n<p>Experiment With Translation<\/p>\n<p>Try wildcards in echo command<br \/>\necho b*<br \/>\nb budget bzzzzz<br \/>\nb* translated by sh before echo runs<br \/>\nWhen echo runs it sees<br \/>\necho b budget bzzzzz<br \/>\nEcho command need not understand wildcards!<\/p>\n<p>Page 51<\/p>\n<p>Order of Translations<\/p>\n<p>Splits into words at spaces and tabs<br \/>\nDivides commands at<br \/>\n; &#038; | &#038;&#038; || (&#8230;) {&#8230;}<br \/>\nEchos command if -v<br \/>\nInterprets quotes<br \/>\nPerforms variable substitution<\/p>\n<p>Page 52<\/p>\n<p>Order of Translations (continued)<\/p>\n<p>Performs command substitution<br \/>\nImplements I\/O redirection and removes redirection characters<br \/>\nDivides command again according to IFS<br \/>\nExpands file name wildcards<br \/>\nEchos translated command if -x<br \/>\nExecutes command<\/p>\n<p>Page 53<\/p>\n<p>Exceptional Case<\/p>\n<p>Delayed expansion for variable assignments<br \/>\nVAR=b*<br \/>\necho $VAR<br \/>\n  b  b_file<br \/>\nWildcard re-expanded for each echo<\/p>\n<p>Page 54<\/p>\n<p>Examples With Translation<\/p>\n<p>Variables translated before execution<br \/>\nCan store command name in variable<br \/>\ncommand=&#8221;ls&#8221;<br \/>\n$command<br \/>\n  file1 file2 dir1 dir2&#8230;<br \/>\nVariables translated before I\/O redirection<br \/>\ntempfile=&#8221;\/tmp\/scriptname_$$&#8221;<br \/>\nls -al > $tempfile<\/p>\n<p>Page 55<\/p>\n<p>Examples (continued)<\/p>\n<p>Delayed expansion of wildcards in variable assignment<br \/>\nOutput of this echo command changes when directory contents change (* is re-evaluated each time the command is run)<br \/>\nx=*<br \/>\necho $x<br \/>\nCan view values stored in variables with<br \/>\nset<br \/>\nTry it: verify that the wildcard is stored in x without expansion<\/p>\n<p>Page 56<\/p>\n<p>Examples (continued)<\/p>\n<p>Wildcards expanded after redirection (assuming file* matches exactly one file):<br \/>\ncat < file*\n  file*: No such file or directory\nCommand in backquotes expanded fully (and before I\/O redirection)\ncat < `echo file*`\n  (contents of file sent to screen)\n\nPage 57\n\n\n\nEval Command\n\nForces an extra evaluation of command\neval cat \\< file*\n  (contents of matching file)\nBackslash delays translation of < until second translation\n\nPage 58\n\n\n\n\n\n\n\n\nSection 10: Writing Advanced Loops \n\nWhile loops\n\nExecute statements while a condition is true\ni=0\nwhile [ $i -lt 10 ]\ndo\n  echo I is $i\n  i=`expr $i + 1`\ndone\n\nPage 59\n\n\n\nUntil loops\n\nExecute statements as long as a condition is false\nuntil grep \"sort\" dbase_log > \/dev\/null<br \/>\ndo<br \/>\n  sleep 10<br \/>\ndone<br \/>\necho &#8220;Database has been sorted&#8221;<br \/>\nExample executes until grep is unsuccessful<\/p>\n<p>Page 60<\/p>\n<p>Redirection of Loops<\/p>\n<p>Can redirect output of a loop<br \/>\nfor f in *.c<br \/>\ndo<br \/>\n  wc -l $f<br \/>\ndone > loop.out<br \/>\nLoop runs in separate shell<br \/>\nNew variables forgotten after loop<br \/>\nBackgrounding OK, too<\/p>\n<p>Page 61<\/p>\n<p>Continue Command<\/p>\n<p>Used in for, while, and until loops<br \/>\nSkip remaining statements<br \/>\nReturn to top of loop<br \/>\nfor name in *<br \/>\ndo<br \/>\n  if [ ! -f $name ] ; then<br \/>\n    continue<br \/>\n  fi<br \/>\n  echo &#8220;Found file $name&#8221;<br \/>\ndone<br \/>\nExample loops over files, skips directories<\/p>\n<p>Page 62<\/p>\n<p>Break Command<\/p>\n<p>Used in for, while, and until loops<br \/>\nSkip remaining statements<br \/>\nExit loop<br \/>\nfor name in *<br \/>\ndo<br \/>\n  if [ ! -r $name ] ; then<br \/>\n    echo &#8220;Cannot read $name, quitting loop&#8221;<br \/>\n    break<br \/>\n  fi<br \/>\n  echo &#8220;Found file or directory $name&#8221;<br \/>\ndone<br \/>\nExample loops over files and directories, quits if one is not readable<\/p>\n<p>Page 63<\/p>\n<p>Case Command<\/p>\n<p>Execute one of several blocks of commands<br \/>\ncase &#8220;string&#8221; in<br \/>\npattern1)<br \/>\n  commands ;;<br \/>\npattern2)<br \/>\n  commands ;;<br \/>\n*) # Default case<br \/>\n  commands ;;<br \/>\nesac<br \/>\nPatterns specified with file name wildcards<br \/>\nquit) &#8230;<br \/>\nqu*)   &#8230;<\/p>\n<p>Page 64<\/p>\n<p>Example With Case<\/p>\n<p>Read commands from keyboard and interpret<br \/>\nEnter this script &#8216;case.sh&#8217;<br \/>\necho Enter a command<br \/>\nwhile read cmd<br \/>\ndo<br \/>\n  case &#8220;$cmd&#8221; in<br \/>\n    list) ls -al ;;<br \/>\n    freespace) df . ;;<br \/>\n    quit|Quit) break ;;<br \/>\n    *) echo &#8220;$cmd: No such command&#8221; ;;<br \/>\n  esac<br \/>\ndone<br \/>\necho &#8220;All done&#8221;<br \/>\nWhen you run it, the script waits for you to type one of:<br \/>\nlist<br \/>\nfreespace<br \/>\nquit<br \/>\nQuit<br \/>\nTry it: modify the example so any command beginning with characters &#8220;free&#8221; runs df<\/p>\n<p>Page 65<\/p>\n<p>Infinite Loops<\/p>\n<p>Infinite loop with while<br \/>\nwhile :<br \/>\ndo<br \/>\n  &#8230;<br \/>\ndone<br \/>\n: is no-op, always returns success status<br \/>\nMust use break or exit inside loop for it to terminate<\/p>\n<p>Page 66<\/p>\n<p>Section 11: Forking Remote Shells <\/p>\n<p>Remote Shells<\/p>\n<p>Rsh command<br \/>\nrsh hostname &#8220;commands&#8221;<br \/>\nRuns commands on remote system<br \/>\nMust have .rhosts set up<br \/>\nCan specify different login name<br \/>\nrsh -l name hostname &#8220;commands&#8221;<\/p>\n<p>Page 67<\/p>\n<p>Examples With rsh<\/p>\n<p>Check who&#8217;s logged on<br \/>\nrsh spooky &#8220;finger&#8221;<br \/>\nRun several remote commands<br \/>\nrsh spooky &#8220;uname -a; time&#8221;<br \/>\nExecutes .cshrc on remote system<br \/>\nBe sure to set path in .cshrc instead of .login<\/p>\n<p>Page 68<\/p>\n<p>Access Control with .Rhosts<\/p>\n<p>May get &#8220;permission denied&#8221; error from rsh<br \/>\nFix this with ~\/.rhosts on remote system<br \/>\nExample: provide for remote shell from spunky to spooky<br \/>\nspunky % rlogin spooky<br \/>\nspooky % vi ~\/.rhosts<br \/>\n\t(insert &#8220;spunky login-name&#8221;)<br \/>\nspooky % chmod 600 ~\/.rhosts<br \/>\nspooky % logout<br \/>\nspunky % rsh spooky uname -a<br \/>\n\tspooky 5.5 sparc SUNW,Ultra-1<br \/>\nMay also rlogin without password: security problem!<\/p>\n<p>Page 69<\/p>\n<p>Remote Shell I\/O<\/p>\n<p>Standard output sent to local host<br \/>\nrsh spooky finger > finger.spooky<br \/>\nStandard input sent to remote host<br \/>\ncat local-file | rsh spooky lpr &#8211;<\/p>\n<p>Page 70<\/p>\n<p>Return Status<\/p>\n<p>Get return status of rsh<br \/>\nrsh mayer &#8220;uname -a&#8221;<br \/>\necho $?<br \/>\nReturns 0 if rsh managed to connect to remote host<br \/>\nReturns 1 otherwise<br \/>\nInvalid hostname<br \/>\nPermission denied<\/p>\n<p>Page 71<\/p>\n<p>Remote Return Status<\/p>\n<p>What about exit status of remote command?<br \/>\nHave to determine success or failure from stdout or stderr<\/p>\n<p>Page 72<\/p>\n<p>Section 12: More Miscellaneous <\/p>\n<p>Temporary Files<\/p>\n<p>Use unique names to avoid clashes<br \/>\ntempfile=$HOME\/Weq_$$<br \/>\ncommand > $tempfile<br \/>\n$$ is PID of current shell<br \/>\nAvoids conflict with concurrent executions of script<br \/>\nDo not use \/tmp!<\/p>\n<p>Page 73<\/p>\n<p>Wait Command<\/p>\n<p>Wait for termination of background job<br \/>\ncommand &#038;<br \/>\npid=$!<br \/>\n(other processing)<br \/>\nwait $pid<br \/>\nAllows overlap of two or more operations<\/p>\n<p>Page 74<\/p>\n<p>Section 13: Using Quotes <\/p>\n<p>Quotes<\/p>\n<p>Provide control of collapsing of spaces and translation of variables<br \/>\nTry it: run three examples<br \/>\nNo quotes (variables translated, spaces collapsed)<br \/>\necho Home:   $HOME<br \/>\n  Home: \/users\/us\/freddy<br \/>\nDouble quotes (no collapsing)<br \/>\necho &#8220;Home:   $HOME&#8221;<br \/>\n  Home:   \/users\/us\/freddy<br \/>\nSingle quotes (no translation or collapsing)<br \/>\necho &#8216;Home:   $HOME&#8217;<br \/>\n  Home:   $HOME<br \/>\nTry it: single quotes within double quotes<br \/>\necho &#8220;Home directory &#8216;$HOME&#8217; is full&#8230;&#8221;<\/p>\n<p>Page 75<\/p>\n<p>Metacharacters<\/p>\n<p>Characters with special meaning to shell<br \/>\n&#8221; &#8216; ` $ * [ ] ?<br \/>\n; > < &#038; ( ) \\\nAvoid special meaning with quoting\necho 'You have $20'\nBackslash like single quotes\nApplies only to next character\necho You have \\$20\n\nPage 76\n\n\n\nExamples With Quotes\n\nBad command line:\ngrep dog.*cat file\nShell tries to expand dot.*cat as file name wildcard\nUse quotes to avoid translation\ngrep 'dog.*cat' file\nSingle quotes OK in this case because we don't need variable translation\n\nPage 77\n\n\n\nMore Examples With Quotes\n\nRead name and search file for name\nread name\ngrep \"$name\" dbase\nSingle quotes not OK because we need variable translation\n\nPage 78\n\n\n\nSearching for Metacharacters\n\nBad command line: search for dollar sign\ngrep \"Gimme.*$20\" file\nProblem: shell translates variable $20\nSolution: use single quotes\ngrep 'Gimme.*$20' file\n\n<\/p>\n","protected":false},"excerpt":{"rendered":"<p>UNIX Bourne Shell Scripting<\/p>\n<p>These are the workshop notes I used to use for teaching a course on Bourne shell (sh) scripting. I haven&#8217;t taught the class for some time now, but the material is all still current since the Bourne shell hasn&#8217;t changed. Often you&#8217;ll find that these notes are a bit short of [&#8230;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[47],"tags":[],"_links":{"self":[{"href":"https:\/\/mohan.sg\/index.php?rest_route=\/wp\/v2\/posts\/3276"}],"collection":[{"href":"https:\/\/mohan.sg\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mohan.sg\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mohan.sg\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mohan.sg\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3276"}],"version-history":[{"count":1,"href":"https:\/\/mohan.sg\/index.php?rest_route=\/wp\/v2\/posts\/3276\/revisions"}],"predecessor-version":[{"id":3277,"href":"https:\/\/mohan.sg\/index.php?rest_route=\/wp\/v2\/posts\/3276\/revisions\/3277"}],"wp:attachment":[{"href":"https:\/\/mohan.sg\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3276"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mohan.sg\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3276"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mohan.sg\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3276"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}