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