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