What is Shell?
The UNIX shell program interprets user commands which are either directly entered by the user, or which can be read from a file called the shell script or shell program. Shell scripts are interpreted, not compiled. The shell reads commands from the script line per line and searches for those commands on the system.
The below command is used to check known shells in a UNIX system.
root@localhost$ cat /etc/shells # List of acceptable shells for chpass(1). # Ftpd will not allow users to connect who are not using # one of these shells. /bin/bash /bin/csh /bin/dash /bin/ksh /bin/sh /bin/tcsh /bin/zsh
To change the shell, just write down the shell name; since a shell is an executable file (program), the current shell activates it and it gets executed. A new prompt is usually shown because each shell has its typical appearance.
Well, not knowing debugging the script will make your life miserable; you will be scratching your brain till your last nerve & still the issue will not be resolved. So, I would recommend to you guys that you must know about debugging because this will come in handy, believe me.
Debugging the script
Why debugging is important? Well, if things don’t go according to plan then we need to know where our script fails, right? So, this is where debugging comes into play.
The most basic step while debugging the script is, “echo”. You can “echo” the command on which you are using the variables so that you can check in the output section whether it is taking the right values or not.
#!/bin/bash var="opstree" echo "Hello, $var"
root@localhost$ ./script.sh Hello, opstree
Similarly, you can use this format for commands also.
For extensive debugging, we use “set” which is Bash’s built-in.
The most common one is the -x option, it will run the script in debug mode. Set -x shows the command as well as their output on the terminal so that you would know for which command, what the output is.
There are two ways to use the -x option,
Example1: bash -x script.sh ( while running the script )
Example2: #!/bin/bash -x ( adding -x option in shebang )
Similarly, if we want to debug a particular part of the script on which you have doubts then we use set bash built-in.
#!/bin/bash set -x #Enabling the Debugging echo "foo" set +x #Disabling the Debugging echo "bar"
root@localhost$ ./script.sh + echo foo foo + set +x bar
And -o xtrace is another way to write -x.
Sometimes you have not provided the variable, yet you have used it somewhere in the script. So, by default bash will ignore the variable that does not exist. So here, Script should report an error instead of continuing the execution silently.
#!/bin/bash echo $foo echo "bar"
root@localhost$ ./script.sh bar
In the above O/P, instead of throwing any error, our script has ignored the variable that is not defined and continues executing the remaining part of the script.
As a DevOps engineer, I don’t want my script to continue running even after some errors, so here we use,
Using, set -u
#!/bin/bash set -u echo $foo echo "bar"
root@localhost$ ./script.sh ./script.sh: line 3: foo: unbound variable
Also, -o nounset is another way to write -u. They are equivalent.
Well, bash will throw an error on any wrong command provided on the script, yet it will continue to execute the remaining part of the script. However, we don’t want bash to accumulate the errors instead, it should stop executing the script on the first error, right away.
#!/bin/bash foo echo "bar"
root@localhost$ ./script.sh ./script.sh: line 2: foo: command not found bar
with set -e
#!/bin/bash set -e foo echo "bar"
root@localhost$ ./script.sh ./script.sh: line 3: foo: command not found
Set -e determines whether the script is a pass or fail, based on the return value. However, some commands may have a non-zero return value and that doesn’t indicate the running fails or you want the script to continue running even if the command fails, so for that, you can turn off “set -e” temporarily and enable “set -e” after the command ends.
#!/bin/bash set +e command1 command2 set -e
set +e means to turn off the -e option, and set -e means to turn on set -e again.
Set -e doesn’t apply to pipe commands because as we know “set -e” is determined by the return value & the pipe command will return the value of the last command, even if the first command fails.
#!/bin/bash set -e foo | echo "a" echo "bar"
vikas.b4_ote$ ./script.sh a ./script.sh: line 3: foo: command not found bar
So to overcome this issue, we can use “set -o pipefail”. As one of the subcommands fails, the script will terminate execution.
#!/bin/bash set -eo pipefail foo | echo "a" echo "bar"
root@localhost$ ./script.sh a ./script.sh: line 3: foo: command not found
Displaying more bash options
-o option is used with a set to display all the options, you can use whatever options that you want to make your script more robust in nature.
root@localhost$ set -o allexport off braceexpand on emacs on errexit off errtrace off functrace off hashall on histexpand on history on ignoreeof off interactive-comments on keyword off monitor on noclobber off noexec off noglob off nolog off notify off nounset off onecmd off physical off pipefail off posix off privileged off verbose off vi off xtrace off
If you are reading the conclusion section, then I hope you guys have learned the debugging methods, which I have mentioned above. As we discussed, how we can stop the execution of our script whenever an error occurs. There are also multiple other methods that we can use but for now, we covered the widely used & general use cases, to see all the other methods we can use “set -o”.
I hope this blog has helped you with the debugging methods, through which we can make our script more robust in nature. Follow the steps mentioned above and you are good to go.
If you guys have any doubts or suggestions feel free to ask in the comment section below.
Opstree is an End to End DevOps solution provider