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