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