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