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