<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Pario TechnoBlob &#187; mailx</title>
	<atom:link href="http://pario.no/tag/mailx/feed/" rel="self" type="application/rss+xml" />
	<link>http://pario.no</link>
	<description>A cronological documentation test project, nothing serious, really!</description>
	<lastBuildDate>Thu, 02 Feb 2012 13:17:58 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Hints and Tips for general shell script programming</title>
		<link>http://pario.no/2007/08/31/hints-and-tips-for-general-shell-script-programing/</link>
		<comments>http://pario.no/2007/08/31/hints-and-tips-for-general-shell-script-programing/#comments</comments>
		<pubDate>Fri, 31 Aug 2007 18:14:36 +0000</pubDate>
		<dc:creator>Hans-Henry Jakobsen</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Scripting]]></category>
		<category><![CDATA[awk]]></category>
		<category><![CDATA[basename]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[mailx]]></category>
		<category><![CDATA[nawk]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[sed]]></category>

		<guid isPermaLink="false">http://hhj.no/wordpress/2007/08/31/hints-and-tips-for-general-shell-script-programing/</guid>
		<description><![CDATA[WARNING: this will fail if the user is playing with $0 For example using a symbolic or hard link with a unexpected name. # Simplest... # PROGNAME=`type $0 &#124; awk '{print $3}'` # search for executable on path PROGNAME=`basename $PROGNAME` # base name of program # Advanced... # Script name, in what directory, and in [...]]]></description>
			<content:encoded><![CDATA[<p>WARNING: this will fail if the user is playing with $0<br />
For example using a symbolic or hard link with a unexpected name.</p>
<pre>
# Simplest...
#
PROGNAME=`type $0 | awk '{print $3}'`  # search for executable on path
PROGNAME=`basename $PROGNAME`          # base name of program

# Advanced...
# Script name, in what directory, and in what directory is user
# running the script from.
#
# Discover where the shell script resides
ORIGDIR=`pwd`                          # original directory (builtin)
PROGNAME=`type $0 | awk '{print $3}'`  # search for executable on path
PROGDIR=`dirname $PROGNAME`            # extract directory of program
PROGNAME=`basename $PROGNAME`          # base name of program
cd $PROGDIR                            # go to that directory
PROGDIR=`pwd`                          # remove any symbolic link parts
cd $ORIGDIR                            # return to original directory (opt)

<strong>Results...</strong>
   $ORIGDIR    -- where the users was when called
   $PROGDIR    -- script directory location (and now current directory)
   $PROGNAME   -- This scripts executable name</pre>
<p><strong>Shell Script Option Handling</strong></p>
<pre>
PROGNAME=`basename $0`   # Or the above script locator
Usage() {
  echo &gt;&amp;2 "$PROGNAME:" "$@"
  echo &gt;&amp;2 "Usage: $PROGNAME [options] [file...]"
  exit 10
}</pre>
<pre>
#!/bin/sh
#
# script [options] args...
#
# The frist 'Usage()' code that follows these comments locates the script on
# disk, and then reads and output these comments as the documentation for this
# script.   That is the script commands and usage documention are in the same
# file, making it self documenting, via options.
#
###
#
#  programmers only docs, whcih are not output by Usage()
#
PROGNAME=`type $0 | awk '{print $3}'`  # search for executable on path
PROGDIR=`dirname $PROGNAME`            # extract directory of program
PROGNAME=`basename $PROGNAME`          # base name of program
Usage() {                              # output the script comments as docs
  echo &gt;&amp;2 "$PROGNAME:" "$@"
  sed &gt;&amp;2 -n '/^###/q; /^#/!q; s/^#//; s/^ //; 3s/^/Usage: /; 2,$ p'
          "$PROGDIR/$PROGNAME"
  exit 10;

#  Generalized Otion handling.
#
while [  $# -gt 0 ]; do
  case "$1" in

  # Standard help option.
  --help|--doc*) Usage ;;

  # Simple flag option
  -d) DEBUG=true ;;

  # Word flag option
  -debug) DEBUG=true ;;

  # Simple option and argument   EG: -n name
  -n) shift; name="$1" ;;

  # Option and argument joined   EG: -Jname
  -J) name=expr "$1" : '-.(.*)'` ||
            Usage "Option \"$1\" missing required value" ;;

  # Joined OR unjoined Argument  EG:  -Nname  or  -N name
  -N*) Name=`expr "$1" : '-.(..*)'`  || { shift; Name="$1"; } ;;

  # As last,  but if  -b0  is an posibility then you need to use this instead.
  # Otherwise it will think the value is unjoined when it isn't  Arrrgghhhh...
  -b*) bits=`expr "$1" : '-.(..*)'`
       [ "$bits" ] || { shift; bits="$1"; }
       ;;

  # Numbers with type checks     EG:  -{width}x{height}
  -[0-9]*x[0-9]*)
      w=`expr "$1" : '-([0-9]*)x'`          || Usage "Bed Geometry"
      h=`expr "$1" : '-[0-9]*x([0-9]*)$'`   || Usage "Bad Geometry"
      [ "$width" -eq 0  -o  "$height" -eq 0 ] &amp;&amp; Usage "Zero Geometry"
      geometry="${w}x${h}"
      ;;

  # Number with type check     EG:  -{size}
  -[0-9]*)
      size=`expr "$1" : '-([0-9]*)$'`  ||  Usage
      [ "$size" -eq 0 ] &amp;&amp; Usage
      Width=$size; Height=$size
      ;;

  # Generalised Argument Save    EG:    -G value  =&gt;  $opt_G
  -*) var=`expr "$1" : '-(.).*'`
      case "$var" in
      [a-zA-Z]) arg=`expr "$1" : '-.(..*)'`  ||  { shift; arg="$1"; }
                eval "opt_$var"="\"$arg\"" ;;
      *) Usage "Bad Non-Letter option \"$1\"" ;;
      esac ;;

  -)  break ;;           # STDIN,  end of user options

  --) shift; break ;;    # end of user options
  -*) Usage "Unknown option \"$1\"" ;;
  *)  break ;;           # end of user options

  esac
  shift   # next option
done

# Handle normal arguments now...
Files=${1+"$@"}

# or
[ $# -gt 0 ] &amp;&amp; Usage "To many Arguments"</pre>
<p><strong>Variable testing under shell (rules of thumb)</strong></p>
<p>Boolean Variable tests&#8230;</p>
<pre>
     true="true";   false=""
     [ "$var" ] &amp;&amp; echo var is true
     [ -z "$var" ] &amp;&amp; echo var is false</pre>
<p>Test of variables containing ANY string  (see PROBLEM CASES below)</p>
<pre>
     option=-xyzzy
     [ "X$option" != X ] &amp;&amp; echo option is defined
     [ "X$a" = "X$b" ] &amp;&amp; echo "$a equals $b"</pre>
<p>Rules of thumb&#8230;<br />
* Always quote all variables being tested<br />
* Only use the boolean type test (above) when<br />
all possible values are defineately known.<br />
* Prepend X (or other alphanumberic) to unknown strings being compared<br />
* Don&#8217;t use ! if you can avoid it.</p>
<p><strong>PROBLEM CASES&#8230;</strong></p>
<p>These cases cause the &#8220;[...]&#8221; tests to fail badly!</p>
<pre>
  [ $var ]         but  var="a = b"
  [ "$var" ]       but  var=-t  (actually any option starting with '-')
  [ "$a" = "$b" ]  but  a='('  and   b=')'</pre>
<p>This is why you must use the string test above (with &#8216;X&#8217; prefixes).</p>
<p>NOTE   test ! &#8230;  differs from UNIX to UNIX<br />
Under bash it is the NOT of the next 3 arguments following<br />
Under solaris &#8220;sh&#8221; it seems to handled with precedence.</p>
<p>EG:   test &#8220;&#8221; -a ! &#8220;&#8221;    is false as you would expect<br />
BUT   test ! &#8220;&#8221; -a &#8220;&#8221;    is true for BASH   and   false for Solaris SH</p>
<p><strong>Argument Sorting By Shells</strong></p>
<p>All Shells normally sort the output of any commands with meta characters.<br />
Also &#8220;ls&#8221; will sort its arguments again internally, while &#8220;echo&#8221; will not.</p>
<p>EG compare the output of these commands<br />
ls -d b* a*<br />
echo  b* a*<br />
echo  [ba]*</p>
<p>The first sorts the output so a&#8217;s are before b&#8217;s, the second does not.<br />
However the shell sorts the arguments of the third so a&#8217;s are again first.</p>
<p>However the &#8220;{}&#8221; meta characters do NOT sort the results&#8230;<br />
echo {b,a}*<br />
will output b&#8217;s first then a&#8217;s.</p>
<p>This can be useful when the order of the arguments is important.</p>
<p>This has been tested and works as described in  csh, tcsh, bash, and zsh</p>
<p><strong>Argument handling&#8230;</strong></p>
<p>If you care for the possibility that there aren&#8217;t any arguments.<br />
&#8220;$@&#8221;         -&gt;    &#8220;&#8221;     (one empty argument)<br />
${1+&#8221;$@&#8221;}    -&gt; nothing at all<br />
This is not a problem with newer modern shells, only very old Bourne shells.</p>
<p>WARNING: Command line shell argument start at 0!</p>
<p>sh -c &#8216;echo $*&#8217; 1 2 3 4 5<br />
or  sh -c &#8216;echo $*&#8217; `seq 5`<br />
Will print<br />
2 3 4 5</p>
<p>Note that the first arguement is missing. That is because it was assigned to<br />
argument $0 and not $1 or the $@ and $* variables!</p>
<p>To use all the arguments on the shell command line, use either<br />
sh -c &#8216;echo $*&#8217; junk `seq 5`<br />
or<br />
sh -c &#8216;echo $0 $*&#8217; `seq 5`</p>
<p>the later is not recomended as if no arguments are provided $0 defaults<br />
to &#8220;sh&#8221; instead of the empty string!</p>
<p>This is a posix compliant feature, $0 being the program name when a shell<br />
script is called. the &#8220;-c&#8221; command line script just layers on top of this.<br />
this is the case with solaris bourne sh, bash, ksh, and zsh.  csh and tcsh<br />
shell does not have an equivelence.</p>
<p>See &#8220;find&#8221; and its &#8220;-exec&#8221; option, (see below), for a useful example of this.</p>
<p>I also use it in csh aliases to provide a default argument.<br />
EG:  alias xvd  &#8216;sh -c &#8221;&#8217;cd ${1:-.}; xv -vsmap &amp;&#8221;&#8217; junk !:* &amp;&#8217;</p>
<p><strong>Auto change shells on brain dead systems (Ultrix)</strong></p>
<pre>
  #!/bin/sh -
  #
  # Check the type of shell that is running (For Ultrix)
  [ "X$1" != 'X-sh5' -a -f /bin/sh5 ] &amp;&amp; exec /bin/sh5 -$- "$0" -sh5 "$@"
  [ "X$1" = 'X-sh5' ] &amp;&amp; shift
  #</pre>
<p><strong>Getting environment from csh</strong></p>
<pre>
   env - DISPLAY=$DISPLAY HOME=$HOME TERM=dumb
         csh -cf 'set prompt="&gt; ";
                  source .cshrc; source .login;
                  echo "#------ENVIRONMENT------"
                  env' |
       sed -n '/#------ENVIRONMENT------/,$p'</pre>
<p><strong>Inserting a AWK or PERL script inside a SHELL script&#8230;</strong></p>
<p>Example&#8230;</p>
<pre>
    nawk '# output to mailx commands!
      /^[^ ]/ { recipent=$0; }
      /^ /    { print $0 &gt; "|mailx -s \"This is the test\" " recipent; }
      /^$/    { close( "|mailx -s \"This is the test\" " recipent );
      ' list.txt</pre>
<p>Note the whole script is inside single quotes on the nawk command line!</p>
<p>ASIDE: old versions of awk must have something on the first line thus the<br />
addition of the # comment to keep it happy!  Perl needs no such comment but<br />
does require a -e option to execute a command line argument.</p>
<p>To insert a external shell variable into the script you need to close<br />
the single quotes, output variable and re-open the single quotes. Also<br />
the variable sould be in double quotes so as to prvent any insertion of<br />
space characters</p>
<p>Example inserting a $prefix shell variable into a awk string.</p>
<pre>
       ...
       {  print "'"$prefix"'" $0; }
       ...</pre>
<p>Also to insert a single quote into the script you have to also exit the<br />
wrapping single quotes and supply it outside those quotes</p>
<pre>
       ...
       {  print "I just can'''t do that!"; }
       ...</pre>
<p>CAUTION: Watch for single quotes inside any COMMENTS which is in the script!<br />
Comments are within the single quotes so are also scanned for thos quotes.</p>
<p>CSH SCRIPTS: If you must write a csh script you will need to escape the new<br />
line at the end of every line, even though single quotes are being used, which<br />
requires to backslashes..  Also watch out for history escapes `!&#8217; which work<br />
inside single quotes!</p>
<p><strong>Is COMMAND available</strong></p>
<p>There is two techniques available to test if a command is available.<br />
The first is to use the `status&#8217; return of the &#8220;type&#8221; or &#8220;which&#8221; command<br />
of the shell you are using. The Second is to examine the output of that<br />
command itself.</p>
<p>Using status return.<br />
This is a simple method of testing the existance of a command<br />
but DOES NOT WORK ON ALL SYSTEMS!  The problem is that old shells<br />
(For Example: SunOS bourne-sh) always returns a true status weather<br />
the command is present or not!</p>
<p>Bourne Shell</p>
<pre>
     if  type COMMAND 2&gt;&amp;1; then
        # COMMAND is available
     fi</pre>
<p>C-Shell</p>
<p>Warning: The &#8220;which&#8221; command in  C shell is not a builtin but a external<br />
script which sources your .cshrc (YUCK). As such the bourne shell alias<br />
is prefered which will only search your current command PATH.</p>
<pre>
    if ( ! $?tcsh ) then
     alias which 'sh -c "type !:1 2&gt;&amp;1"'
    endif

    if ( -x "`which COMMAND &gt;/dev/null`" ) then
      # COMMAND Available
    endif</pre>
<p>TC-Shell</p>
<p>Tcsh 6.06 also does not return the correct status in its which<br />
command. Use it like the csh which above.</p>
<p>WARNING: this method will also test positive for :-<br />
subroutines, bash aliases, and probably other non-command definitions.</p>
<p>Examine output<br />
The other am more reliable method is to examine the output of the<br />
&#8220;type&#8221; or &#8220;which&#8221; command to looks for the string &#8220;not found&#8221; (See<br />
below).  This is more complex than the above status check but should<br />
work on ALL unix machines regardless of age.</p>
<p>Bourne Shell</p>
<pre>
    cmd_found() {
      case "`type $1 2&gt;&amp;1`" in *'not found'*) return 1 ;; esac; return 0
    }
    ...
    if  cmd_found COMMAND; then
      # COMMAND is available
    fi</pre>
<p>C-Shell &amp; Tcsh   (See notes below)</p>
<pre>
    if ( ! $?tcsh ) then
      alias which 'sh -c "type !:1 2&gt;&amp;1"'
    endif
    ...
    if ( "`which less`" !~ *'not found'* ) then
      # COMMAND Available
    endif</pre>
<p>NOTES for &#8220;which/type&#8221; commands :-</p>
<p>The above methods look for the specific string  &#8220;not found&#8221;<br />
This is important as the sh  type command  and tcsh which command<br />
produce different output, and this may also vary from bourne shell<br />
to bourne shell or other shell types.</p>
<p>Csh  &#8212; &#8220;which&#8221; is an unreliable shell script!<br />
fudge it into a shell script &#8220;type&#8221; command.<br />
See the &#8220;Which Problem&#8221; below.</p>
<p>Tcsh<br />
&gt; which less<br />
/opt/bin/less<br />
&gt; which junk<br />
junk: Command not found.<br />
&gt; which which<br />
which: shell built-in command.<br />
&gt; alias a alias<br />
&gt; which a<br />
a:  aliased to alias</p>
<p>Solaris Bourne shell<br />
&gt; type less<br />
less is /opt/bin/less<br />
&gt; type junk<br />
junk not found<br />
&gt; type type<br />
type is a shell builtin<br />
&gt; func(){ echo ha; }<br />
&gt; type func<br />
func is a function<br />
func(){<br />
echo ha<br />
}</p>
<p>Solaris Ksh<br />
As per Sh, but the actual function definition is NOT listed</p>
<p>Bash<br />
&gt; type less<br />
less is /opt/bin/less<br />
&gt; type junk<br />
bash: type: junk: not found<br />
&gt; type type<br />
type is a shell builtin<br />
&gt; func(){ echo ha; }<br />
&gt; type func<br />
func is a function<br />
func ()<br />
{<br />
echo ha<br />
}<br />
NOTE: bash also has a type -t which responds with a single word &#8220;file&#8221;,<br />
&#8220;alias&#8221;, &#8220;function&#8221;, &#8220;builtin&#8221;, &#8220;keyword&#8221;, or nothing if command does not<br />
exist.  A -p will print the disk file name, or nothing. A -a prints all the<br />
places that have that name.</p>
<p>From the results above, only the appearence of  &#8220;not found&#8221; in the false<br />
statement is consistant.  But only when the result is not a bourne shell<br />
function, which presumably replaces the real-command of that name.</p>
<p>The Expanded Bourne shell form, without using the &#8220;cmd_found&#8221; function is as<br />
follows, But is is a low simpler and easier to read if you use the funtion.</p>
<p>If command present</p>
<p>if  expr match &#8220;`type COMMAND 2&gt;&amp;1`&#8221; &#8216;.*not found&#8217; == 0 &gt;/dev/null;  then<br />
# COMMAND is available<br />
fi</p>
<p>and its inverse (not present)</p>
<p>if  expr match &#8220;`type COMMAND 2&gt;&amp;1`&#8221; &#8216;.*not found&#8217; &gt;/dev/null;  then<br />
# Comand is NOT present<br />
fi</p>
<p>finally only using built-in commands&#8230;</p>
<p>case &#8220;`type COMMAND`&#8221; in<br />
*&#8217;not found&#8217;*) # Command not found ;;<br />
*)             # Command found ;;<br />
esac</p>
<p>Functional forms<br />
cmd_found() {<br />
expr match &#8220;`type $1 2&gt;&amp;1`&#8221; &#8216;.*not found&#8217; == 0 &gt;/dev/null<br />
}<br />
OR<br />
cmd_found() {<br />
case &#8220;`type $1 2&gt;&amp;1`&#8221; in *&#8217;not found&#8217;*) return 1 ;; esac; return 0<br />
}</p>
<p><strong>Which problem!</strong></p>
<p>The which command is a csh script that specifically reads the .cshrc<br />
file to find out about aliases. To avoid having .cshrc do lots of odd<br />
things to your script, use the following form.</p>
<p>set program = `/bin/env HOME= /usr/ucb/which $program`</p>
<p>This is NOT a problem in Tcsh, where &#8220;which&#8221; is a built-in.</p>
<p>The best solution it to replace &#8220;which&#8221; with the bourne shell &#8220;type&#8221; command<br />
in C shells but leave it alone for TC shells.</p>
<p>if ( ! $?tcsh ) then<br />
alias which &#8216;sh -c &#8220;type !:1 2&gt;&amp;1&#8243; | sed &#8220;s/.* is //&#8221;&#8216;<br />
endif</p>
<p>set program = `which $program`</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
<strong>builtin cat command</strong></p>
<p>This cat only uses the shell builtins! As such can be used on a machine<br />
which has no access to shared libraries and nothing but the statically<br />
linked bourne sh can run.</p>
<p>shcat() {<br />
while test $# -ge 1; do<br />
while read i; do<br />
echo &#8220;$i&#8221;<br />
done &lt; $1<br />
shift<br />
done<br />
}</p>
<p>Of course the real cat command is only as big as it is, to protect the<br />
user from himself and to provide a huge number of options.</p>
<p>PS:  If the   ls   command is also not available then you can use</p>
<p>echo *</p>
<p>to do a directory listing using only builtins. Though this will not tell<br />
you what files are executables or sub-directories.</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Note `cmd` does save the newline characters within the output</p>
<p>::::&gt; m=`mount`;<br />
::::&gt; echo $m<br />
/ /usr /home<br />
::::&gt; echo &#8220;$m&#8221;<br />
/<br />
/usr<br />
/home</p>
<p>In other words outside quotes newlines in the input are treated purely<br />
as white space between arguments and thus ignored. Inside quotes<br />
newlines are retained as newlines.</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
One line if-then-else shell command</p>
<p>cmd1 &amp;&amp; cmd2 || cmd3</p>
<p>This hoever will execute cmd3 if cmd2 fails!  If cmd2 never fails &#8212; fine</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
The useless use of &#8216;cat&#8217;!</p>
<p>You often see in shell scripts&#8230;</p>
<p>cat input_file | some_command</p>
<p>The cat is usless as it is exactly the same as</p>
<p>some_command &lt; input_file</p>
<p>without needing to fork the extra &#8220;cat&#8221; process or creating a pipeline.<br />
However it is sometimes usefull to do anyway to make the code more readable.<br />
Particularly in long pipelines.</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Cat with here file and pipe to another command  (Shell Syntax Example)</p>
<p>cat &lt;<eof><br />
&#8230;..<br />
E0F<br />
while  read line;  do<br />
&#8230;<br />
done</eof></p>
<p>This is also a &#8220;Useless use of &#8216;cat&#8217;!&#8221;.  The equivelent without cat is&#8230;</p>
<p>while  read line;  do<br />
&#8230;<br />
done &lt;<eof><br />
&#8230;..<br />
EOF</eof></p>
<p>Of course the &#8220;cat&#8221; could be a &#8220;sed&#8221; &#8220;awk&#8221; or other filter of the here file<br />
data before you feed it into the while loop.</p>
<p>Also in the second method, the here file is then at the end, which can be<br />
confusing for later code readers as their is no indication at the start of the<br />
loop you are reading from a here file and not from the stdandard input of the<br />
script.</p>
<p>Because of this I usally prefer the cat, unless the while-loop is very very<br />
short.</p>
<p>WARNING: See the &#8220;non-posix shell bug&#8221; below</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Non-Posix shell bug</p>
<p>foo=bar<br />
while read line<br />
do<br />
foo=bletch<br />
done &lt; /etc/passwd<br />
echo &#8220;foo = $foo&#8221;</p>
<p>In some shells redirecting a file into a control structure will use a<br />
sub-shell for that structure.  In the above this will result in &#8220;foo =<br />
bar&#8221; instead of  &#8220;foo = bletch&#8221; assigned in the while loop.</p>
<p>The POSIX standard forbids this behaviour.</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
echo without a return (bsd &amp; sysV)</p>
<p>For Bourne Shell</p>
<p># echo without a return (bsd &amp; sysV)<br />
if [ "X`echo -n`" = "X-n" ]; then<br />
echo_n() { echo ${1+&#8221;$@&#8221;}&#8221;c&#8221;; }<br />
else<br />
echo_n() { echo -n ${1+&#8221;$@&#8221;}; }<br />
fi</p>
<p>For Csh or Tcsh (which you shouldn&#8217;t use for scripts anyway)</p>
<p># echo without a return (bsd &amp; sysV)<br />
if ( &#8220;X`echo -n`&#8221; == &#8216;X-n&#8217; ) then<br />
alias echo_n &#8216;echo !:* &#8220;c&#8221;&#8216;<br />
else<br />
alias echo_n &#8216;echo -n !:* &#8216;<br />
endif</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Columizing data</p>
<p>pr<br />
To convert a file (or part of a file) to columns.<br />
Warning: this program will truncate data to fit into space available!</p>
<p>Example file:  numbers 1 &#8211; 10, one number per line<br />
It is recomended that the -# be the first option.</p>
<p>Down the columns (like ls)<br />
pr -3 -t -l3<br />
|  `&#8211; number of columns<br />
`&#8212;&#8211; lines per column (very important to give correctly)<br />
if output width is a problem add  -w80<br />
EG: seq -f %02g 10 | pr -3 -t -l3 -w30<br />
Outputs:  01       04       07<br />
02       05       08<br />
03       06       09<br />
10</p>
<p>Or on most linux machines..<br />
column -x -c30<br />
`&#8212; width of output line</p>
<p>Across<br />
paste &#8211; - -<br />
Number of &#8211; given determines number of columns<br />
EG: seq 10 | paste &#8211; - -<br />
Outputs:  01       02       03<br />
04       05       06<br />
07       08       09<br />
10<br />
OR (with posible data truncation to fit ouptput line width)<br />
pr -t -l1 -3</p>
<p>column<br />
On linux to just put the data into columns use &#8220;column&#8221;<br />
CAUTION: any meta-characters found will may column silently ABORT,<br />
truncating the file being processed!</p>
<p>Example:<br />
seq -f %03g 5 5 500 | column</p>
<p>This can also format text tables using &#8220;-t&#8221; function of column to preserve<br />
the line by line structure of the input file.</p>
<p>column -s: -t /etc/group</p>
<p>OR a more complex example&#8230;</p>
<p>( echo &#8220;PERM LINKS OWNER GROUP SIZE MONTH DAY HH:MM NAME&#8221;;<br />
ls -l | tail -n+2;<br />
) | column -t</p>
<p>Warnings:<br />
* It also does not work as well as should<br />
EG: compare the output of   &#8220;ls&#8221;  and &#8220;ls | column&#8221;<br />
* Tabs are used.  Use the &#8220;expand&#8221; filter to remove them before using<br />
the &#8220;column&#8221; filter.</p>
<p>You can also using a non-standard perl module (from CPAN)</p>
<p>seq -f %03g 100 |<br />
perl -MArray::PrintCols -e &#8216;@a=&lt;&gt;; chomp @a; print_cols @a&#8217;</p>
<p>Otherwise manually handle columns in perl</p>
<p>seq -f %03g 100 |<br />
perl -e &#8216;  @a=&lt;&gt;; chomp @a;<br />
my ( $c, $i ) = (10,10);<br />
foreach ( @a ) {<br />
print(&#8220;n&#8221;),$i=$c  unless $i;<br />
printf &#8221;  %4s&#8221;, $_;   $i&#8211;;<br />
} print &#8220;n&#8221;; &#8216;</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Suppressing the shell background fork message (*csh)</p>
<p>The trick is to redirect the standard error output of the _shell_ itself</p>
<p>Korn (with loss of standard error)</p>
<p>( command &amp; ) 2&gt;/dev/null</p>
<p>csh/tcsh (this appears to work)</p>
<p>( command &amp; )</p>
<p>bourne shell does not give a message about background forks ever.</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Auto Background a shell script<br />
#!/bin/csh -f<br />
if ( $1:q != &#8216;&#8230;&#8217; ) then<br />
( $0 &#8216;&#8230;&#8217; $*:q &amp; )<br />
exit 0<br />
endif<br />
shift<br />
&#8230;rest of script to run in background&#8230;</p>
<p>OR<br />
#!/bin/sh<br />
foreground stuff<br />
( background stuff<br />
) &amp;<br />
exit</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Sort a shell array</p>
<p>=======8&lt; &#8212;&#8212;&#8211;<br />
i=0<br />
for n ;<br />
do<br />
Â  a[$((i++))]=$n ;<br />
done</p>
<p># sort array</p>
<p>for (( i=0; $i&lt;(${#a[@]}-1); i++ )) ;<br />
do<br />
Â  for (( j=i+1; $j&lt;(${#a[@]}); j++ )) ;<br />
Â  do<br />
Â Â Â  if [[ ${a[$j]} &lt; ${a[$i]} ]] ;<br />
Â Â Â  then<br />
Â Â Â Â Â  t=${a[$j]};<br />
Â Â Â Â Â  a[$j]=${a[$i]};<br />
Â Â Â Â Â  a[$i]=$t;<br />
Â Â Â  fi<br />
Â  done<br />
done</p>
<p># launch xmms with sorted list<br />
xmms -e &#8220;${a[@]}&#8221;<br />
=======8&lt;&#8212;&#8212;&#8211;</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Command timeout</p>
<p>Run a command but kill it if it runs for too long. This prevents scripts<br />
hanging on a command, especially network related commands, like nslookup.</p>
<p>Simple C solution&#8230;<br />
Look at ~/store/c/programs/timeout.c  which will timeout a command<br />
simply and easilly.  A lot better than the complex solutions below.</p>
<p>&#8212;- Attempt 1 &#8212;-<br />
Runs the command in the background and waits for it to<br />
complete. A sleep command is also run to provide a timeout.</p>
<p>Works but full timeout period is always waited before exiting</p>
<p>TIMEOUT=60  # timelimit for command<br />
( command_which_can_hang &amp;<br />
sleep $TIMEOUT; kill $! 2&gt;/dev/null<br />
)</p>
<p>This does not work. We will wait on the sleep for the full TIMEOUT period<br />
regardless of how fast the command completes.  If the command is known to<br />
never finish then the above will be fine!</p>
<p>&#8212;- Attempt 2 &#8212;&#8211;<br />
Works for modern day shells&#8230;</p>
<p>do_quota() {<br />
# lookup the users disk quota but with a timeout<br />
quota -v &#8220;$@&#8221; &amp;<br />
# now ensure that the above command does not run too long<br />
cmd_pid=$!<br />
( sleep $QUOTA_TIMEOUT<br />
echo &gt;&amp;2 &#8220;Quota Timeout&#8221;<br />
kill -9 $cmd_pid 2&gt;/dev/null<br />
) &amp;<br />
kill_pid=$!<br />
wait $cmd_pid<br />
kill $kill_pid 2&gt;/dev/null<br />
}</p>
<p>The problem here is that this works fine but the sleep will still continue<br />
for the full timeout period. Basically the parent has no simple way of<br />
determining the PID of the sleep, to abort it, if it was not needed.  This is<br />
not a problem if the sleep itself is not too long, so it doesn&#8217;t stick around<br />
for hour.</p>
<p>The sub-shell however is killed, so the kill command is not run if the command<br />
completes before the timeout</p>
<p>Unfortunatally BASH generates a &#8220;terminate&#8221; message on standard out when<br />
the command is killed due to timeout :-(</p>
<p>Full Example<br />
try with/without a timeout argument of 15&#8230;</p>
<p>=======8&lt; &#8212;&#8212;&#8211;<br />
#!/bin/sh<br />
#<br />
# Countdown [abort_after]<br />
#<br />
COUNTDOWN_FROM=10          # count down from this value<br />
COUNTDOWN_ABORT=${1:-4}    # abort countdown after this long</p>
<p>countdown() {<br />
i=$COUNTDOWN_FROM<br />
echo &#8220;Countdown continuing at T minus $i&#8221;<br />
while [ $i -gt 0 ]; do<br />
sleep 1<br />
i=`expr $i &#8211; 1`<br />
[ $i -eq 3 ] &amp;&amp; echo &#8220;Main engine start&#8221;<br />
|| echo &#8220;&#8211;&gt; $i&#8221;<br />
done<br />
echo &#8220;We have lift off!!!&#8221;<br />
exit 0;<br />
}</p>
<p>countdown &amp;   # the command that we wish to timeout<br />
cmd_pid=$!</p>
<p>( sleep $COUNTDOWN_ABORT;<br />
kill $cmd_pid<br />
echo &#8220;countdown aborted&#8221;;<br />
) &amp;<br />
kill_pid=$!</p>
<p>wait $cmd_pid<br />
echo &#8220;Launch status: $?&#8221;</p>
<p>kill $kill_pid 2&gt;/dev/null</p>
<p>#/bin/ps   # uncomment to show if sleep is still running</p>
<p>=======8&lt; &#8212;&#8212;&#8211;</p>
<p>&#8212;- Attempt 3 &#8212;-<br />
Reports a `Terminate&#8217; error to stdout if command timesout.</p>
<p>TIMEOUT=60  # timelimit for command<br />
command_which_can_hang &amp;<br />
cmd_pid=$!<br />
sleep $TIMEOUT | (<br />
read nothing  # wait on sleep to finish<br />
kill $cmd_pid 2&gt;/dev/null;            # otherwise abort the command!<br />
) &amp;<br />
sleep_pid=$!<br />
wait $cmd_pid<br />
kill -ALRM $sleep_pid 2&gt;/dev/null     # kill sleep if still running</p>
<p>The reason this works is that we are specifically killing the sleep (we hope).<br />
This is because $! returns the pid of the first command in a command pipeline,<br />
so we fake such a pipeline.</p>
<p>WARNING: Some shells may have $! set to the last command in pipeline!  which<br />
make this equivelent to &#8220;Attempt 2&#8243; though will also work as expected.</p>
<p>Using an ALRM signal to kill sleep is especially good as it means sleep exits<br />
normally and cleanly, stopping a shell like &#8220;bash&#8221; from producing &#8220;Terminated&#8221;<br />
messages all over the place.</p>
<p>In the &#8220;kill command&#8221; sub-shell, the &#8220;read&#8221; is needed to ensure it waits for<br />
non-existant output from the sleep command. It will automatically exit when<br />
the sleep exits without producing any output.</p>
<p>A &#8220;kill -0 $pid&#8221; could be used to check if the pid given is still running or<br />
not, and in a full working version is probably a good idea.</p>
<p>I suppose we could also backgound the sleep, and have a background kill<br />
process wait specifically on the background sleep process but this seems to<br />
complex to me&#8230;.  It also does NOT work as you wither have to put a a wait<br />
for the sleep the sub-shell (or a looped pid poll) which did NOT launch the<br />
sleep and thus does not know its pid, or the background sleep in the<br />
sub-shell, in which case the main script did not launch it so can&#8217;t abort it.<br />
Very difficult to sort that out.  Better to use a command pipeline.</p>
<p>I supose you could use a `SIGCHLD trap&#8217; but that is a completely different<br />
method.  If anyone have created such a trap in shell, please email me your<br />
code, so I can include it here (with your name of course).</p>
<p>Full example..</p>
<p>TIMEOUT=20    # Limit name server lookup to 20 seconds<br />
# &#8230;<br />
do_nslookup() {<br />
# Do a FAST nslookup &#8211; even if primary namserver is down<br />
# Background the required command &#8211; note the process id<br />
# any output to be printed to standard argument and calling function<br />
nslookup 2&gt;/dev/null &lt; &lt;-EOF | sed -n &#8216;/arpa/s/.*name = //p&#8217; &amp;<br />
set type=PTR<br />
$1<br />
EOF<br />
cmd_pid=$!<br />
#<br />
# Wait for it (sleep pipeline) &#8211; note sleep&#8217;s process id<br />
sleep $TIMEOUT | (<br />
read nothing  # wait on sleep to finish<br />
kill -0 $cmd_pid 2&gt;/dev/null || exit;  # cmd finished? &#8211; exit sub-shell<br />
kill $cmd_pid 2&gt;/dev/null;<br />
echo &gt;&amp;2 &#8220;WARNING: nslookup failed to return in $TIMEOUT seconds!&#8221;<br />
) &amp;<br />
sleep_pid=$!<br />
#<br />
# wait for nslookup to finish or be killed<br />
wait $cmd_pid<br />
kill -0 $sleep_pid 2&gt;/dev/null || return;  # sleep finished?<br />
kill $sleep_pid 2&gt;/dev/null<br />
}<br />
# &#8230;<br />
Reverse_IP_Arg=&#8221;4.3.2.1.in-addr.arpa&#8221;<br />
FQDN_hostname=`do_nslookup $Reverse_IP_Arg`</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Loop until parent process dies</p>
<p>Background tasks have an annoying habit of continuing to run AFTER you have<br />
logged out. This following example program looks up the launching parent<br />
process (typically your login shell) and only loops if the parent process is<br />
still alive.</p>
<p>WARNING: The PS command varies from UNIX system to UNIX system so you will<br />
have to tweek the arguments to the `ps&#8217; command to make this script work on<br />
your UNIX system</p>
<p>=======8&lt; &#8212;&#8212;&#8211;<br />
#!/bin/sh<br />
#<br />
# Loop until parent dies<br />
#<br />
sleep_time=300     # time between background checks</p>
<p># Pick the appropriate ps options for your UNIX system<br />
# Uncomment ONE of the following lines<br />
#job_opt=xj; sep=&#8221;";  ppid=1;   # SunOS<br />
job_opt=xl; sep=&#8221; &#8220;; ppid=4;   # Solaris<br />
#job_opt=xl; sep=&#8221; &#8220;; ppid=4;   # IBM UNIX (aix)<br />
#job_opt=xl; sep=&#8221; &#8220;; ppid=4;   # SGI UNIX (irix)</p>
<p># Discover the parents process ID<br />
set &#8211; `ps $job_opt$sep$$ | tail -n+2` &#8220;1&#8243;<br />
eval parent=$$ppid</p>
<p># While parent is still alive<br />
# The  kill command &#8220;-0&#8243; option checks to see if process is alive!<br />
# It does NOT actually kill the process (EG: test process)<br />
while  kill -0 $parent 2&gt;/dev/null; do<br />
# &#8230;<br />
# Do the background job here<br />
# &#8230;<br />
sleep $sleep_time<br />
done</p>
<p># Parent process has died so we also better die.<br />
exit 0<br />
=======8&lt; &#8212;&#8212;&#8211;</p>
<p>Also see the script   ~anthony/bin/scripts/hostspace<br />
as an example of a shell script for multiple hosts</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Convert ls permissions to octal format</p>
<p>&#8230; anyone? &#8230;</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Time in Seconds, NOW!  (for later comparision)</p>
<p>EG: getting the number of seconds since epoch (midnight, 1/1/1970, GMT)</p>
<p>This is for the purposes of timing various command and comparing the start<br />
and end times.  The problem is as there is no standard way for geting the<br />
system clock under normal plain shell scripts.</p>
<p>Under linux (Gnu-date) you can<br />
date +%s</p>
<p>With perl you can<br />
perl -e &#8216;print time(), &#8220;n&#8221;&#8216;</p>
<p>This seems to work very well on all system I know of, and can be used for<br />
sequencing events. Do not rely on it for time deltas (how long) as it does not<br />
handle years (%j is day within the year)</p>
<p>date +&#8217;%j * 86400 + %H * 3600 + %M * 60 + %S&#8217; | bc</p>
<p>Convert Time since epoch to Date<br />
(for more see the perl/general.hints page)<br />
perl -e &#8216;require &#8220;ctime.pl&#8221;; print &amp;ctime(1000000000);</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Get the date of yesterday (without a lot of fuss)</p>
<p>Getting yesterdays day is usually best done in either C or perl.</p>
<p>#!/usr/bin/perl<br />
require &#8220;ctime.pl&#8221;;<br />
print &amp;ctime($^T &#8211; 60*60*24);</p>
<p>By changing your timezone you can fake the getting of yesterdays date,<br />
But ONLY if you live in Australia where it is already tomorrow!.<br />
It is not perfect (2 hours short but close enough in most cases)</p>
<p>env TZ=GMT-12 date</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Four Digit Year in shell (for logfiles)</p>
<p>For Newer UNIX Machines, IEEE date format</p>
<p>date=`date +&#8217;%Y-%m-%d %R:%S&#8217;`</p>
<p>For Older UNIX machines (SunOS)&#8230;<br />
While the newest UNIX&#8217;s allow a %Y for a four digit year older machines<br />
like SunOS does NOT. This means the year will need to be extracted<br />
from the date output (or auturnative format code).</p>
<p>year=`date | sed &#8216;s/.*([0-9][0-9][0-9][0-9]).*/1/&#8217;`<br />
date=`date $year-%m-%d %H:%M:%S&#8221;</p>
<p>If you have the MH mail system install you can convert the date to rfc822 date<br />
format, use the &#8220;dp&#8221; program. This has its own mh output format codes.</p>
<p>/usr/lib/mh/dp &#8220;`date`&#8221;<br />
or  /usr/lib/mh/dp -format &#8216;%(year{text})&#8217; &#8220;`date`&#8221;<br />
or  /usr/lib/mh/dp<br />
-format &#8216;%04(year{text})-%02(mon{text})-%02(mday{text})&#8217;<br />
&#8220;`date`&#8221;</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Setting a timed alarm in shell<br />
This can be done (as a background task) exactly how is an another matter<br />
As an educated guess probably something like..</p>
<p># Trap the USR1 signal<br />
trap &#8220;do timeout commands&#8221; 16<br />
( sleep $timeout; kill -16 $$; ) &amp;</p>
<p>See also  &#8220;Command timeout&#8221; above which was a later addition to this file.</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Am I a Non-Interactive Shell</p>
<p>bourne sh:<br />
if [ -z "$PS1" ]; then   # script/remote execution (non-interactive)<br />
csh:<br />
if ( ! $?prompt ) then   # script/remote execution (non-interactive)</p>
<p>In the bourne shell a better way is to test the shell options &#8220;$-&#8221;<br />
for the interactive flag directly.</p>
<p>case $- in<br />
*i*) ;;   # do things for interactive shell<br />
*)   ;;   # do things for non-interactive shell<br />
esac</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Merge multiple blank lines into one line.</p>
<p>This is not easy, as we want to preserve a blank line, removing extras.<br />
Two methods,  print paragraphs, or delete extra blanks.</p>
<p>awk &#8216;{ printf &#8220;%s &#8220;, $0 } NF == 0 { print &#8220;n&#8221; }&#8217; filename</p>
<p>cat -s    # will do this (if available)</p>
<p>perl -ne &#8216;if (/S/) { print; $i=0 } else {print unless $i; $i=1; }&#8217;</p>
<p>perl -ne &#8216;print if /S/../^s*$/&#8217;</p>
<p>sed &#8216;/./,/^$/!d&#8217;               # NOTE: (t)csh users must backslash the !<br />
sed &#8216;/[^      ]/,/^[  ]*$/!d&#8217;  # For blank lines with spaces and tabs</p>
<p># Line joining in the vim editor!  as a macro<br />
:map QE  :$s/$/rZ/<cr>:g/^[ <tab>]*$/,/[^ </tab><tab>]/-j<cr>Gdd<br />
or       :%s/ns*n(s*n)*/rr/</cr></tab></cr></p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
LIST functions<br />
(Also usable for PATH-like environment variables)</p>
<p>list=&#8221;list_variable&#8221;              # a:b:c:d<br />
element=&#8221;element_of_list&#8221;         # e<br />
sep=&#8221;list_seperator&#8221;              # :</p>
<p>Append to list (even if empty!)<br />
list=&#8221;${list:+$list$sep}$element&#8221;</p>
<p>Split up list<br />
function split_path () {<br />
( IFS=&#8221;$sep&#8221;<br />
set &#8212; $list<br />
for f in &#8220;$@&#8221;; do<br />
echo -n &#8220;${f:-.} &#8221;<br />
done; echo<br />
)<br />
}</p>
<p>Count of elements<br />
count=`IFS=&#8221;$sep&#8221;; set &#8211; $list; echo $#`</p>
<p>get I&#8217;th element (shell must understand shift argument)<br />
element=`IFS=&#8221;$sep&#8221;; set &#8211; $list; shift $I; echo $1`</p>
<p>get first element<br />
element=`IFS=&#8221;$sep&#8221;; set &#8211; $list; echo $1`</p>
<p>delete first element<br />
list=`echo &#8220;$list&#8221; | sed &#8220;/$sep/!d; /:/s/^[^$sep]*$sep//;&#8221;`</p>
<p>delete specific element over the whole list<br />
list=`echo &#8220;$list&#8221; | sed &#8220;s|${element}${sep}||g; s|${sep}${element}$||;&#8221;`</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Checking for a numerical value<br />
case &#8220;$var&#8221; in<br />
&#8221; | *[!0-9]*)  echo &#8220;non-numeric&#8221; ;;<br />
*)              echo &#8220;numeric&#8221; ;;<br />
esac                     # &#8212; Nick Holloway  (alfie@dcs.warwick.ac.uk)<br />
OR<br />
echo &#8220;$arg&#8221; | egrep &#8216;^[0-9]+$&#8217; &gt;/dev/null || echo &#8220;not-numeric&#8221;</p>
<p>Get value from option  (NOT zero)<br />
expr &#8220;$var&#8221; : &#8216;([0-9]*)$&#8217; || echo &#8220;not a non-zero-numeric value&#8221;</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
random number generation in a shell<br />
nawk:<br />
set range  = ????<br />
set random = `nawk &#8216;BEGIN { srand();<br />
printf &#8220;%d&#8221;,  rand()*&#8217;$range&#8217;<br />
}&#8217; /dev/null`</p>
<p>date:<br />
set range  = ????     # file length &#8212; `wc -l &lt; $file`<br />
set date   = `date +%j%H%M%S`<br />
set random = `expr $date % $range`</p>
<p>(k/z/ba)sh<br />
$RANDOM<br />
Posible improvment&#8230;<br />
!/bin/bash<br />
MX=&#8221;0123456789&#8243;<br />
NL=&#8221;5&#8243;   # size of the random number wanted<br />
while [ ${n:=1} -le $NL ]<br />
do<br />
NUM=&#8221;$NUM${MX:$(($RANDOM%${#MX})):1}&#8221;<br />
let n+=1<br />
done<br />
echo &#8220;$NUM&#8221;</p>
<p>Programed (csh) (could be via expr too)<br />
set multiplier = 25173<br />
set modulus =    65536<br />
set increment =  13849<br />
set seedfile =   $HOME/.rnd  # seed file to use</p>
<p>if ( ! -f $seedfile ) then<br />
echo &#8217;17&#8242; &gt; $seedfile<br />
endif</p>
<p>@ number = ( `cat $seedfile` * $multiplier + $increment ) % $modulus<br />
echo $number &gt; $seedfile</p>
<p>@ number = $number % $range<br />
echo $number</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
increment a character in shell</p>
<p>char=`echo $char | tr &#8216; -~&#8217; &#8216;!-~&#8217;</p>
<p>or (sutable for hex chars)</p>
<p>str1=&#8221;ABCDEFGHIJKLMNOPQRSTUVWXYZ&#8221;<br />
str2=&#8221;BCDEFGHIJKLMNOPQRSTUVWXYZA&#8221;<br />
pos=`expr index $str1 $char`<br />
char=`expr substr $str2 $pos 1`<br />
echo $char</p>
<p>In perl the &#8220;increment alpha string&#8221; makes this easy</p>
<p>perl -e &#8216;$n=&#8221;&#8216;&#8221;$char&#8221;&#8216;&#8221;; print substr( ++$n, -1), &#8220;n&#8221;;&#8217;</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Protecting shell scripts from ^Z</p>
<p>This signal can&#8217;t normally be stoped in a shell, the trick is to change<br />
key generating the signal (Don&#8217;t forget to return it to normal).</p>
<p>stty susp undef      &#8212; if available<br />
stty susp &#8216;^-&#8217;       &#8212; maybe    system dependant</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Curses in shell script<br />
To use termcap entries in a shell script use the `tput&#8217; command<br />
EXAMPLES<br />
/usr/5bin/tput bold            # bold (extra half brigtht mode)<br />
/usr/5bin/tput bink            # bink mode (if available)<br />
/usr/5bin/tput rev             # reverse video<br />
/usr/5bin/tput smul            # underline mode<br />
/usr/5bin/tput smso            # standout (usually reverse)<br />
/usr/5bin/tput rmso            # end standout  (return to normal)<br />
/usr/5bin/tput clear           # clear screen<br />
/usr/5bin/tput cup 5 23        # cursor to move to row 5 column 23<br />
See terminfo(5) for more info.</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
<strong>Split pipe into two separate commands (executable tee)</strong></p>
<p>awk &#8216;{ print | &#8220;&#8216;cmd1&#8242;&#8221; ; print }&#8217; | cmd 2</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Capitalize the first word.</p>
<p>The initial solutions in the news group cam out to more than 10 lines of code!</p>
<p># Ken Manheimer   (expr-tr-expr)  NOTE: fails if   word=&#8221;match&#8221;<br />
Word=`expr &#8220;$word&#8221; : &#8220;(.).*&#8221; | tr a-z A-Z&#8220;expr &#8220;$word&#8221; : &#8220;.(.*)&#8221;`</p>
<p># Paul Falstad    (cut-tr-sed)<br />
Word=`echo $word|tr a-z A-Z|cut -c1&#8220;echo $word|sed s/.//`</p>
<p>==&gt; # Logan Shaw     (cut-tr-cut)<br />
Word=`echo &#8220;$word&#8221; | cut -c1 | tr [a-z] [A-Z]&#8220;echo &#8220;$word&#8221; | cut -c2-`</p>
<p># Harald Eikrem (sed only)<br />
Word=`echo $word | sed  -e &#8216;<br />
h; &#8216;y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/;<br />
G; &#8216;s/(.).*n./1/; &#8216; `</p>
<p># Tom Christiansen  (perl)<br />
Word=`echo $word | perl -pe &#8216;s/.*/u$&amp;/&#8217;`</p>
<p># Jean-Michel Chenais <jean><br />
# (korn shell built-ins only)<br />
typeset -u W;typeset -l w;W=${word%${word#?}};w=$W;Word=$W${word#${w}}</jean></p>
<p># perl of course makes this easy<br />
perl -e &#8216;print &#8220;u&#8217;&#8221;$word&#8221;&#8216;&#8221;&#8216;</p>
<p><strong>Multi-line Paragraph handline</strong></p>
<p>Most text files including this one consists of paragraphs of multiple lines<br />
seperated by a blank line.</p>
<p>Convert all paragraphs into a single line (blank line seperated)</p>
<p>sed &#8216;/^$/d; :loop y/n/ /; N; /n$/! b loop;  s/   */ /g; s/^ //; s/ $//&#8217;</p>
<p>Also remove blank lines between paragraph lines</p>
<p>sed &#8216;/^$/d; :loop N; s/n$//; T loop; y/n/ /; s/   */ /g; s/^ //; s/ $//&#8217;</p>
<p>WARNING: better space handling is probably needed, especially for a last<br />
paragraph that has no final blank line after it.</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Find a Process of a particular name                        &#8212; see csh alias pf</p>
<p>Usign grep</p>
<p>ps auxgww | grep &#8220;$NAME&#8221; | grep -v grep | cut -c1-15,36-99</p>
<p>Merging the two greps..</p>
<p>ps uxw | grep &#8220;[s]sh-agent&#8221; | awk &#8216;{print $2}&#8217;</p>
<p>The &#8220;[s]sh-agent&#8221;   will not match the grep process itself!<br />
EG: it will not match the &#8220;[s]sh-agent&#8221; string in the grep process</p>
<p>Using awk&#8230;</p>
<p>ps auxgww | awk &#8220;/$NAME/ &amp;&amp; ! /(awk)/&#8221; | cut -c1-15,36-99</p>
<p>or for a exact process name</p>
<p>ps auxgww | awk &#8216;/(^| |(|/)$NAME( |)|$)/&#8217; | cut -c1-15,36-99</p>
<p>or alturnativeally which matches under a lot of conditions&#8230;<br />
EG: matches    :01 NAME arg<br />
:01 some/path/NAME<br />
:01 NAME:</p>
<p>ps auxgww |<br />
awk &#8216;/:[0-9][0-9] (|[^ ]*/)$NAME($| |:)/&#8217; | cut -c1-15,36-99</p>
<p>Perl version (also matches on username)</p>
<p>ps auxgww |<br />
perl -nle &#8216;print if $. == 1<br />
|| /^s*!:1s/o<br />
|| /:dd (|[ *|[^ ]*/)!:1($|[]: ])/o; &#8216;</p>
<p>As you can see things can get complex very quickly</p>
<p><strong>Context Grep</strong><br />
Or how to display lines before/after search pattern.</p>
<p>GNU grep, has context built in to it, check out the -A, -B, and -C options.<br />
otherwise<br />
grep -v pattern file | diff -c3 &#8211; file | grep &#8216;^. &#8216; | colrm 1 2<br />
or<br />
grep -n $1 $2 | awk -F: &#8216;{ print $1 }&#8217; | while read linenum<br />
do<br />
awk &#8216;NR&gt;target-5 &amp;&amp; NR<target target="$linenum"><br />
echo &#8220;&#8212;&#8212;-next match&#8212;&#8212;&#8212;&#8221;<br />
done<br />
</target></p>
<p>Source: <a href="http://www.cit.gu.edu.au/~anthony/info/shell/script.hints">http://www.cit.gu.edu.au</a></p>
<script type="text/javascript">var wordpress_toolbar_urls = ["http:\/\/www.cit.gu.edu.au\/~anthony\/info\/shell\/script.hints"];var wordpress_toolbar_url = "http://pario.no/wp-content/plugins/wordpress-toolbar/toolbar.php";var wordpress_toolbar_oinw = "n";var wordpress_toolbar_hash = "aHR0cDovL3BhcmlvLm5vLzIwMDcvMDgvMzEvaGludHMtYW5kLXRpcHMtZm9yLWdlbmVyYWwtc2hlbGwtc2NyaXB0LXByb2dyYW1pbmcvPHdwdGI%2BSGludHMgYW5kIFRpcHMgZm9yIGdlbmVyYWwgc2hlbGwgc2NyaXB0IHByb2dyYW1taW5nPHdwdGI%2BaHR0cDovL3BhcmlvLm5vPHdwdGI%2BUGFyaW8gVGVjaG5vQmxvYg%3D%3D";</script>]]></content:encoded>
			<wfw:commentRss>http://pario.no/2007/08/31/hints-and-tips-for-general-shell-script-programing/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

