Best practices to create a Shell Script in GNU / Linux

Usually when you start working on the Servers Administration area with GNU / Linux and / or Unix Operating Systems, one finds oneself (faces) working in an environment where there is usually a bunch of scheduled tasks other admins wrote and that at some point we must manage (administer) for solve any problem, improve and / or eliminate, to comply with a new requirement of the Institution where he works. So it is not strange, that any new Sysadmin In any workplace, you are faced with the cumbersome task of understanding some of the Shell script created by others old SysAdmin, which are not well written, or are in a logical or writing structure, not easy to understand, or in the worst case, with command commands, atypical, old, inefficient, or written in an awkward and confusing way.

Shell scripting

While solving poorly written scripts is always a momentary annoyance, this teaches anyone good SysAdmin something important. If one is going to create a Shell script to be used beyond today, is always better write them in a very professional and standardized way, so that over time, anyone else, or oneself, can with the minimal effort and knowledge achieve understanding and administration in a minimum of time.

Therefore, after the practical series of publications on "Learn Shell Scripting" where we examine some very practical scripts with simple and basic commands, we will start with this new series called "Best practices to create a Shell Script in GNU / Linux", where we will thoroughly focus on each little aspect of it and the reason for many things, that is, we will cover some tips that will make us make better scripts, but not so much for ourselves, but for the next person (SysAdmin) that has to manage them. So you don't have to go through the tedious and difficult task of figuring out what I code, how and why, and why it no longer works.

In this first (1st) post of this new series "Best practices for a good Shell Script for GNU / Linux" We will talk about what goes or should go in the Shell Script header.

=======================================
HEADER - INVOCATION OF THE SHELL
=======================================

#! / path / interpret [parameter-argument]

The top line is the basic structure with which a Shell Script for GNU / Linux is invoked. Its elements can be described as follows:

#! => sha-bang

