1 <?xml version="1.0"?>
   2 <!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V5.0//EN" "http://www.oasis-open.org/docbook/xml/5.0b5/dtd/docbook.dtd" [
   3     <!ENTITY tag_bourneonly   '<inlinemediaobject><imageobject><imagedata fileref="images/tag_bourne.png"></imagedata></imageobject><textobject><phrase>[Bourne]</phrase></textobject></inlinemediaobject> '>
   4     <!ENTITY tag_kshonly      '<inlinemediaobject><imageobject><imagedata fileref="images/tag_ksh.png"></imagedata></imageobject><textobject><phrase>[ksh]</phrase></textobject></inlinemediaobject> '>
   5     <!ENTITY tag_ksh88only    '<inlinemediaobject><imageobject><imagedata fileref="images/tag_ksh88.png"></imagedata></imageobject><textobject><phrase>[ksh88]</phrase></textobject></inlinemediaobject> '>
   6     <!ENTITY tag_ksh93only    '<inlinemediaobject><imageobject><imagedata fileref="images/tag_ksh93.png"></imagedata></imageobject><textobject><phrase>[ksh93]</phrase></textobject></inlinemediaobject> '>
   7     <!ENTITY tag_performance  '<inlinemediaobject><imageobject><imagedata fileref="images/tag_perf.png"></imagedata></imageobject><textobject><phrase>[perf]</phrase></textobject></inlinemediaobject> '>
   8     <!ENTITY tag_i18n         '<inlinemediaobject><imageobject><imagedata fileref="images/tag_i18n.png"></imagedata></imageobject><textobject><phrase>[i18n]</phrase></textobject></inlinemediaobject> '>
   9     <!ENTITY tag_l10n         '<inlinemediaobject><imageobject><imagedata fileref="images/tag_l10n.png"></imagedata></imageobject><textobject><phrase>[l10n]</phrase></textobject></inlinemediaobject> '>
  10 ]>
  11 <!--
  12 
  13  CDDL HEADER START
  14 
  15  The contents of this file are subject to the terms of the
  16  Common Development and Distribution License (the "License").
  17  You may not use this file except in compliance with the License.
  18 
  19  You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  20  or http://www.opensolaris.org/os/licensing.
  21  See the License for the specific language governing permissions
  22  and limitations under the License.
  23 
  24  When distributing Covered Code, include this CDDL HEADER in each
  25  file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  26  If applicable, add the following below this CDDL HEADER, with the
  27  fields enclosed by brackets "[]" replaced with your own identifying
  28  information: Portions Copyright [yyyy] [name of copyright owner]
  29 
  30  CDDL HEADER END
  31 
  32 -->
  33 
  34 <!--
  35 
  36  Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  37  Use is subject to license terms.
  38 
  39 -->
  40 
  41 <!-- tag images were created like this:
  42 $ (text="perf" ;
  43    pbmtext -nomargins -lspace 0 -builtin fixed "${text}" |
  44        pbmtopgm 1 1 |
  45        pgmtoppm 1.0,1.0,1.0-0,0,0 /dev/stdin |
  46        ppmtogif |
  47        giftopnm |
  48        pnmtopng >"tag_${text}.png")
  49 -->
  50 
  51 <!-- compile with:
  52 xsltproc &minus;&minus;stringparam generate.section.toc.level 0 \
  53          &minus;&minus;stringparam toc.max.depth 3 \
  54          &minus;&minus;stringparam toc.section.depth 12 \
  55          &minus;&minus;xinclude -o opensolaris_shell_styleguide.html /usr/share/sgml/docbook/docbook-xsl-stylesheets-1.69.1/html/docbook.xsl opensolaris_shell_styleguide.docbook
  56 -->
  57 
  58 <article
  59     xmlns:xlink="http://www.w3.org/1999/xlink"
  60     xmlns="http://docbook.org/ns/docbook"
  61     xml:lang="en">
  62     <!-- xmlns:xi="http://www.w3.org/2001/XInclude" -->
  63 
  64   <info>
  65     <title><emphasis>[DRAFT]</emphasis> Bourne/Korn Shell Coding Conventions</title>
  66 
  67     <!-- subtitle abuse -->
  68     <subtitle>
  69       This page is currently work-in-progress until it is approved by the OS/Net community. Please send any comments to
  70       <email>shell-discuss@opensolaris.org</email>.
  71     </subtitle>
  72 
  73 
  74     <authorgroup>
  75 <!--
  76         <author><personname>David G. Korn</personname><email>dgk@research.att.com</email></author>
  77         <author><personname>Roland Mainz</personname><email>roland.mainz@nrubsig.org</email></author>
  78         <author><personname>Mike Shapiro</personname><email>mike.shapiro@sun.com</email></author>
  79 -->
  80         <author><orgname>OpenSolaris.org</orgname></author>
  81     </authorgroup>
  82   </info>
  83 
  84 <section xml:id="intro">
  85   <title>Intro</title>
  86   <para>This document describes the shell coding style used for all the SMF script changes integrated into (Open)Solaris.</para>
  87   <para>All new SMF shell code should conform to this coding standard, which is intended to match our existing C coding standard.</para>
  88   <para>When in doubt, think "what would be the C-Style equivalent ?" and "What does the POSIX (shell) standard say ?"</para>   
  89 </section><!-- end of intro -->
  90 
  91 
  92 <section xml:id="rules">
  93   <title>Rules</title>
  94   
  95   
  96   
  97   <section xml:id="general">
  98   <title>General</title>
  99 
 100       <section xml:id="basic_format">
 101           <title>Basic Format</title>
 102           <para>Similar to <literal>cstyle</literal>, the basic format is that all
 103           lines are indented by TABs or eight spaces, and continuation lines (which
 104           in the shell end with "\") are indented by an equivalent number of TABs
 105           and then an additional four spaces, e.g.
 106 <programlisting>
 107 cp foo bar
 108 cp some_realllllllllllllllly_realllllllllllllly_long_path \
 109    to_another_really_long_path
 110 </programlisting>
 111           </para>
 112           <para>The encoding used for the shell scripts is either <literal>ASCII</literal>
 113           or <literal>UTF-8</literal>, alternative encodings are only allowed when the
 114           application requires this.</para>
 115       </section>
 116 
 117 
 118       <section xml:id="commenting">
 119           <title>Commenting</title>
 120           <para>Shell comments are preceded by the '<literal>#</literal>' character. Place
 121           single-line comments in the right-hand margin. Use an extra '<literal>#</literal>'
 122           above and below the comment in the case of multi-line comments:
 123 <programlisting>
 124 cp foo bar              # Copy foo to bar
 125 
 126 #
 127 # Modify the permissions on bar.  We need to set them to root/sys
 128 # in order to match the package prototype.
 129 #
 130 chown root bar
 131 chgrp sys bar
 132 </programlisting>
 133           </para>
 134       </section>
 135 
 136 
 137       <section xml:id="interpreter_magic">
 138           <title>Interpreter magic</title>
 139           <para>The proper interpreter magic for your shell script should be one of these:
 140 <programlisting>
 141 #!/bin/sh        Standard Bourne shell script
 142 #!/bin/ksh -p    Standard Korn shell 88 script.  You should always write ksh
 143                  scripts with -p so that ${ENV} (if set by the user) is not
 144                  sourced into your script by the shell.
 145 #!/bin/ksh93     Standard Korn shell 93 script (-p is not needed since ${ENV} is
 146                  only used for interactive shell sessions).
 147 </programlisting>
 148           </para>
 149       </section>
 150 
 151 
 152       <section xml:id="harden_your_script_against_unexpected_input">
 153           <title>Harden the script against unexpected (user) input</title>
 154           <para>Harden your script against unexpected (user) input, including
 155           command line options, filenames with blanks (or other special
 156           characters) in the name, or file input</para>
 157       </section>
 158 
 159 
 160       <section xml:id="use_builtin_commands">
 161           <title>&tag_kshonly;&tag_performance;Use builtin commands if the shell provides them</title>
 162           <para>
 163           Use builtin commands if the shell provides them. For example ksh93s+
 164           (ksh93, version 's+') delivered with Solaris (as defined by PSARC 2006/550)
 165           supports the following builtins:
 166           <simplelist type="inline">
 167           <member>basename</member>
 168           <member>cat</member>
 169           <member>chgrp</member>
 170           <member>chmod</member>
 171           <member>chown</member>
 172           <member>cmp</member>
 173           <member>comm</member>
 174           <member>cp</member>
 175           <member>cut</member>
 176           <member>date</member>
 177           <member>dirname</member>
 178           <member>expr</member>
 179           <member>fds</member>
 180           <member>fmt</member>
 181           <member>fold</member>
 182           <member>getconf</member>
 183           <member>head</member>
 184           <member>id</member>
 185           <member>join</member>
 186           <member>ln</member>
 187           <member>logname</member>
 188           <member>mkdir</member>
 189           <member>mkfifo</member>
 190           <member>mv</member>
 191           <member>paste</member>
 192           <member>pathchk</member>
 193           <member>rev</member>
 194           <member>rm</member>
 195           <member>rmdir</member>
 196           <member>stty</member>
 197           <member>tail</member>
 198           <member>tee</member>
 199           <member>tty</member>
 200           <member>uname</member>
 201           <member>uniq</member>
 202           <member>wc</member>
 203           <member>sync</member>
 204           </simplelist>
 205           Those builtins can be enabled via <literal>$ builtin name_of_builtin #</literal> in shell
 206           scripts (note that ksh93 builtins implement exact POSIX behaviour - some
 207           commands in Solaris <filename>/usr/bin/</filename> directory implement pre-POSIX behaviour.
 208           Add <literal>/usr/xpg6/bin/:/usr/xpg4/bin</literal> before
 209           <filename>/usr/bin/</filename> in <envar>${PATH}</envar> to test whether your script works with
 210           the XPG6/POSIX versions)
 211           </para>
 212       </section>
 213 
 214 
 215       <section xml:id="use_blocks_not_subshells">
 216           <title>&tag_performance;Use blocks and not subshells if possible</title>
 217           <para>Use blocks and not subshells if possible, e.g. use
 218           <literal>$ { print "foo" ; print "bar" ; }</literal> instead of
 219           <literal>$ (print "foo" ; print "bar") #</literal> - blocks are
 220           faster since they do not require to save the subshell context (ksh93) or
 221           trigger a shell child process (Bourne shell, bash, ksh88 etc.)
 222           </para>
 223       </section>
 224 
 225 
 226       <section xml:id="use_long_options_for_set_builtin">
 227            <title>&tag_kshonly; use long options for "<literal>set</literal>"</title>
 228            <para>use long options for "<literal>set</literal>", for example instead of <literal>$ set -x #</literal>
 229            use <literal>$ set -o xtrace #</literal> to make the code more readable.</para>
 230       </section>
 231 
 232 
 233       <section xml:id="use_posix_command_substitutions_syntax">
 234           <title>&tag_kshonly; Use <literal>$(...)</literal> instead of <literal>`...`</literal> command substitutions</title>
 235           <para>Use <literal>$(...)</literal> instead of <literal>`...`</literal> - <literal>`...`</literal>
 236           is an obsolete construct in ksh+POSIX sh scripts and <literal>$(...)</literal>.is a cleaner design,
 237           requires no escaping rules, allows easy nesting etc.</para>
 238 
 239           <note><title>&tag_ksh93only; <literal>${ ...;}</literal>-style command substitutions</title>
 240           <para>ksh93 has support for an alternative version of command substitutions with the
 241           syntax <literal>${ ...;}</literal> which do not run in a subshell.
 242           </para></note>
 243       </section>
 244 
 245 
 246       <section xml:id="put_command_substitution_result_in_quotes">
 247           <title>&tag_kshonly; Always put the result of a <literal>$(...)</literal> or
 248           <literal>$( ...;)</literal> command substitution in quotes</title>
 249           <para>Always put the result of <literal>$( ... )</literal> or <literal>$( ...;)</literal> in
 250           quotes (e.g. <literal>foo="$( ... )"</literal> or <literal>foo="$( ...;)"</literal>) unless
 251           there is a very good reason for not doing it</para>
 252       </section>
 253 
 254 
 255       <section xml:id="always_set_path">
 256           <title>Scripts should always set their <envar>PATH</envar></title>
 257           <para>Scripts should always set their <envar>PATH</envar> to make sure they do not use
 258           alternative commands by accident (unless the value of <envar>PATH</envar> is well-known
 259           and guaranteed to be set by the caller)</para>
 260       </section>
 261 
 262 
 263       <section xml:id="make_sure_commands_are_available">
 264           <title>Make sure that commands from other packages/applications are really installed on the machine</title>
 265           <para>Scripts should make sure that commands in optional packages are really
 266           there, e.g. add a "precheck" block in scipts to avoid later failure when
 267           doing the main job</para>
 268       </section>
 269 
 270 
 271       <section xml:id="check_usage_of_boolean_variables">
 272           <title>Check how boolean values are used/implemented in your application</title>
 273           <para>Check how boolean values are used in your application.</para>
 274           <para>For example:
 275 <programlisting>
 276 mybool=0
 277 # do something
 278 if [ $mybool -eq 1 ] ; then do_something_1 ; fi
 279 </programlisting>
 280 could be rewritten like this:
 281 <programlisting>
 282 mybool=false # (valid values are "true" or "false", pointing
 283 # to the builtin equivalents of /bin/true or /bin/false)
 284 # do something
 285 if ${mybool} ; then do_something_1 ; fi
 286 </programlisting>
 287 or
 288 <programlisting>
 289 integer mybool=0 # values are 0 or 1
 290 # do something
 291 if (( mybool==1 )) ; then do_something_1 ; fi
 292 </programlisting>
 293           </para>
 294       </section>
 295 
 296       <section xml:id="shell_uses_characters_not_bytes">
 297           <title>&tag_i18n;The shell always operates on <emphasis>characters</emphasis> not bytes</title>
 298           <para>Shell scripts operate on characters and <emphasis>not</emphasis> bytes.
 299           Some locales use multiple bytes (called "multibyte locales") to represent one character</para>
 300 
 301           <note><para>ksh93 has support for binary variables which explicitly
 302           operate on bytes, not characters. This is the <emphasis>only</emphasis> allowed
 303           exception.</para></note>
 304       </section>
 305 
 306 
 307       <section xml:id="multibyte_locale_input">
 308           <title>&tag_i18n;Multibyte locales and input</title>
 309           <para>Think about whether your application has to handle file names or
 310           variables in multibyte locales and make sure all commands used in your
 311           script can handle such characters (e.g. lots of commands in Solaris's
 312           <filename>/usr/bin/</filename> are <emphasis>not</emphasis> able to handle such values - either use ksh93
 313           builtin constructs (which are guaranteed to be multibyte-aware) or
 314           commands from <filename>/usr/xpg4/bin/</filename> and/or <filename>/usr/xpg6/bin</filename>)
 315           </para>
 316       </section>
 317 
 318 
 319       <section xml:id="use_external_filters_only_for_large_datasets">
 320           <title>&tag_performance;Only use external filters like <literal>grep</literal>/<literal>sed</literal>/<literal>awk</literal>/etc.
 321           if you want to process lots of data with them</title>
 322           <para>Only use external filters like <literal>grep</literal>/<literal>sed</literal>/<literal>awk</literal>/etc.
 323           if a significant amount of data is processed by the filter or if
 324           benchmarking shows that the use of builtin commands is significantly slower
 325           (otherwise the time and resources needed to start the filter are
 326           far greater then the amount of data being processed,
 327           creating a performance problem).</para>
 328           <para>For example:
 329 <programlisting>
 330 if [ "$(echo "$x" | egrep '.*foo.*')" != "" ] ; then
 331     do_something ;
 332 done
 333 </programlisting>
 334 can be re-written using ksh93 builtin constructs, saving several
 335 <literal>|fork()|+|exec()|</literal>'s:
 336 <programlisting>
 337 if [[ "${x}" == ~(E).*foo.* ]] ; then
 338     do_something ;
 339 done
 340 </programlisting>
 341           </para>
 342       </section>
 343 
 344 
 345       <section xml:id="use_dashdash_if_first_arg_is_variable">
 346           <title>If the first operand of a command is a variable, use <literal>--</literal></title>
 347           <para>If the first operand of a command is a variable, use <literal>--</literal>
 348           for any command that accepts this as end of argument to
 349           avoid problems if the variable expands to a value starting with <literal>-</literal>.
 350           </para>
 351           <note><para>
 352           At least
 353           <simplelist type="inline">
 354               <member>print</member>
 355               <member>/usr/bin/fgrep</member><member>/usr/xpg4/bin/fgrep</member>
 356               <member>/usr/bin/grep</member> <member>/usr/xpg4/bin/grep</member>
 357               <member>/usr/bin/egrep</member><member>/usr/xpg4/bin/egrep</member>
 358           </simplelist>
 359           support <literal>--</literal> as "end of arguments"-terminator.
 360           </para></note>
 361       </section>
 362 
 363       <section xml:id="use_export">
 364           <title>&tag_kshonly;&tag_performance;Use <literal>$ export FOOBAR=val #</literal> instead of
 365           <literal>$ FOOBAR=val ; export FOOBAR #</literal></title>
 366           <para>Use <literal>$ export FOOBAR=val # instead of $ FOOBAR=val ; export FOOBAR #</literal> -
 367           this is much faster.</para>
 368       </section>
 369 
 370 
 371       <section xml:id="use_subshell_around_set_dashdash_usage">
 372           <title>Use a subshell (e.g. <literal>$ ( mycmd ) #</literal>) around places which use
 373               <literal>set -- $(mycmd)</literal> and/or <literal>shift</literal></title>
 374           <para>Use a subshell (e.g. <literal>$ ( mycmd ) #</literal>) around places which use
 375           <literal>set -- $(mycmd)</literal> and/or <literal>shift</literal> unless the variable
 376           affected is either a local one or if it's guaranteed that this variable will no longer be used
 377           (be careful for loadable functions, e.g. ksh/ksh93's <literal>autoload</literal> !!!!)
 378           </para>
 379       </section>
 380 
 381 
 382       <section xml:id="be_careful_with_tabs_in_script_code">
 383           <title>Be careful with using TABS in script code, they are not portable
 384           between editors or platforms</title>
 385           <para>Be careful with using TABS in script code, they are not portable
 386           between editors or platforms.</para>
 387           <para>If you use ksh93 use <literal>$'\t'</literal> to include TABs in sources, not the TAB character itself.</para>
 388       </section>
 389 
 390 
 391       <section xml:id="centralise_error_exit">
 392            <title>If you have multiple points where your application exits with an error
 393            message create a central function for this purpose</title>
 394            <para>If you have multiple points where your application exits with an error
 395            message create a central function for this, e.g.
 396 <programlisting>
 397 if [ -z "$tmpdir" ] ; then
 398         print -u2 "mktemp failed to produce output; aborting."
 399         exit 1
 400 fi
 401 if [ ! -d $tmpdir ] ; then
 402         print -u2 "mktemp failed to create a directory; aborting."
 403         exit 1
 404 fi
 405 </programlisting>
 406 should be replaced with
 407 <programlisting>
 408 function fatal_error
 409 {
 410     print -u2 "${progname}: $*"
 411     exit 1
 412 }
 413 # do something (and save ARGV[0] to variable "progname")
 414 if [ -z "$tmpdir" ] ; then
 415         fatal_error "mktemp failed to produce output; aborting."
 416 fi
 417 if [ ! -d "$tmpdir" ] ; then
 418         fatal_error "mktemp failed to create a directory; aborting."
 419 fi
 420 </programlisting>
 421           </para>
 422       </section>
 423 
 424 
 425       <section xml:id="use_set_o_nounset">
 426           <title>&tag_kshonly; Think about using <literal>$ set -o nounset #</literal> by default</title>
 427           <para>Think about using <literal>$ set -o nounset #</literal> by default (or at least during the
 428     script's development phase) to catch errors where variables are used
 429     when they are not set (yet), e.g.
 430 <screen>
 431 $ <userinput>(set -o nounset ; print ${foonotset})</userinput>
 432 <computeroutput>/bin/ksh93: foonotset: parameter not set</computeroutput>
 433 </screen>
 434            </para>
 435       </section>
 436 
 437 
 438       <section xml:id="avoid_eval_builtin">
 439           <title>Avoid using <literal>eval</literal> unless absolutely necessary</title>
 440           <para>Avoid using <literal>eval</literal> unless absolutely necessary.  Subtle things
 441           can happen when a string is passed back through the shell
 442           parser.  You can use name references to avoid uses such as
 443           <literal>eval $name="$value"</literal>.
 444           </para>
 445       </section>
 446 
 447 
 448       <section xml:id="use_concatenation_operator">
 449           <title>&tag_ksh93only;Use the string/array concatenation operator <literal>+=</literal></title>
 450           <para>Use <literal>+=</literal> instead of manually adding strings/array elements, e.g.
 451 <programlisting>
 452 foo=""
 453 foo="${foo}a"
 454 foo="${foo}b"
 455 foo="${foo}c"
 456 </programlisting>
 457 should be replaced with
 458 <programlisting>
 459 foo=""
 460 foo+="a"
 461 foo+="b"
 462 foo+="c"
 463 </programlisting>
 464           </para>
 465       </section>
 466  
 467       <section xml:id="use_source_not_dot">
 468           <title>&tag_ksh93only;Use <literal>source</literal> instead of '<literal>.</literal> '(dot)
 469           to include other shell script fragments</title>
 470           <para>Use <literal>source</literal> instead of '<literal>.</literal>'
 471           (dot) to include other shell script fragments - the new form is much
 472           more readable than the tiny dot and a failure can be caught within the script.</para>
 473       </section>
 474 
 475 
 476       <section xml:id="use_builtin_localisation_support">
 477           <title>&tag_ksh93only;&tag_performance;&tag_l10n;Use <literal>$"..."</literal> instead of
 478           <literal>gettext ... "..."</literal> for strings that need to be localized for different locales</title>
 479           <para>Use $"..." instead of <literal>gettext ... "..."</literal> for strings that need to be
 480           localized for different locales. <literal>gettext</literal> will require a
 481           <literal>fork()+exec()</literal> and
 482           reads the whole catalog each time it's called, creating a huge overhead for localisation
 483           (and the  <literal>$"..."</literal> is easier to use, e.g. you only have to put a
 484           <literal>$</literal> in front of the catalog and the string will be localised).
 485           </para>
 486       </section>
 487 
 488 
 489       <section xml:id="use_set_o_noglob">
 490           <title>&tag_kshonly;&tag_performance;Use <literal>set -o noglob</literal> if you do not need to expand files</title>
 491           <para>If you don't expect to expand files, you can do set <literal>-f</literal>
 492           (<literal>set -o noglob</literal>) as well.  This way the need to use <literal>""</literal> is
 493           greatly reduced.</para>
 494       </section>
 495 
 496 
 497       <section xml:id="use_empty_ifs_to_handle_spaces">
 498           <title>&tag_ksh93only;Use <literal>IFS=</literal> to avoid problems with spaces in filenames</title>
 499           <para>Unless you want to do word splitting, put <literal>IFS=</literal>
 500           at the beginning of a command.  This way spaces in
 501           file names won't be a problem.  You can do
 502           <literal>IFS='delims' read -r</literal> line
 503           to override <envar>IFS</envar> just for the <literal>read</literal> command.  However,
 504           you can't do this for the <literal>set</literal> builtin.</para>
 505       </section>
 506 
 507 
 508       <section xml:id="set_locale_when_comparing_against_localised_output">
 509           <title>Set the message locale if you process output of tools which may be localised</title>
 510           <para>Set the message locale (<envar>LC_MESSAGES</envar>) if you process output of tools which may be localised</para>
 511           <example><title>Set <envar>LC_MESSAGES</envar> when testing for specific outout of the <filename>/usr/bin/file</filename> utility:</title>
 512 <programlisting>
 513 # set french as default message locale
 514 export LC_MESSAGES=fr_FR.UTF-8
 515 
 516 ...
 517 
 518 # test whether the file "/tmp" has the filetype "directory" or not
 519 # we set LC_MESSAGES to "C" to ensure the returned message is in english
 520 if [[ "$(LC_MESSAGES=C file /tmp)" = *directory ]] ; then
 521     print "is a directory"
 522 fi
 523 </programlisting>
 524           <note><para>The environment variable <envar>LC_ALL</envar> always
 525           overrides any other <envar>LC_*</envar> environment variables
 526           (and <envar>LANG</envar>, too),
 527           including <envar>LC_MESSAGES</envar>.
 528           if there is the chance that <envar>LC_ALL</envar> may be set
 529           replace <envar>LC_MESSAGES</envar> with <envar>LC_ALL</envar>
 530           in the example above.</para></note>
 531           </example>
 532       </section>
 533       
 534       <section xml:id="cleanup_after_yourself">
 535           <title>Cleanup after yourself.</title>
 536           <para>Cleanup after yourself. For example ksh/ksh93 have an <literal>EXIT</literal> trap which
 537           is very useful for this.
 538           </para>
 539           <note><para>
 540           Note that the <literal>EXIT</literal> trap is executed for a subshell and each subshell
 541           level can run it's own <literal>EXIT</literal> trap, for example
 542 <screen>
 543 $ <userinput>(trap "print bam" EXIT ; (trap "print snap" EXIT ; print "foo"))</userinput>
 544 <computeroutput>foo
 545 snap
 546 bam</computeroutput>
 547 </screen>
 548           </para></note>
 549       </section>
 550 
 551       <section xml:id="use_proper_exit_code">
 552           <title>Use a proper <literal>exit</literal> code</title>
 553           <para>Explicitly set the exit code of a script, otherwise the exit code
 554           from the last command executed will be used which may trigger problems
 555           if the value is unexpected.</para>
 556       </section>
 557 
 558 
 559       <section xml:id="shell_lint">
 560           <title>&tag_ksh93only;Use <literal>shcomp -n scriptname.sh /dev/null</literal> to check for common errors</title>
 561           <para>Use <literal>shcomp -n scriptname.sh /dev/null</literal> to
 562           check for common problems (such as insecure, depreciated or ambiguous constructs) in shell scripts.</para>
 563       </section>
 564   </section><!-- end of general -->
 565 
 566 
 567 
 568 
 569 
 570   <section xml:id="functions">
 571       <title>Functions</title>
 572 
 573       <section xml:id="use_functions">
 574           <title>Use functions to break up your code</title>
 575           <para>Use functions to break up your code into smaller, logical blocks.</para>
 576       </section>
 577 
 578       <section xml:id="do_not_reserved_keywords_for_function_names">
 579           <title>Do not use function names which are reserved keywords in C/C++/JAVA or the POSIX shell standard</title>
 580           <para>Do not use function names which are reserved keywords (or function names) in C/C++/JAVA or the POSIX shell standard
 581           (to avoid confusion and/or future changes/updates to the shell language).
 582           </para>
 583       </section> 
 584       
 585       <section xml:id="use_ksh_style_function_syntax">
 586           <title>&tag_kshonly;&tag_performance;Use ksh-style <literal>function</literal></title>
 587           <para>It is <emphasis>highly</emphasis> recommended to use ksh style functions
 588           (<literal>function foo { ... }</literal>) instead
 589           of Bourne-style functions (<literal>foo() { ... }</literal>) if possible
 590           (and local variables instead of spamming the global namespace).</para>
 591 
 592           <warning><para>
 593           The difference between old-style Bourne functions and ksh functions is one of the major differences
 594           between ksh88 and ksh93 - ksh88 allowed variables to be local for Bourne-style functions while ksh93
 595           conforms to the POSIX standard and will use a function-local scope for variables declared in
 596           Bourne-style functions.</para>
 597           <para>Example (note that "<literal>integer</literal>" is an alias for "<literal>typeset -li</literal>"):
 598 <programlisting>
 599 # new style function with local variable
 600 $ ksh93 -c 'integer x=2 ; function foo { integer x=5 ; } ; print "x=$x"
 601 ; foo ; print "x=$x" ;'
 602 x=2
 603 x=2
 604 # old style function with an attempt to create a local variable
 605 $ ksh93 -c 'integer x=2 ; foo() { integer x=5 ; } ; print "x=$x" ; foo ;
 606 print "x=$x" ;'       
 607 x=2
 608 x=5
 609 </programlisting>
 610 
 611           <uri xlink:href="http://www.opensolaris.org/os/project/ksh93-integration/docs/ksh93r/general/compatibility/">usr/src/lib/libshell/common/COMPATIBILITY</uri>
 612           says about this issue:
 613 <blockquote><para>
 614 Functions, defined with name() with ksh-93 are compatible with
 615 the POSIX standard, not with ksh-88.  No local variables are
 616 permitted, and there is no separate scope.  Functions defined
 617 with the function name syntax, maintain compatibility.
 618 This also affects function traces.
 619 </para></blockquote>
 620 (this issue also affects <filename>/usr/xpg4/bin/sh</filename> in Solaris 10 because it is based on ksh88. This is a bug.).          
 621           </para></warning>
 622 
 623       </section>
 624 
 625 
 626       <section xml:id="use_proper_return_code">
 627           <title>Use a proper <literal>return</literal> code</title>
 628           <para>Explicitly set the return code of a function - otherwise the exit code
 629           from the last command executed will be used which may trigger problems
 630           if the value is unexpected.</para>
 631           <para>The only allowed exception is if a function uses the shell's <literal>errexit</literal> mode to leave
 632           a function, subshell or the script if a command returns a non-zero exit code.
 633           </para>
 634       </section>
 635 
 636       <section xml:id="use_fpath_to_load_common_code">
 637           <title>&tag_kshonly;Use <envar>FPATH</envar> to load common functions, not <literal>source</literal></title>
 638           <para>
 639           Use the ksh <envar>FPATH</envar> (function path) feature to load functions which are shared between scripts
 640           and not <literal>source</literal> - this allows to load such a function on demand and not all at once.</para>
 641       </section>
 642  
 643   </section><!-- end of functions -->
 644 
 645 
 646 
 647 
 648   <section xml:id="if_for_while">
 649       <title><literal>if</literal>, <literal>for</literal> and <literal>while</literal></title>
 650 
 651       <section xml:id="if_for_while_format">
 652           <title>Format</title>
 653           <para>To match <literal>cstyle</literal>, the shell token equivalent to the <literal>C</literal>
 654           "<literal>{</literal>" should appear on the same line, separated by a
 655           "<literal>;</literal>", as in:
 656 <programlisting>
 657 if [ "$x" = "hello" ] ; then
 658     echo $x
 659 fi
 660 
 661 if [[ "$x" = "hello" ]] ; then
 662     print $x
 663 fi
 664 
 665 for i in 1 2 3; do
 666     echo $i
 667 done
 668 
 669 for ((i=0 ; i &lt; 3 ; i++)); do
 670     print $i
 671 done
 672 
 673 while [ $# -gt 0 ]; do
 674     echo $1
 675     shift
 676 done
 677 
 678 while (( $# &gt; 0 )); do
 679   print $1
 680   shift
 681 done
 682 </programlisting>
 683           </para>
 684       </section>
 685 
 686 
 687       <section xml:id="test_builtin">
 688           <title><literal>test</literal> Builtin</title>
 689           <para>DO NOT use the test builtin. Sorry, executive decision.</para>
 690           <para>In our Bourne shell, the <literal>test</literal> built-in is the same as the "["
 691           builtin (if you don't believe me, try "type test" or refer to <filename>usr/src/cmd/sh/msg.c</filename>).</para>
 692           <para>
 693           So please do not write:
 694 <programlisting>
 695 if test $# -gt 0 ; then
 696 </programlisting>         
 697 instead use:
 698 <programlisting>
 699 if [ $# -gt 0 ] ; then
 700 </programlisting>
 701           </para>
 702       </section>
 703 
 704 
 705       <section xml:id="use_ksh_test_syntax">
 706            <title>&tag_kshonly;&tag_performance;Use "<literal>[[ expr ]]</literal>" instead of "<literal>[ expr ]</literal>"</title>
 707            <para>Use "<literal>[[ expr ]]</literal>" instead of "<literal>[ expr ]</literal>" if possible
 708            since it avoids going through the whole pattern expansion/etc. machinery and
 709            adds additional operators not available in the Bourne shell, such as short-circuit
 710            <literal>&amp;&amp;</literal> and <literal>||</literal>.
 711            </para>
 712       </section>
 713 
 714       
 715       <section xml:id="use_posix_arithmetic_expressions">
 716           <title>&tag_kshonly; Use "<literal>(( ... ))</literal>" for arithmetic expressions</title>
 717           <para>Use "<literal>(( ... ))</literal>" instead of "<literal>[ expr ]</literal>"
 718           or "<literal>[[ expr ]]</literal>" expressions.
 719           </para>
 720           <para>
 721           Example: Replace
 722 <programlisting>
 723 i=5
 724 # do something
 725 if [ $i -gt 5 ] ; then
 726 </programlisting>
 727 with
 728 <programlisting>
 729 i=5
 730 # do something
 731 if (( i &gt; 5 )) ; then
 732 </programlisting>
 733           </para>
 734       </section>
 735 
 736 
 737       <section xml:id="compare_exit_code_using_math">
 738           <title>&tag_kshonly;&tag_performance;Compare exit code using arithmetic expressions expressions</title>
 739           <para>Use POSIX arithmetic expressions to test for exit/return codes of commands and functions.
 740           For example turn
 741 <programlisting>
 742 if [ $? -gt 0 ] ; then
 743 </programlisting>
 744 into
 745 <programlisting>
 746 if (( $? &gt; 0 )) ; then
 747 </programlisting>
 748          </para>
 749       </section>
 750       
 751 
 752       <section xml:id="use_builtin_commands_in_loops">
 753          <title>&tag_bourneonly; Use builtin commands in conditions for <literal>while</literal> endless loops</title>
 754          <para>Make sure that your shell has a "<literal>true</literal>" builtin (like ksh93) when
 755          executing endless loops like <literal>$ while true ; do do_something ; done #</literal> -
 756          otherwise each loop cycle runs a <literal>|fork()|+|exec()|</literal>-cycle to run
 757          <filename>/bin/true</filename>
 758          </para>
 759       </section>
 760 
 761 
 762       <section xml:id="single_line_if_statements">
 763          <title>Single-line if-statements</title>
 764          <para>It is permissible to use <literal>&amp;&amp;</literal> and <literal>||</literal> to construct
 765          shorthand for an "<literal>if</literal>" statement in the case where the if statement has a
 766          single consequent line:
 767 <programlisting>
 768 [ $# -eq 0 ] &amp;&amp; exit 0
 769 </programlisting>
 770 instead of the longer:
 771 <programlisting>
 772 if [ $# -eq 0 ]; then
 773   exit 0
 774 fi
 775 </programlisting>
 776          </para>
 777       </section>
 778 
 779 
 780       <section xml:id="exit_status_and_if_for_while">
 781          <title>Exit Status and <literal>if</literal>/<literal>while</literal> statements</title>
 782          <para>Recall that "<literal>if</literal>" and "<literal>while</literal>"
 783          operate on the exit status of the statement
 784          to be executed. In the shell, zero (0) means true and non-zero means false.
 785          The exit status of the last command which was executed is available in the $?
 786          variable. When using "<literal>if</literal>" and "<literal>while</literal>",
 787          it is typically not necessary to use
 788          <literal>$?</literal> explicitly, as in:
 789 <programlisting>
 790 grep foo /etc/passwd &gt;/dev/null 2>&amp;1
 791 if [ $? -eq 0 ]; then
 792   echo "found"
 793 fi
 794 </programlisting>
 795 Instead, you can more concisely write:
 796 <programlisting>
 797 if grep foo /etc/passwd &gt;/dev/null 2>&amp;1; then
 798   echo "found"
 799 fi
 800 </programlisting>
 801 Or, when appropriate:
 802 <programlisting>
 803 grep foo /etc/passwd &gt;/dev/null 2>&amp;1 &amp;&amp; echo "found"
 804 </programlisting>
 805          </para>
 806       </section>
 807 
 808   </section><!-- end of if/for/while -->
 809 
 810 
 811 
 812 
 813 
 814 
 815   <section xml:id="variables">
 816   <title>Variable types, naming and usage</title>
 817 
 818       <section xml:id="names_should_be_lowercase">
 819           <title>Names of local, non-environment, non-constant variables should be lowercase</title>
 820           <para>Names of variables local to the current script which are not exported to the environment
 821           should be lowercase while variable names which are exported to the
 822           environment should be uppercase.</para>
 823           <para>The only exception are global constants (=global readonly variables,
 824           e.g. <literal>$ float -r M_PI=3.14159265358979323846 #</literal> (taken from &lt;math.h&gt;))
 825           which may be allowed to use uppercase names, too.
 826           </para>
 827 
 828           <warning><para>
 829               Uppercase variable names should be avoided because there is a good chance
 830               of naming collisions with either special variable names used by the shell
 831               (e.g.  <literal>PWD</literal>, <literal>SECONDS</literal> etc.).
 832           </para></warning>
 833       </section>
 834 
 835       <section xml:id="do_not_reserved_keywords_for_variable_names">
 836           <title>Do not use variable names which are reserved keywords/variable names in C/C++/JAVA or the POSIX shell standard</title>
 837           <para>Do not use variable names which are reserved keywords in C/C++/JAVA or the POSIX shell standard
 838           (to avoid confusion and/or future changes/updates to the shell language).
 839           </para>
 840           <note>
 841             <para>The Korn Shell and the POSIX shell standard have many more
 842             reserved variable names than the original Bourne shell. All
 843             these reserved variable names are spelled uppercase.           
 844             </para>
 845           </note>
 846       </section> 
 847            
 848       <section xml:id="use_brackets_around_long_names">
 849           <title>Always use <literal>'{'</literal>+<literal>'}'</literal> when using variable
 850           names longer than one character</title>
 851           <para>Always use <literal>'{'</literal>+<literal>'}'</literal> when using
 852           variable names longer than one character unless a simple variable name is
 853           followed by a blank, <literal>/</literal>, <literal>;</literal>, or <literal>$</literal>
 854           character (to avoid problems with array,
 855           compound variables or accidental misinterpretation by users/shell)
 856 <programlisting>
 857 print "$foo=info"
 858 </programlisting>
 859 should be rewritten to
 860 <programlisting>
 861 print "${foo}=info"
 862 </programlisting>
 863           </para>
 864       </section>
 865 
 866 
 867       <section xml:id="quote_variables_containing_filenames_or_userinput">
 868           <title><emphasis>Always</emphasis> put variables into quotes when handling filenames or user input</title>
 869           <para><emphasis>Always</emphasis> put variables into quotes when handling filenames or user input, even if
 870           the values are hardcoded or the values appear to be fixed. Otherwise at
 871           least two things may go wrong:
 872           <itemizedlist>
 873           <listitem><para>a malicious user may be able to exploit a script's inner working to
 874           infect his/her own code</para></listitem>
 875           <listitem><para>a script may (fatally) misbehave for unexpected input (e.g. file names
 876           with blanks and/or special symbols which are interpreted by the shell)</para></listitem>
 877           </itemizedlist>
 878           </para>
 879 
 880           <note><para>
 881           As alternative a script may set <literal>IFS='' ; set -o noglob</literal> to turn off the
 882           interpretation of any field seperators and the pattern globbing.
 883           </para></note>
 884       </section>
 885 
 886    
 887    
 888       <section xml:id="use_typed_variables">
 889           <title>&tag_kshonly;&tag_performance;Use typed variables if possible.</title>
 890           <para>For example the following is very
 891           inefficient since it transforms the integer values to strings and back
 892           several times:
 893 <programlisting>
 894 a=0
 895 b=1
 896 c=2
 897 # more code
 898 if [ $a -lt 5 -o $b -gt c ] ; then do_something ; fi
 899 </programlisting>
 900 This could be rewritten using ksh constructs:
 901 <programlisting>
 902 integer a=0
 903 integer b=1
 904 integer c=2
 905 # more code
 906 if (( a &lt; 5 || b &gt; c )) ; then do_something ; fi
 907 </programlisting>
 908           </para>
 909       </section>
 910 
 911 
 912       <section xml:id="store_lists_in_arrays">
 913           <title>&tag_ksh93only; Store lists in arrays or associative arrays</title>
 914           <para>Store lists in arrays or associative arrays - this is usually easier
 915           to manage.</para>
 916           <para>
 917     For example:
 918 <programlisting>
 919 x="
 920 /etc/foo
 921 /etc/bar
 922 /etc/baz
 923 "
 924 echo $x
 925 </programlisting>
 926 can be replaced with
 927 <programlisting>
 928 typeset -a mylist
 929 mylist[0]="/etc/foo"
 930 mylist[1]="/etc/bar"
 931 mylist[2]="/etc/baz"
 932 print "${mylist[@]}"
 933 </programlisting>
 934 or (ksh93-style append entries to a normal (non-associative) array)
 935 <programlisting>
 936 typeset -a mylist
 937 mylist+=( "/etc/foo" )
 938 mylist+=( "/etc/bar" )
 939 mylist+=( "/etc/baz" )
 940 print "${mylist[@]}"
 941 </programlisting>
 942           </para>
 943           <note>
 944               <title>Difference between expanding arrays with mylist[@] and mylist[*] subscript operators</title>
 945               <para>
 946               Arrays may be expanded using two similar subscript operators, @ and *. These subscripts
 947               differ only when the variable expansion appears within double quotes. If the variable expansion
 948               is between double-quotes, "${mylist[*]}" expands to a single string with the value of each array
 949               member separated by the first character of the <envar>IFS</envar> variable, and "${mylist[@]}"
 950               expands each element of name to a separate string.
 951               </para>
 952               <example><title>Difference between [@] and [*] when expanding arrays</title>
 953 <programlisting>
 954 typeset -a mylist
 955 mylist+=( "/etc/foo" )
 956 mylist+=( "/etc/bar" )
 957 mylist+=( "/etc/baz" )
 958 IFS=","
 959 printf "mylist[*]={ 0=|%s| 1=|%s| 2=|%s| 3=|%s| }\n" "${mylist[*]}"
 960 printf "mylist[@]={ 0=|%s| 1=|%s| 2=|%s| 3=|%s| }\n" "${mylist[@]}"
 961 </programlisting>
 962 <para>will print:</para>
 963 <screen>
 964 <computeroutput>mylist[*]={ 0=|/etc/foo,/etc/bar,/etc/baz| 1=|| 2=|| 3=|| }
 965 mylist[@]={ 0=|/etc/foo| 1=|/etc/bar| 2=|/etc/baz| 3=|| }
 966 </computeroutput>
 967 </screen>
 968               </example>
 969           </note>
 970       </section>
 971 
 972 
 973       <section xml:id="use_compound_variables_or_lists_for_grouping">
 974           <title>&tag_ksh93only; Use compound variables or associative arrays to group similar variables together</title>
 975           <para>Use compound variables or associative arrays to group similar variables together.</para>
 976           <para>
 977     For example:
 978 <programlisting>
 979 box_width=56
 980 box_height=10
 981 box_depth=19
 982 echo "${box_width} ${box_height} ${box_depth}"
 983 </programlisting>
 984 could be rewritten to ("associative array"-style)
 985 <programlisting>
 986 typeset -A -E box=( [width]=56 [height]=10 [depth]=19 )
 987 print -- "${box[width]} ${box[height]} ${box[depth]}"
 988 </programlisting>
 989 or ("compound variable"-style
 990 <programlisting>
 991 box=(
 992     float width=56
 993     float height=10
 994     float depth=19
 995     )
 996 print -- "${box.width} ${box.height} ${box.depth}"
 997 </programlisting>
 998           </para>
 999       </section>    
1000   </section><!-- end of variables -->
1001 
1002 
1003 
1004 
1005 
1006 
1007 
1008   <section xml:id="io">
1009   <title>I/O</title>
1010 
1011       <section xml:id="avoid_echo">
1012           <title>Avoid using the "<literal>echo</literal>" command for output</title>
1013           <para>The behaviour of "<literal>echo</literal>" is not portable
1014           (e.g. System V, BSD, UCB and ksh93/bash shell builtin versions all
1015           slightly differ in functionality) and should be avoided if possible.
1016           POSIX defines the "<literal>printf</literal>" command as replacement
1017           which provides more flexible and portable behaviour.</para>
1018 
1019           <note>
1020               <title>&tag_kshonly;Use "<literal>print</literal>" and not "<literal>echo</literal>" in Korn Shell scripts</title>
1021               <para>Korn shell scripts should prefer the "<literal>print</literal>"
1022               builtin which was introduced as replacement for "<literal>echo</literal>".</para>
1023               <caution>
1024                   <para>Use <literal>$ print -- ${varname}" #</literal> when there is the slightest chance that the
1025                   variable "<literal>varname</literal>" may contain symbols like "-". Or better use "<literal>printf</literal>"
1026                   instead, for example
1027 <programlisting>
1028 integer fx
1029 # do something
1030 print $fx
1031 </programlisting>
1032 may fail if "f" contains a negative value. A better way may be to use
1033 <programlisting>
1034 integer fx
1035 # do something
1036 printf "%d\n" fx
1037 </programlisting>
1038                   </para>
1039               </caution>
1040           </note>
1041       </section>
1042       
1043       <section xml:id="use_redirect_not_exec_to_open_files">
1044           <title>&tag_ksh93only;Use <literal>redirect</literal> and not <literal>exec</literal> to open files</title>
1045           <para>Use <literal>redirect</literal> and not <literal>exec</literal> to open files - <literal>exec</literal>
1046           will terminate the current function or script if an error occurs while <literal>redirect</literal>
1047           just returns a non-zero exit code which can be caught.</para>
1048 <para>Example:
1049 <programlisting>
1050 if redirect 5&lt;/etc/profile ; then
1051     print "file open ok"
1052     head &lt;&amp;5
1053 else
1054     print "could not open file"
1055 fi
1056 </programlisting>
1057            </para>
1058       </section>
1059       
1060       <section xml:id="group_identical_redirections_together">
1061           <title>&tag_performance;Avoid redirections per command when the output goes into the same file,
1062           e.g. <literal>$ echo "foo" &gt;xxx ; echo "bar" &gt;&gt;xxx ; echo "baz" &gt;&gt;xxx #</literal></title>
1063           <para>Each of the redirections above trigger an
1064           <literal>|open()|,|write()|,|close()|</literal>-sequence. It is much
1065           more efficient (and faster) to group the rediction into a block,
1066           e.g. <literal>{ echo "foo" ; echo "bar" ; echo "baz" } &gt;xxx #</literal></para>
1067       </section>
1068 
1069 
1070       <section xml:id="avoid_using_temporary_files">
1071           <title>&tag_performance;Avoid the creation of temporary files and store the values in variables instead</title>
1072           <para>Avoid the creation of temporary files and store the values in variables instead if possible</para>
1073           <para>
1074     Example:
1075 <programlisting>
1076 ls -1 &gt;xxx
1077 for i in $(cat xxx) ; do
1078     do_something ;
1079 done
1080 </programlisting>
1081 can be replaced with
1082 <programlisting>
1083 x="$(ls -1)"
1084 for i in ${x} ; do
1085     do_something ;
1086 done
1087 </programlisting>
1088            </para>
1089            <note><para>ksh93 supports binary variables (e.g. <literal>typeset -b varname</literal>) which can hold any value.</para></note>
1090       </section>  
1091 
1092 
1093       <section xml:id="create_subdirs_for_multiple_temporary_files">
1094           <title>If you create more than one temporary file create an unique subdir</title>
1095           <para>If you create more than one temporary file create an unique subdir for
1096           these files and make sure the dir is writable. Make sure you cleanup
1097           after yourself (unless you are debugging).
1098           </para>
1099       </section>           
1100 
1101 
1102       <section xml:id="use_dynamic_file_descriptors">
1103           <title>&tag_ksh93only;Use {n}&lt;file instead of fixed file descriptor numbers</title>
1104           <para>When opening a file use {n}&lt;file, where <envar>n</envar> is an
1105           integer variable rather than specifying a fixed descriptor number.</para>
1106           <para>This is highly recommended in functions to avoid that fixed file
1107           descriptor numbers interfere with the calling script.</para>
1108 <example><title>Open a network connection and store the file descriptor number in a variable</title>
1109 <programlisting>
1110 function cat_http
1111 {
1112     integer netfd
1113 
1114 ...
1115 
1116     # open TCP channel
1117     redirect {netfd}&lt;&gt;"/dev/tcp/${host}/${port}"
1118 
1119     # send HTTP request    
1120     request="GET /${path} HTTP/1.1\n"
1121     request+="Host: ${host}\n"
1122     request+="User-Agent: demo code/ksh93 (2007-08-30; $(uname -s -r -p))\n"
1123     request+="Connection: close\n"
1124     print "${request}\n" &gt;&amp;${netfd}
1125     
1126     # collect response and send it to stdout
1127     cat &lt;&amp;${netfd}
1128     
1129     # close connection
1130     exec {netfd}&lt;&amp;-
1131 
1132 ...
1133 
1134 }
1135 </programlisting>
1136 </example>
1137       </section>           
1138 
1139 
1140       <section xml:id="use_inline_here_documents">
1141           <title>&tag_ksh93only;&tag_performance;Use inline here documents
1142           instead of <literal>echo "$x" | command</literal></title>
1143           <para>Use inline here documents, for example
1144 <programlisting>
1145 command &lt;&lt;&lt; $x
1146 </programlisting>
1147        rather than
1148 <programlisting>
1149 print -r -- "$x" | command
1150 </programlisting>
1151           </para>
1152       </section>           
1153 
1154 
1155       <section xml:id="use_read_r">
1156           <title>&tag_ksh93only;Use the <literal>-r</literal> option of <literal>read</literal> to read a line</title>
1157           <para>Use the <literal>-r</literal> option of <literal>read</literal> to read a line.
1158           You never know when a line will end in <literal>\</literal> and without a
1159           <literal>-r</literal> multiple
1160           lines can be read.</para>
1161       </section>
1162 
1163 
1164       <section xml:id="print_compound_variables_using_print_C">
1165           <title>&tag_ksh93only;Print compound variables using <literal>print -C varname</literal> or <literal>print -v varname</literal></title>
1166           <para>Print compound variables using <literal>print -C varname</literal> or
1167           <literal>print -v varname</literal> to make sure that non-printable characters
1168           are correctly encoded.</para>
1169 <example><title>Print compound variable with non-printable characters</title>
1170 <programlisting>
1171 compound x=(
1172     a=5
1173     b="hello"
1174     c=(
1175         d=9
1176         e="$(printf "1\v3")" <co xml:id="co.vertical_tab1" />
1177     )
1178 )
1179 print -v x
1180 </programlisting>
1181 <para>will print:</para>
1182 <screen>
1183 <computeroutput>(
1184         a=5
1185         b=hello
1186         c=(
1187                 d=9
1188                 e=$'1\0133' <co xml:id="co.vertical_tab2" />
1189         )
1190 )</computeroutput>
1191 </screen>
1192 <calloutlist>
1193   <callout arearefs="co.vertical_tab1 co.vertical_tab2">
1194     <para>vertical tab, <literal>\v</literal>, octal=<literal>\013</literal>.</para>
1195   </callout>
1196 </calloutlist>
1197 </example>
1198       </section>
1199 
1200       <section xml:id="command_name_before_redirections">
1201           <title>Put the command name and arguments before redirections</title>
1202           <para>Put the command name and arguments before redirections.
1203           You can legally do <literal>$ &gt; file date</literal> instead of <literal>date &gt; file</literal>
1204           but don't do it.</para>
1205       </section>           
1206 
1207       <section xml:id="enable_gmacs_editor_mode_for_user_prompts">
1208           <title>&tag_ksh93only;Enable the <literal>gmacs</literal> editor
1209           mode when reading user input using the <literal>read</literal> builtin</title>
1210           <para>Enable the <literal>gmacs</literal>editor mode before reading user
1211           input using the <literal>read</literal> builtin to enable the use of
1212           cursor+backspace+delete keys in the edit line</para>
1213 <example><title>Prompt user for a string with gmacs editor mode enabled</title>
1214 <programlisting>
1215 set -o gmacs <co xml:id="co.enable_gmacs" />
1216 typeset inputstring="default value"
1217 ...
1218 read -v<co xml:id="co.read_v" /> inputstring<co xml:id="co.readvar" />?"Please enter a string: "<co xml:id="co.prompt" />
1219 ...
1220 printf "The user entered the following string: '%s'\n" "${inputstring}"
1221 
1222 ...
1223 </programlisting>
1224 <calloutlist>
1225   <callout arearefs="co.enable_gmacs">
1226     <para>Enable gmacs editor mode.</para>
1227   </callout>
1228   <callout arearefs="co.read_v">
1229     <para>The value of the variable is displayed and used as a default value.</para>
1230   </callout>  
1231   <callout arearefs="co.readvar">
1232     <para>Variable used to store the result.</para>
1233   </callout> 
1234   <callout arearefs="co.prompt">
1235     <para>Prompt string which is displayed in stderr.</para>
1236   </callout> 
1237 </calloutlist>
1238 </example>
1239       </section>        
1240   </section><!-- end of I/O -->
1241 
1242 
1243 
1244 
1245 
1246 
1247   <section xml:id="math">
1248   <title>Math</title>
1249 
1250       <section xml:id="use_builtin_arithmetic_expressions">
1251           <title>&tag_kshonly;&tag_performance;Use builtin arithmetic expressions instead of external applications</title>
1252           <para>Use builtin (POSIX shell) arithmetic expressions instead of
1253           <filename>expr</filename>,
1254           <filename>bc</filename>,
1255           <filename>dc</filename>,
1256           <filename>awk</filename>,
1257           <filename>nawk</filename> or
1258           <filename>perl</filename>.
1259           </para>
1260           <note>
1261               <para>ksh93 supports C99-like floating-point arithmetic including special values
1262               such as
1263               <simplelist type="inline">
1264               <member>+Inf</member>
1265               <member>-Inf</member>
1266               <member>+NaN</member>
1267               <member>-NaN</member>
1268               </simplelist>.
1269               </para>
1270           </note>
1271       </section>
1272 
1273 
1274       <section xml:id="use_floating_point_arithmetic_expressions">
1275           <title>&tag_ksh93only; Use floating-point arithmetic expressions if
1276           calculations may trigger a division by zero or other exceptions</title>
1277           <para>Use floating-point arithmetic expressions if calculations may
1278           trigger a division by zero or other exceptions - floating point arithmetic expressions in
1279           ksh93 support special values such as <literal>+Inf</literal>/<literal>-Inf</literal> and
1280           <literal>+NaN</literal>/<literal>-NaN</literal> which can greatly simplify testing for
1281           error conditions, e.g. instead of a <literal>trap</literal> or explicit
1282           <literal>if ... then... else</literal> checks for every sub-expression
1283           you can check the results for such special values.
1284           </para>
1285           <para>Example:
1286 <screen>
1287 $ <userinput>ksh93 -c 'integer i=0 j=5 ; print -- "x=$((j/i)) "'</userinput>
1288 <computeroutput>ksh93: line 1: j/i: divide by zero</computeroutput>
1289 $ <userinput>ksh93 -c 'float i=0 j=-5 ; print -- "x=$((j/i)) "'</userinput>
1290 <computeroutput>x=-Inf</computeroutput>
1291 </screen>
1292           </para>
1293       </section>
1294 
1295 
1296       <section xml:id="use_printf_a_for_passing_float_values">
1297           <title>&tag_ksh93only; Use <literal>printf "%a"</literal> when passing floating-point values</title>
1298           <para>Use <literal>printf "%a"</literal> when passing floating-point values between scripts or
1299           as output of a function to avoid rounding errors when converting between
1300           bases.</para>
1301           <para>
1302     Example:
1303 <programlisting>
1304 function xxx
1305 {
1306     float val
1307 
1308     (( val=sin(5.) ))
1309     printf "%a\n" val
1310 }
1311 float out
1312 (( out=$(xxx) ))
1313 xxx
1314 print -- $out
1315 </programlisting>
1316 This will print:
1317 <programlisting>
1318 -0.9589242747
1319 -0x1.eaf81f5e09933226af13e5563bc6p-01
1320 </programlisting>
1321           </para>
1322       </section>
1323 
1324 
1325       <section xml:id="put_constants_into_readonly_variables">
1326          <title>&tag_kshonly;&tag_performance;Put constant values into readonly variables</title>
1327          <para>Put constant values into readonly variables</para>
1328          <para>For example:
1329 <programlisting>
1330 float -r M_PI=3.14159265358979323846
1331 </programlisting>
1332 or
1333 <programlisting>
1334 float M_PI=3.14159265358979323846
1335 readonly M_PI
1336 </programlisting>
1337           </para>
1338       </section>
1339 
1340 
1341       <section xml:id="avoid_unnecessary_string_number_conversions">
1342          <title>&tag_kshonly;&tag_performance;Avoid string to number
1343          (and/or number to string) conversions in arithmetic expressions
1344          expressions</title>
1345          <para>Avoid string to number and/or number to string conversions in
1346          arithmetic expressions expressions to avoid performance degradation
1347          and rounding errors.</para>
1348          <example><title>(( x=$x*2 )) vs. (( x=x*2 ))</title>
1349 <programlisting>
1350 float x
1351 ...
1352 (( x=$x*2 ))
1353 </programlisting>
1354 <para>
1355 will convert the variable "x" (stored in the machine's native
1356 <literal>|long double|</literal> datatype) to a string value in base10 format,
1357 apply pattern expansion (globbing), then insert this string into the
1358 arithmetic expressions and parse the value which converts it into the internal |long double| datatype format again.
1359 This is both slow and generates rounding errors when converting the floating-point value between
1360 the internal base2 and the base10 representation of the string.
1361 </para>
1362 <para>
1363 The correct usage would be:
1364 </para>
1365 <programlisting>
1366 float x
1367 ...
1368 (( x=x*2 ))
1369 </programlisting>
1370 <para>
1371 e.g. omit the '$' because it's (at least) redundant within arithmetic expressions.
1372 </para>
1373          </example>
1374 
1375 
1376          <example><title>x=$(( y+5.5 )) vs. (( x=y+5.5 ))</title>
1377 <programlisting>
1378 float x
1379 float y=7.1
1380 ...
1381 x=$(( y+5.5 ))
1382 </programlisting>
1383 <para>
1384 will calculate the value of <literal>y+5.5</literal>, convert it to a
1385 base-10 string value amd assign the value to the floating-point variable
1386 <literal>x</literal> again which will convert the string value back to the
1387 internal |long double| datatype format again.
1388 </para>
1389 <para>
1390 The correct usage would be:
1391 </para>
1392 <programlisting>
1393 float x
1394 float y=7.1
1395 ...
1396 (( x=y+5.5 ))
1397 </programlisting>
1398 <para>
1399 i.e. this will save the string conversions and avoid any base2--&gt;base10--&gt;base2-conversions.
1400 </para>
1401           </example>
1402       </section>
1403 
1404 
1405       <section xml:id="set_lc_numeric_when_using_floating_point">
1406          <title>&tag_ksh93only;Set <envar>LC_NUMERIC</envar> when using floating-point constants</title>
1407          <para>Set <envar>LC_NUMERIC</envar> when using floating-point constants to avoid problems with radix-point
1408          representations which differ from the representation used in the script, for example the <literal>de_DE.*</literal> locale
1409          use ',' instead of '.' as default radix point symbol.</para>
1410          <para>For example:
1411 <programlisting>
1412 # Make sure all math stuff runs in the "C" locale to avoid problems with alternative
1413 # radix point representations (e.g. ',' instead of '.' in de_DE.*-locales). This
1414 # needs to be set _before_ any floating-point constants are defined in this script)
1415 if [[ "${LC_ALL}" != "" ]] ; then
1416     export \
1417         LC_MONETARY="${LC_ALL}" \
1418         LC_MESSAGES="${LC_ALL}" \
1419         LC_COLLATE="${LC_ALL}" \
1420         LC_CTYPE="${LC_ALL}"
1421         unset LC_ALL
1422 fi
1423 export LC_NUMERIC=C
1424 ...
1425 float -r M_PI=3.14159265358979323846
1426 </programlisting>
1427           </para>
1428 
1429           <note><para>The environment variable <envar>LC_ALL</envar> always overrides all other <envar>LC_*</envar> variables,
1430           including <envar>LC_NUMERIC</envar>. The script should always protect itself against custom <envar>LC_NUMERIC</envar> and
1431           <envar>LC_ALL</envar> values as shown in the example above.
1432           </para></note>
1433       </section>
1434 
1435 
1436 
1437   </section><!-- end of math -->
1438     
1439   
1440 
1441 
1442 
1443 
1444   <section xml:id="misc">
1445   <title>Misc</title>
1446 
1447       <section xml:id="debug_use_lineno_in_ps4">
1448           <title>Put <literal>[${LINENO}]</literal> in your <envar>PS4</envar></title>
1449           <para>Put <literal>[${LINENO}]</literal> in your <envar>PS4</envar> prompt so that you will get line
1450           numbers with you run with <literal>-x</literal>. If you are looking at performance
1451           issues put <literal>$SECONDS</literal> in the <envar>PS4</envar> prompt as well.</para>
1452       </section>
1453 
1454   </section><!-- end of misc -->
1455 
1456 
1457 
1458 
1459 </section><!-- end of RULES -->
1460 
1461 
1462 
1463   
1464 </article>