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