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