About bash scripting : Good practices

Introduction

This post purpose is to highlight general rules, AKA “good practices” for bash scripting. Note that these “rules” may also be good for other shell scripting languages. I personally do not especially  like rules, but these are not rules in a legal sense! These are just some good way of scripting.

I built those rules against my own experience as well as against the web experiences i fetch from here to here and more….

Of course these are kind of personal rules when i am talking about indenting size for example, but they are always based on a good reason. The good reason could be any of :

  1. Readability (could be seen as a trivial thing but it is NOT, take a look at some scripts and you will trust me!)
  2. Reliability (things like quoting variable names…)
  3. Style (OK this one is very personal and not even valuable, but i do like when things are classy, or at least when they look good)

1 Generalities

Here are the general rules or guidelines i try to follow.

  • KEEP YOUR STYLE ALONG ALL OF YOUR SCRIPTS.
    You should not mix up your style within one shell script.

 

  • COMMENT YOUR HIEROGLYPHS.
    It is very usual to say that but it is so true.

 

  • PRINT BASIC INFO AT THE BEGINNING OF YOUR SCRIPT.
    Use a “cartouche” at its top. (See below for an example, coming from this page)

    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    # Company name - Division name - Project name
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    # [MyTrigram] - [TheDate] - [ScriptName] - [VersionNumber]
    # ---------------------------------
    # [Script description and context for usage]
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

  • GIVE A USAGE SUMMARY FOR YOUR FUNCTION.
    Functions are a very powerful tools but they need to be documented.

 

  • TRY NOT TO USE DEPRECATED FORM.
    There is sometimes good reasons (mostly for portability) to use a deprecated form, unless you have one you should NOT.

 

  • USE RELEVANT SHEBANG.
    Do not use #!/bin/sh for bash : use #!/bin/bashor you will, one or another day, be having problems…

 

  • RETURN RELEVANT EXIT CODE.
    By relevant i mean use different code for different exit cause. This way your code debugging will be easier and the general use will be ease too.

 

 

2 Formatting your shell scripts

This first chapter is about formatting in general and “indentation” more specifically. Although this is not mandatory for a shell script to be well indented in order to work properly, it becomes quiet necessary for the script maintenance and future revisions to be easily readable and optically nice and easy. Of course the readability of a program is not only due to its indentation, but it is a big part of it.

2.1 Generalities

Here is the general rule i am trying to apply to all of my shell production regarding indentation.

  • 2 spaces as a tab is what i do prefer (less would not be enough and more would only be useless IMO), 4 spaces is also  one recommended form.

 

2.2 if, case, functions etc …

This section presents some indentations and formatting “rules” i found to be the best.

  • if statement
    I usually like if statement as :

    if [[ "${variable}" = "value1" ]] ; then
      command_1
    elif [[ "${variable}" = "value2" ]] ; then
      command_2
    else
      command_3
    fi

    I very rarely (if never) put if statement on a single line, i don’t think it is readable enough.

  • for, while, until loop
    That is the way i like my loops :

    for i in ${var1} ${var2} ; do
      command_1
    done
  • case statement
    • One indentation level for the inside case statement and no more after
    • No extra spaces or new-line for each case statement if possible (when the command block is not too long)
      case ${foo}  in
        foo1 ) command1
        ;;
        foo2 ) command2
        ;;
        * ) command3
        ;;
      esac
    • If the command block is too long or too complex, just indent it of one more level as :
      case ${foo}  in
        foo1 )
          if [[ "${var}" = "value" ]] ; then
            command1
          fi
        ;;
        foo2 )
          command2
        ;;
        * )
          command3
        ;;
      esac

    Note : You should NOT mix your style within one script of inside one loops, statement, function etc… As for the above case construct even if command2 and command3 are not too long or too complex you should format them the same way you did for the command1.

  • Functions
    • Here is how i format my functions :
Controls () {
  for t in ${DirsFiles[@]} ; do
     if [[ ! -d "${t}" ]] && [[ ! -e "${t}" ]] ; then
       ErrorPrintandlog "${F_Error}" "${t} does not exist!"
     fi
  done
}

I am trying to keep the indentation level for inside the function, but i must confess that i sometimes forget it…

 

3 Naming in shell scripts

This section is about the “naming convention” i found to be efficient and clear.

  • Variables naming
    Use only lowercase names for my variables since system variables are UPPERCASE.
  • Functions naming
    Use CamelCase for function naming and you should not use existing built-in or external command name for your function, unless you want to overwrite them.

 

4 Miscellaneous stuff

4.1 Variables Vs Functions

  1. You generally do NOT want to put command names or command options in variables.
  2. Variables should contain only data, such as usernames, hostnames, ports, text, etc.
  3. Functions are used for complex or not so complex (even a simple command that you just do not want to repeat) commands.

 

4.2 Handle long lines of code and comments

  1. Long line of CODE : use the line continuation “\” to handle long lines of code
    e.g :

    echo 'a very long line, that is so long that i may \
            have cut it yesterday or the other day, \
            but now it is  too late : it is just tooooo long !'

    Note : in this case and in the case of an HEREDOC i use a bigger indentation unit (8 spaces) just because i found it clearer that way.

  2. Long line of COMMENTS : use the code i found in the ABS guide and that i found it smart and clean, the “#+
    e.g :

    # A very long line of comment, that is so long that i may
    #+ have cut it yesterday or the other day
    #+ but now it is  too late : it is just tooooo long !'
    # Then this is a new sentence, a new comment

 

4.3 Storing path in variables

It is better and so much cleaner to NOT INCLUDE A TRAILING SLASH in variable that store paths.

Here is two examples, the first is the stuff i really do not like or think it is efficient while the second one is what i humbly recommend.

  1. The wrong way
    source_path="/path/to/this"
    dest_path="/path/to/that"
    mv "${HOME}${source_path}" "/home/whoever${dest_path}"  # Here there is no clue about what is inside the two variables
                                                            #+ (ok this is obviously a path... bad example!)
                                                            #+ isn't that awful ?
  2. The best way (imho)
    source_path="path/to/this"
     dest_path="path/to/that"
    mv "${HOME}/${source_path}" "/home/whoever/${dest_path}"  # Here everything is clean and clear (and also some much elegant)

 

 4.4 Testing a command return code with “if”

It is quite usual, to say the least, to test a return code of a given command in a shell script. One can often see form like :

test=$(grep "blabla" /tmp/test.txt)
if [[ "$?" = "0 " ]] ; then
  # do something
fi

 

when it should be done like :

if grep "blabla" /tmp/test.txt ; then
  # do something
fi

Why should it be done this way ?

  1. It is clearer, therefore easier to debug
  2. It is classer, therefore you will shine among other scripter (!)
  3. It is shorter and more efficient
  4. It is more robust, because in the first form you can’t always escape from a command to come between the ${test} definition and the if statement.

Find more about this topic here.

 

Resources

Leave a Reply

Your email address will not be published. Required fields are marked *

This site supports SyntaxHighlighter via WP SyntaxHighlighter. It can highlight your code.
How to highlight your code: Paste your code in the comment form, select it and then click the language link button below. This will wrap your code in a <pre> tag and format it when submitted.