{"id":440,"date":"2007-11-30T00:00:00","date_gmt":"2007-11-29T16:00:00","guid":{"rendered":"http:\/\/www.strongd.net\/?p=440"},"modified":"2011-07-15T09:55:16","modified_gmt":"2011-07-15T01:55:16","slug":"how-to-write-a-shell-script","status":"publish","type":"post","link":"https:\/\/www.strongd.net\/?p=440","title":{"rendered":"How to write a shell script"},"content":{"rendered":"<p><P>How to write a shell script<BR>Introduction<BR>A shell is a command line interpretor. It takes commands and executes them. As such, it implements a programming language. The Bourne shell is used to create shell scripts &#8212; ie. programs that are interpreted\/executed by the shell. You can write shell scripts with the C-shell; however, this is not covered here. <\/P><br \/>\n<P>Creating a Script<BR>Suppose you often type the command <BR>&nbsp;&nbsp;&nbsp; find . -name file -print<\/P><br \/>\n<P>and you&#8217;d rather type a simple command, say <BR>&nbsp;&nbsp;&nbsp; sfind file<\/P><br \/>\n<P>Create a shell script <BR>&nbsp;&nbsp;&nbsp; % cd ~\/bin<BR>&nbsp;&nbsp;&nbsp; % emacs sfind<BR>&nbsp;&nbsp;&nbsp; % page sfind<BR>&nbsp;&nbsp;&nbsp; find . -name $1 -print<BR>&nbsp;&nbsp;&nbsp; % chmod a+x sfind<BR>&nbsp;&nbsp;&nbsp; % rehash<BR>&nbsp;&nbsp;&nbsp; % cd \/usr\/local\/bin<BR>&nbsp;&nbsp;&nbsp; % sfind tcsh<BR>&nbsp;&nbsp;&nbsp; .\/shells\/tcsh<\/P><br \/>\n<P>Observations<BR>This quick example is far from adequate but some observations: <BR>Shell scripts are simple text files created with an editor. <BR>Shell scripts are marked as executeable <BR>&nbsp;&nbsp;&nbsp; %chmod a+x sfind<\/P><br \/>\n<P>Should be located in your search path and ~\/bin should be in your search path. <BR>You likely need to rehash if you&#8217;re a Csh (tcsh) user (but not again when you login). <BR>Arguments are passed from the command line and referenced. For example, as $1. <BR>#!\/bin\/sh<BR>All Bourne Shell scripts should begin with the sequence <BR>&nbsp;&nbsp;&nbsp; #!\/bin\/sh<\/P><br \/>\n<P>From the man page for exec(2): <BR>&#8220;On the first line of an interpreter script, following the &#8220;#!&#8221;, is the name of a program which should be used to interpret the contents of the file. For instance, if the first line contains &#8220;#! \/bin\/sh&#8221;, then the con- tents of the file are executed as a shell script.&#8221; <\/P><br \/>\n<P>You can get away without this, but you shouldn&#8217;t. All good scripts state the interpretor explicitly. Long ago there was just one (the Bourne Shell) but these days there are many interpretors &#8212; Csh, Ksh, Bash, and others. <\/P><br \/>\n<P>Comments<BR>Comments are any text beginning with the pound (#) sign. A comment can start anywhere on a line and continue until the end of the line. <BR>Search Path<BR>All shell scripts should include a search path specifica- tion: <BR>&nbsp;&nbsp;&nbsp; PATH=\/usr\/ucb:\/usr\/bin:\/bin; export PATH<\/P><br \/>\n<P>A PATH specification is recommended &#8212; often times a script will fail for some people because they have a different or incomplete search path. <BR>The Bourne Shell does not export environment variables to children unless explicitly instructed to do so by using the export command. <\/P><br \/>\n<P>Argument Checking<BR>A good shell script should verify that the arguments sup- plied (if any) are correct. <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; if [ $# -ne 3 ]; then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; echo 1&gt;&amp;2 Usage: $0 19 Oct 91<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit 127<BR>&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P>This script requires three arguments and gripes accordingly. <\/P><br \/>\n<P>Exit status<BR>All Unix utilities should return an exit status. <BR>&nbsp;&nbsp;&nbsp; # is the year out of range for me?<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; if [ $year -lt 1901&nbsp; -o&nbsp; $year -gt 2099 ]; then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; echo 1&gt;&amp;2 Year \\&#8221;$year\\&#8221; out of range<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit 127<BR>&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; etc&#8230;<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; # All done, exit ok<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; exit 0<\/P><br \/>\n<P>A non-zero exit status indicates an error condition of some sort while a zero exit status indicates things worked as expected. <BR>On BSD systems there&#8217;s been an attempt to categorize some of the more common exit status codes. See \/usr\/include\/sysexits.h. <\/P><br \/>\n<P>Using exit status<BR>Exit codes are important for those who use your code. Many constructs test on the exit status of a command. <BR>The conditional construct is: <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; if command; then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; command<BR>&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P>For example, <BR>&nbsp;&nbsp;&nbsp; if tty -s; then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; echo Enter text end with \\^D<BR>&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P>Your code should be written with the expectation that others will use it. Making sure you return a meaningful exit status will help. <BR>Stdin, Stdout, Stderr<BR>Standard input, output, and error are file descriptors 0, 1, and 2. Each has a particular role and should be used accordingly: <BR>&nbsp;&nbsp;&nbsp; # is the year out of range for me?<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; if [ $year -lt 1901&nbsp; -o&nbsp; $year -gt 2099 ]; then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; echo 1&gt;&amp;2 Year \\&#8221;$year\\&#8221; out of my range<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit 127<BR>&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; etc&#8230;<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; # ok, you have the number of days since Jan 1, &#8230;<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; case `expr $days % 7` in<BR>&nbsp;&nbsp;&nbsp; 0)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; echo Mon$$<BR>&nbsp;&nbsp;&nbsp; 1)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; echo Tue$$<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; etc&#8230;<\/P><br \/>\n<P>Error messages should appear on stderr not on stdout! Output should appear on stdout. As for input\/output dialogue: <BR>&nbsp;&nbsp;&nbsp; # give the fellow a chance to quit<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; if tty -s ; then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; echo This will remove all files in $* since &#8230;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; echo $n Ok to procede? $c;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; read ans<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case &#8220;$ans&#8221; in<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; n*|N*)<BR>&nbsp;&nbsp;&nbsp; echo File purge abandoned;<BR>&nbsp;&nbsp;&nbsp; exit 0&nbsp;&nbsp; $$<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; esac<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RM=&#8221;rm -rfi&#8221;<BR>&nbsp;&nbsp;&nbsp; else<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RM=&#8221;rm -rf&#8221;<BR>&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P>Note: this code behaves differently if there&#8217;s a user to communicate with (ie. if the standard input is a tty rather than a pipe, or file, or etc. See tty(1)). <BR>Language Constructs<BR>For loop iteration<BR>Substitute values for variable and perform task: <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; for variable in word &#8230;<BR>&nbsp;&nbsp;&nbsp; do<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; command<BR>&nbsp;&nbsp;&nbsp; done<\/P><br \/>\n<P>For example: <BR>&nbsp;&nbsp;&nbsp; for i in `cat $LOGS`<BR>&nbsp;&nbsp;&nbsp; do<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mv $i $i.$TODAY<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cp \/dev\/null $i<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; chmod 664 $i<BR>&nbsp;&nbsp;&nbsp; done<\/P><br \/>\n<P>Alternatively you may see: <BR>&nbsp;&nbsp;&nbsp; for variable in word &#8230;; do command; done<\/P><br \/>\n<P>Case<BR>Switch to statements depending on pattern match <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; case word in<BR>&nbsp;&nbsp;&nbsp; [ pattern [ | pattern &#8230; ] )<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; command $$ ] &#8230;<BR>&nbsp;&nbsp;&nbsp; esac<\/P><br \/>\n<P>For example: <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; case &#8220;$year&#8221; in<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; [0-9][0-9])<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; year=19${year}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; years=`expr $year &#8211; 1901`<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $$<BR>&nbsp;&nbsp;&nbsp; [0-9][0-9][0-9][0-9])<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; years=`expr $year &#8211; 1901`<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $$<BR>&nbsp;&nbsp;&nbsp; *)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; echo 1&gt;&amp;2 Year \\&#8221;$year\\&#8221; out of range &#8230;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit 127<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $$<BR>&nbsp;&nbsp;&nbsp; esac<\/P><br \/>\n<P>Conditional Execution<BR>Test exit status of command and branch <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; if command<BR>&nbsp;&nbsp;&nbsp; then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; command<BR>&nbsp;&nbsp;&nbsp; [ else<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; command ]<BR>&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P>For example: <BR>&nbsp;&nbsp;&nbsp; if [ $# -ne 3 ]; then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; echo 1&gt;&amp;2 Usage: $0 19 Oct 91<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit 127<BR>&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P>Alternatively you may see: <BR>&nbsp;&nbsp;&nbsp; if command; then command; [ else command; ] fi<\/P><br \/>\n<P>While\/Until Iteration<BR>Repeat task while command returns good exit status. <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; {while | until} command<BR>&nbsp;&nbsp;&nbsp; do<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; command<BR>&nbsp;&nbsp;&nbsp; done<\/P><br \/>\n<P>For example: <BR>&nbsp;&nbsp;&nbsp; # for each argument mentioned, purge that directory<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; while [ $# -ge 1 ]; do<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _purge $1<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; shift<BR>&nbsp;&nbsp;&nbsp; done<\/P><br \/>\n<P>Alternatively you may see: <BR>&nbsp;&nbsp;&nbsp; while command; do command; done<\/P><br \/>\n<P>Variables<BR>Variables are sequences of letters, digits, or underscores beginning with a letter or underscore. To get the contents of a variable you must prepend the name with a $. <\/P><br \/>\n<P>Numeric variables (eg. like $1, etc.) are positional vari- ables for argument communication. <\/P><br \/>\n<P><BR>Variable Assignment<BR>Assign a value to a variable by variable=value. For example: <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; PATH=\/usr\/ucb:\/usr\/bin:\/bin; export PATH<\/P><br \/>\n<P>or <BR>&nbsp;&nbsp;&nbsp; TODAY=`(set \\`date\\`; echo $1)`<\/P><br \/>\n<P>Exporting Variables<BR>Variables are not exported to children unless explicitly marked. <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; # We MUST have a DISPLAY environment variable<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; if [ &#8220;$DISPLAY&#8221; = &#8220;&#8221; ]; then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if tty -s ; then<BR>&nbsp;&nbsp;&nbsp;&nbsp; echo &#8220;DISPLAY (`hostname`:0.0)? \\c&#8221;;<BR>&nbsp;&nbsp;&nbsp;&nbsp; read DISPLAY<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fi<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if [ &#8220;$DISPLAY&#8221; = &#8220;&#8221; ]; then<BR>&nbsp;&nbsp;&nbsp;&nbsp; DISPLAY=`hostname`:0.0<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fi<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; export DISPLAY<BR>&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P>Likewise, for variables like the PRINTER which you want hon- ored by lpr(1). From a user&#8217;s .profile: <BR>&nbsp;&nbsp;&nbsp; PRINTER=PostScript; export PRINTER<\/P><br \/>\n<P>Note: that the Cshell exports all environment variables. <\/P><br \/>\n<P>Referencing Variables<BR>Use $variable (or, if necessary, ${variable}) to reference the value. <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; # Most user&#8217;s have a \/bin of their own<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; if [ &#8220;$USER&#8221; != &#8220;root&#8221; ]; then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PATH=$HOME\/bin:$PATH<BR>&nbsp;&nbsp;&nbsp; else<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PATH=\/etc:\/usr\/etc:$PATH<BR>&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P>The braces are required for concatenation constructs. <BR>$p_01<\/P><br \/>\n<P>The value of the variable &#8220;p_01&#8221;. <BR>${p}_01<\/P><br \/>\n<P>The value of the variable &#8220;p&#8221; with &#8220;_01&#8221; pasted onto the end. <\/P><br \/>\n<P>Conditional Reference<\/P><br \/>\n<P>${variable-word}<\/P><br \/>\n<P>If the variable has been set, use it&#8217;s value, else use word. <BR>POSTSCRIPT=${POSTSCRIPT-PostScript};<BR>export POSTSCRIPT<\/P><br \/>\n<P>${variable:-word}<\/P><br \/>\n<P>If the variable has been set and is not null, use it&#8217;s value, else use word. <BR>These are useful constructions for honoring the user envi- ronment. Ie. the user of the script can override variable assignments. Cf. programs like lpr(1) honor the PRINTER environment variable, you can do the same trick with your shell scripts. <\/P><br \/>\n<P>${variable:?word}<\/P><br \/>\n<P>If variable is set use it&#8217;s value, else print out word and exit. Useful for bailing out. <\/P><br \/>\n<P>Arguments<BR>Command line arguments to shell scripts are positional vari- ables: <\/P><br \/>\n<P>$0, $1, &#8230;<\/P><br \/>\n<P>The command and arguments. With $0 the command and the rest the arguments. <BR>$#<\/P><br \/>\n<P>The number of arguments. <BR>$*, $@<\/P><br \/>\n<P>All the arguments as a blank separated string. Watch out for &#8220;$*&#8221; vs. &#8220;$@&#8221;. <BR>And, some commands: <BR>shift<\/P><br \/>\n<P>Shift the postional variables down one and decrement number of arguments. <BR>set arg arg &#8230;<\/P><br \/>\n<P>Set the positional variables to the argument list. <BR>Command line parsing uses shift: <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; # parse argument list<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; while [ $# -ge 1 ]; do<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case $1 in<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; process arguments&#8230;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; esac<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; shift<BR>&nbsp;&nbsp;&nbsp; done<\/P><br \/>\n<P>A use of the set command: <BR>&nbsp;&nbsp;&nbsp; # figure out what day it is<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; TODAY=`(set \\`date\\`; echo $1)`<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; cd $SPOOL<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; for i in `cat $LOGS`<BR>&nbsp;&nbsp;&nbsp; do<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mv $i $i.$TODAY<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cp \/dev\/null $i<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; chmod 664 $i<BR>&nbsp;&nbsp;&nbsp; done<\/P><br \/>\n<P>Special Variables <BR>$$<\/P><br \/>\n<P>Current process id. This is very useful for constructing temporary files. <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tmp=\/tmp\/cal0$$<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; trap &#8220;rm -f $tmp \/tmp\/cal1$$ \/tmp\/cal2$$&#8221;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; trap exit 1 2 13 15<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \/usr\/lib\/calprog &gt;$tmp<\/P><br \/>\n<P>$?<\/P><br \/>\n<P>The exit status of the last command. <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $command<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Run target file if no errors and &#8230;<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if [ $? -eq 0 ]<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; then<BR>&nbsp; etc&#8230;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P><BR>Quotes\/Special Characters<BR>Special characters to terminate words: <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; &amp; ( ) | ^ &lt; &gt; new-line space tab<\/P><br \/>\n<P>These are for command sequences, background jobs, etc. To quote any of these use a backslash (\\) or bracket with quote marks (&#8220;&#8221; or &#8221;).<BR>Single Quotes<\/P><br \/>\n<P>Within single quotes all characters are quoted &#8212; including the backslash. The result is one word. <\/P><br \/>\n<P><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; grep :${gid}: \/etc\/group | awk -F: &#8216;{print $1}&#8217;<\/P><br \/>\n<P>Double Quotes<BR>Within double quotes you have variable subsitution (ie. the dollar sign is interpreted) but no file name generation (ie. * and ? are quoted). The result is one word. <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if [ ! &#8220;${parent}&#8221; ]; then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; parent=${people}\/${group}\/${user}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P>Back Quotes<BR>Back quotes mean run the command and substitute the output. <\/P><br \/>\n<P><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if [ &#8220;`echo -n`&#8221; = &#8220;-n&#8221; ]; then<BR>&nbsp;&nbsp; n=&#8221;&#8221;<BR>&nbsp;&nbsp; c=&#8221;\\c&#8221;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<BR>&nbsp;&nbsp; n=&#8221;-n&#8221;<BR>&nbsp;&nbsp; c=&#8221;&#8221;<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P>and <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TODAY=`(set \\`date\\`; echo $1)`<\/P><br \/>\n<P>Functions<BR>Functions are a powerful feature that aren&#8217;t used often enough. Syntax is <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; name ()<BR>&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; commands<BR>&nbsp;&nbsp;&nbsp; }<\/P><br \/>\n<P>For example: <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; # Purge a directory<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; _purge()<BR>&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # there had better be a directory<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if [ ! -d $1 ]; then<BR>&nbsp;&nbsp;&nbsp;&nbsp; echo $1: No such directory 1&gt;&amp;2<BR>&nbsp;&nbsp;&nbsp;&nbsp; return<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; etc&#8230;<BR>&nbsp;&nbsp;&nbsp; }<\/P><br \/>\n<P>Within a function the positional parmeters $0, $1, etc. are the arguments to the function (not the arguments to the script). <BR>Within a function use return instead of exit. <\/P><br \/>\n<P>Functions are good for encapsulations. You can pipe, redi- rect input, etc. to functions. For example: <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; # deal with a file, add people one at a time<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; do_file()<BR>&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while parse_one<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; etc&#8230;<BR>&nbsp;&nbsp;&nbsp; }<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; etc&#8230;<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; # take standard input (or a specified file) and do it.<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; if [ &#8220;$1&#8221; != &#8220;&#8221; ]; then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cat $1 | do_file<BR>&nbsp;&nbsp;&nbsp; else<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; do_file<BR>&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P>Sourcing commands<BR>You can execute shell scripts from within shell scripts. A couple of choices: <\/P><br \/>\n<P>sh command<\/P><br \/>\n<P>This runs the shell script as a separate shell. For example, on Sun machines in \/etc\/rc: <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sh \/etc\/rc.local<\/P><br \/>\n<P>. command<BR>This runs the shell script from within the current shell script. For example: <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Read in configuration information<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .&nbsp; \/etc\/hostconfig<\/P><br \/>\n<P>What are the virtues of each? What&#8217;s the difference? The second form is useful for configuration files where environment variable are set for the script. For example: <BR>&nbsp;&nbsp;&nbsp; for HOST in $HOSTS; do<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # is there a config file for this host?<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if [ -r ${BACKUPHOME}\/${HOST} ]; then<BR>.&nbsp; ${BACKUPHOME}\/${HOST}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fi<BR>&nbsp;&nbsp;&nbsp; etc&#8230;<\/P><br \/>\n<P>Using configuration files in this manner makes it possible to write scripts that are automatically tailored for differ- ent situations. <BR>Some Tricks<BR>Test<BR>The most powerful command is test(1). <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; if test expression; then<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; etc&#8230;<\/P><br \/>\n<P>and (note the matching bracket argument) <BR>&nbsp;&nbsp;&nbsp; if [ expression ]; then<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; etc&#8230;<\/P><br \/>\n<P>On System V machines this is a builtin (check out the com- mand \/bin\/test). <BR>On BSD systems (like the Suns) compare the command \/usr\/bin\/test with \/usr\/bin\/[. <\/P><br \/>\n<P>Useful expressions are: <\/P><br \/>\n<P>test { -w, -r, -x, -s, &#8230; } filename<\/P><br \/>\n<P>is file writeable, readable, executeable, empty, etc? <BR>test n1 { -eq, -ne, -gt, &#8230; } n2<\/P><br \/>\n<P>are numbers equal, not equal, greater than, etc.? <BR>test s1 { =, != } s2<\/P><br \/>\n<P>Are strings the same or different? <BR>test cond1 { -o, -a } cond2<\/P><br \/>\n<P>Binary or; binary and; use ! for unary negation. <BR>For example <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; if [ $year -lt 1901&nbsp; -o&nbsp; $year -gt 2099 ]; then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; echo 1&gt;&amp;2 Year \\&#8221;$year\\&#8221; out of range<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit 127<BR>&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P>Learn this command inside out! It does a lot for you. <\/P><br \/>\n<P>String matching<BR>The test command provides limited string matching tests. A more powerful trick is to match strings with the case switch. <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; # parse argument list<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; while [ $# -ge 1 ]; do<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case $1 in<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -c*)&nbsp;&nbsp;&nbsp; rate=`echo $1 | cut -c3-`$$<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -c)&nbsp;&nbsp;&nbsp;&nbsp; shift;&nbsp; rate=$1 $$<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -p*)&nbsp;&nbsp;&nbsp; prefix=`echo $1 | cut -c3-`$$<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -p)&nbsp;&nbsp;&nbsp;&nbsp; shift;&nbsp; prefix=$1 $$<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -*)&nbsp;&nbsp;&nbsp;&nbsp; echo $Usage; exit 1 $$<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; disks=$*;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break&nbsp;&nbsp; $$<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; esac<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; shift<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; done<\/P><br \/>\n<P>Of course getopt would work much better. <\/P><br \/>\n<P>SysV vs BSD echo<BR>On BSD systems to get a prompt you&#8217;d say: <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; echo -n Ok to procede?;&nbsp; read ans<\/P><br \/>\n<P>On SysV systems you&#8217;d say: <BR>&nbsp;&nbsp;&nbsp; echo Ok to procede? \\c; read ans<\/P><br \/>\n<P>In an effort to produce portable code we&#8217;ve been using: <BR>&nbsp;&nbsp;&nbsp; # figure out what kind of echo to use<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; if [ &#8220;`echo -n`&#8221; = &#8220;-n&#8221; ]; then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; n=&#8221;&#8221;;&nbsp; c=&#8221;\\c&#8221;<BR>&nbsp;&nbsp;&nbsp; else<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; n=&#8221;-n&#8221;;&nbsp;&nbsp;&nbsp;&nbsp; c=&#8221;&#8221;<BR>&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; etc&#8230;<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; echo $n Ok to procede? $c; read ans<\/P><br \/>\n<P>Is there a person?<BR>The Unix tradition is that programs should execute as qui- etly as possible. Especially for pipelines, cron jobs, etc. <\/P><br \/>\n<P>User prompts aren&#8217;t required if there&#8217;s no user. <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; # If there&#8217;s a person out there, prod him a bit.<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; if tty -s; then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; echo Enter text end with \\^D<BR>&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P>The tradition also extends to output. <BR>&nbsp;&nbsp;&nbsp; # If the output is to a terminal, be verbose<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; if tty -s &lt;&amp;1; then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; verbose=true<BR>&nbsp;&nbsp;&nbsp; else<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; verbose=false<BR>&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P>Beware: just because stdin is a tty that doesn&#8217;t mean that stdout is too. User prompts should be directed to the user terminal. <BR>&nbsp;&nbsp;&nbsp; # If there&#8217;s a person out there, prod him a bit.<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; if tty -s; then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; echo Enter text end with \\^D &gt;&amp;0<BR>&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P>Have you ever had a program stop waiting for keyboard input when the output is directed elsewhere? <\/P><br \/>\n<P>Creating Input<BR>We&#8217;re familiar with redirecting input. For example: <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; # take standard input (or a specified file) and do it.<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; if [ &#8220;$1&#8221; != &#8220;&#8221; ]; then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cat $1 | do_file<BR>&nbsp;&nbsp;&nbsp; else<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; do_file<BR>&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P>alternatively, redirection from a file: <BR>&nbsp;&nbsp;&nbsp; # take standard input (or a specified file) and do it.<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; if [ &#8220;$1&#8221; != &#8220;&#8221; ]; then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; do_file &lt; $1<BR>&nbsp;&nbsp;&nbsp; else<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; do_file<BR>&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P>You can also construct files on the fly. <BR>&nbsp;&nbsp;&nbsp; rmail bsmtp &lt;<BR>&nbsp;&nbsp;&nbsp; rcpt to:<BR>&nbsp;&nbsp;&nbsp; data<BR>&nbsp;&nbsp;&nbsp; from: &lt;<A href=\"mailto:$1@newshost.uwo.ca\">$1@newshost.uwo.ca<\/A>&gt;<BR>&nbsp;&nbsp;&nbsp; to: <BR>&nbsp;&nbsp;&nbsp; Subject: Signon $2<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; subscribe $2 Usenet Feeder at UWO<BR>&nbsp;&nbsp;&nbsp; .<BR>&nbsp;&nbsp;&nbsp; quit<BR>&nbsp;&nbsp;&nbsp; EOF<\/P><br \/>\n<P>Note: that variables are expanded in the input. <\/P><br \/>\n<P>String Manipulations<BR>One of the more common things you&#8217;ll need to do is parse strings. Some tricks <\/P><br \/>\n<P><BR>&nbsp;&nbsp;&nbsp; TIME=`date | cut -c12-19`<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; TIME=`date | sed &#8216;s\/.* .* .* \\(.*\\) .* .*\/\\1\/&#8217;`<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; TIME=`date | awk &#8216;{print $4}&#8217;`<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; TIME=`set \\`date\\`; echo $4`<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; TIME=`date | (read u v w x y z; echo $x)`<\/P><br \/>\n<P>With some care, redefining the input field separators can help. <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; #!\/bin\/sh<BR>&nbsp;&nbsp;&nbsp; # convert IP number to in-addr.arpa name<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; name()<BR>&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp; set `IFS=&#8221;.&#8221;;echo $1`<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; echo $4.$3.$2.$1.in-addr.arpa<BR>&nbsp;&nbsp;&nbsp; }<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; if [ $# -ne 1 ]; then<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; echo 1&gt;&amp;2 Usage: bynum IP-address<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit 127<BR>&nbsp;&nbsp;&nbsp; fi<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; add=`name $1`<\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; nslookup &lt; &lt; EOF | grep &#8220;$add&#8221; | sed &#8216;s\/.*= \/\/&#8217;<BR>&nbsp;&nbsp;&nbsp; set type=any<BR>&nbsp;&nbsp;&nbsp; $add<BR>&nbsp;&nbsp;&nbsp; EOF<\/P><br \/>\n<P>Debugging<BR>The shell has a number of flags that make debugging easier: <\/P><br \/>\n<P>sh -n command<\/P><br \/>\n<P>Read the shell script but don&#8217;t execute the commands. IE. check syntax. <\/P><br \/>\n<P>sh -x command<\/P><br \/>\n<P>Display commands and arguments as they&#8217;re executed. In a lot of my shell scripts you&#8217;ll see <\/P><br \/>\n<P>&nbsp;&nbsp;&nbsp; # Uncomment the next line for testing<BR>&nbsp;&nbsp;&nbsp; # set -x<\/P><br \/>\n<P>&nbsp;<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>How to write a shell scriptIntroductionA shell is a command line interpretor. It takes commands and executes them. As such, it implements a programming language. The Bourne shell is used to create shell scripts &#8212; ie. programs that are interpreted\/executed by the shell. You can write shell scripts with the C-shell; however, this is not &hellip; <a href=\"https:\/\/www.strongd.net\/?p=440\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">How to write a shell script<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[101],"class_list":["post-440","post","type-post","status-publish","format-standard","hentry","category-linux","tag-shell"],"_links":{"self":[{"href":"https:\/\/www.strongd.net\/index.php?rest_route=\/wp\/v2\/posts\/440","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.strongd.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.strongd.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.strongd.net\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.strongd.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=440"}],"version-history":[{"count":1,"href":"https:\/\/www.strongd.net\/index.php?rest_route=\/wp\/v2\/posts\/440\/revisions"}],"predecessor-version":[{"id":935,"href":"https:\/\/www.strongd.net\/index.php?rest_route=\/wp\/v2\/posts\/440\/revisions\/935"}],"wp:attachment":[{"href":"https:\/\/www.strongd.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=440"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.strongd.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=440"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.strongd.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=440"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}