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