The sha-bang (#!) at the top of the Script created or to be created is a script that tells our Operating System that our file is a set of commands that will be fed (will be interpreted) by the command interpreter indicated after it. The character pair #! actually, it's a magic number two-byte, a special marker that designate a file type, and in our case, an executable shell script. Immediately after the sha-bang comes the name of the path where the interpreter to be executed is located plus the name of said interpreter. In other words, this is the path to the program that interprets the commands in the script, whether it be an interpreter, a programming language, or a utility. This shell then executes the commands in the script, starting at the top (the line after the sha-bang), and ignoring any comments. Some sha bang They may be:

#! / Bin / sh
#! / Bin / bash
#! / usr / bin / perl
#! / usr / bin / tcl
#! / bin / sed -f
#! / usr / awk -f

Each of the lines described above (as an example) invokes a different shell. The line / bin / sh, invoke the shell default (Bash on a GNU / Linux Operating System) or other similar. Using #! / Bin / sh, the default value of Bourne shell In most commercial variants of UNIX-based Operating Systems, it makes the Script created portable to other Operating Systems that are not Linux properly, but similar to or based on it or UNIX, although this sacrifices BASH-specific features. However, the sequence "#! / Bin / sh" conforms to the norm POSIX sh standard.

Note that the path given in the sha-bang must be correct, otherwise an error message, usually "Command not found", it will be the only result of the script execution. Remember the character pair »#! « it can be omitted if the Script consists only of a set of generic Operating System commands, that is, without using internal Shell directives. And keep in mind once again that »#! / Bin / sh« invokes the default shell interpreter, which defaults to »#! / Bin / bash« in a team with him GNU / Linux Operating System.

With regard to the arguments, there are several that could be used but the most common is: »-E«. which makes the script validate the execution errors of any commando (execution line) and if positive, forces the stop and exit, a typical one is »-F« for indicate which script to load and one of the rarest is »-Rm« that performs the deletion of it once its execution is finished. It is only possible to specify in the sha bang until a single argument (parameter) after the name of the program to be executed.

And finally, tell the script the global variables you will use in essential parts of your code, for validation of events, such as the execution path, authorized user, script name, among others. And end with the data of the program, creator, organization, among others, plus the licensing that applies to the program.

My advice (Best practices) to choose the best sha-bang and heading a Shell script are:

#! / usr / bin / env bash

Why using the command »Env« We indicate to the Operating System the interpreter to be used with the exact path specified within it by default, which allows us to have a sha bang that increases the portability of it, because not in all OS GNU / Linux interpreters or programs have the same path. And without arguments, because for that it is better to use the command set, because with him we can validate errors, general (-e) or specific (+ x / -x), or for clear global presets for environment (-i) or specific (-u / –unset) variables. And finally, to execute specific (- o) complementary actions inside the script.

So my recommended HEADER would be:

#! / usr / bin / env bash
# Indicate the bash interpreter with absolute path by Operating System.

set -o raised
# To tell the script to stop and close when a command or line of execution fails.

set -o nounset
# To tell the script to stop and close when the script tries to use undeclared variables.

set -o pipefail
# To get the exit status of the last order that returned a non-zero exit code.

# set -o xtrace
# To track what is running. Useful for debugging. Enable it to check for errors only.

Remember to additionally follow these recommendations:

01.- Indente your code: Making your code readable is very important, and it's something that a lot of people seem to forget as well. Try to make the necessary indentations to perceive a good logical structure in sight.

02.- Add spaces between code sections: This can help make the code much more understandable, since spacing by modules or sections helps make the code readable and easy to understand.

03.- Comment as much as possible about the code: At the top (or bottom) of each Command Order (Execution Line) or Code Section, it is ideal to add a description of the function of the script (s) to explain what happens within the code itself.

04.- Create variables with descriptive names of their functions: Assign descriptive variable names that obviously identify the function for which it will be created. Although you create temporary variables that will never be used outside of a single code block, it is still good to put a name that implicitly (objectively) explains what values ​​or functions it handles.

05.- Use the syntax VARIABLE = $ (command) for command substitution: If you want to create a variable whose value is derived from another command, there are two ways to do it in bash. With back tick, that is, with the characters `` , Eg: VARIABLE = `command -options parameters`, but is already deprecated, so the syntax VARIABLE = $ (command) it is the most modern, accepted and recommended way. NO -> DATE = `date +% F` / YES -> DATE = $ (date +% F)

06.- Use Superuser and Authorized User Validation modules and / or variables with or without password: To increase security levels if necessary.

07.- Use modules and / or variables of Validation of the Operating System (Distro, Version, Architecture): to prevent use on unsuitable platforms.

08.- Use modules (procedures / sections) to confirm the execution of critical or batch actions (modules / functions): To minimize mistakes due to improvisation or carelessness.

09.- Provide User Friendly Interfaces (User-friendly): By Terminal with menus and colors with Dialogue and with Graphical Interfaces for Basic Users with Zenity, Gxmessage. And if possible use the support of sonic alerts identifying recognizable events according to the sound. I tried as much as possible that your Script can work both ways just by enabling and disabling options / modules / functions.

10.- Include Welcome and Farewell modules (messages): if necessary to increase interactivity with the user.

11.- Include a double execution verification module: Create a lock file to prevent it from being executed more than 1 time at the same time.

12.- Rationalize the size of the script with external functions and / or modules: If the script is very large divide the code using functions or divide them into small scripts that are invoked by means of a main one.

13.- Invocation in a clear and evident way the calls to other Interpreters (programming languages) within the Script: Invokes them clearly by lines or modules.

Example:

# ================================================== #
#!/bin/bash
#Llamando a un interprete externo a BASH
echo 'El siguiente texto será mostrado por el interprete de PERL'
perl -e 'print "Este texto es mostrado por un script PERL embebido.\n";'
exit 0
# ==================================================#
# ==================================================# 
#!/bin/bash #Llamando al interprete de Python. 
echo 'El siguiente es un script de python:'
echo print "Hola, mundo!" | tee $HOME/.testpythonbash.py
python $HOME/.testpythonbash.py exit 0
# ==================================================#

# ======================================================= #
#!/bin/bash
# bash-y-perl.sh

echo "Saludos desde la parte BASH del script."
# Es posible añadir mas comandos BASH aqui.

exit 0
# Fin de la parte BASH del script.

###########################################################

#!/usr/bin/perl
# Esta parte del script se invoca con la opcion -x.

print "Saludos desde la parte PERL del script.\n";
# Podemos añadir mas comandos PERL aqui.

# Fin de la parte PERL del script.
# ======================================================= #
 

In future publications we will expand in more detail each of the practices described above.

And if you know some other good practices, your own or others, do not hesitate to comment on them to make a more complete compendium!

Until the next publication of this new series.


Leave a Comment

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

*

*

  1. Responsible for the data: Miguel Ángel Gatón
  2. Purpose of the data: Control SPAM, comment management.
  3. Legitimation: Your consent
  4. Communication of the data: The data will not be communicated to third parties except by legal obligation.
  5. Data storage: Database hosted by Occentus Networks (EU)
  6. Rights: At any time you can limit, recover and delete your information.

  1.   Max j ​​rodriguez said

    Just one detail, it's "shebang" 😛
    very good post, good practices in the long run always help to standardize.

  2.   One that passed by here said

    Bash is not the default shell on all distributions, and therefore the / bin / sh symbolic link does not always point to bash. In Debian for example (and I assume therefore Ubuntu):
    $ ls -l / bin / sh
    lrwxrwxrwx 1 root root 4 aza 8 2014 / bin / sh -> dash
    The default shell therefore on Debian is dash. See here: https://wiki.debian.org/Shell

  3.   nameless said

    As a tip to know the Shell in Use:

    echo $ 0
    echo $ SHELL
    send | grep SHELL

  4.   Jose Albert said

    You are indeed right! I tried on DEBIAN 9 and Kali Linux 2.0 and it's true! takes you to dash. Even more so is the recommendation of: #! / Usr / bin / env bash if it is the Shell you want to use.

    And you are absolutely right it is shebang, but on some websites (technical literatures) they call it shabang or other words, hence my confusion. Example:

    In computing, a shebang is the character sequence consisting of the characters number sign and exclamation mark (#!) At the beginning of a script. It is also called sha-bang, [1] [2] hashbang, [3] [4] pound-bang, [5] or hash-pling

    From: https://en.wikipedia.org/wiki/Shebang_%28Unix%29

    And Chapter 2. Starting Off With a Sha-Bang
    From: http://www.tldp.org/LDP/abs/html/sha-bang.html

  5.   Jose Albert said

    Also: basename $ 0