{"id":8854,"date":"2021-11-23T17:08:36","date_gmt":"2021-11-23T11:38:36","guid":{"rendered":"https:\/\/opstree.com\/blog\/\/?p=8854"},"modified":"2026-03-05T17:26:14","modified_gmt":"2026-03-05T11:56:14","slug":"debugging-in-shell-script","status":"publish","type":"post","link":"https:\/\/opstree.com\/blog\/2021\/11\/23\/debugging-in-shell-script\/","title":{"rendered":"How To Debug a Bash Shell Script?"},"content":{"rendered":"\r\n<p>The fastest way to debug a Bash script is to enable debug mode. This prints each command before execution, helping you find problems.\u00a0<\/p>\r\n<p>In this tutorial, we&#8217;ll explore various techniques for debugging Bash shell scripts. Although the Bash shell doesn&#8217;t have a built-in debugger, we can effectively use certain commands and constructs to aid in this process. First, we&#8217;ll understand how to use the set command for script debugging. Next, we&#8217;ll discuss specific uses of the set and trap commands. Finally, we&#8217;ll outline some strategies for debugging a script that&#8217;s already running.<\/p>\r\n<h2 class=\"wp-block-heading\" id=\"what-is-shell\"><strong>What is Shell?<\/strong><\/h2>\r\n\r\n\r\n\r\n<p class=\"has-text-align-justify\">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.<!--more--><\/p>\r\n\r\n\r\n\r\n<p>The below command is used to check known shells in a <a href=\"https:\/\/opstree.com\/blog\/2019\/07\/16\/unix-file-tree-part-1\/\">UNIX<\/a> system.<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-verse has-white-color has-dark-gray-background-color has-text-color has-background\">root@localhost$ cat \/etc\/shells \r\n# List of acceptable shells for chpass(1).\r\n# Ftpd will not allow users to connect who are not using\r\n# one of these shells.\r\n\r\n\/bin\/bash\r\n\/bin\/csh\r\n\/bin\/dash\r\n\/bin\/ksh\r\n\/bin\/sh\r\n\/bin\/tcsh\r\n\/bin\/zsh<\/pre>\r\n\r\n\r\n\r\n<p class=\"has-text-align-justify\">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.<\/p>\r\n\r\n\r\n\r\n\r\n\r\n<p>Well, not knowing debugging the script will make your life miserable; you will be scratching your brain till your last nerve &amp; 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.<\/p>\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n<p id=\"debugging-the-script\">Why debugging is important? Well, if things don\u2019t go according to plan then we need to know where our script fails, right? So, this is where debugging comes into play.<\/p>\r\n\r\n\r\n\r\n\r\n\r\n<p class=\"has-text-align-justify\">The most basic step while debugging the script is, \u201c<strong>echo<\/strong>\u201d. You can &#8220;echo&#8221; 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.<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-verse has-white-color has-dark-gray-background-color has-text-color has-background\">#!\/bin\/bash\r\n\r\nvar=\"opstree\"\r\necho \"Hello, $var\"<\/pre>\r\n\r\n\r\n\r\n<p>OUTPUT:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-verse has-white-color has-dark-gray-background-color has-text-color has-background\">root@localhost$ .\/script.sh \r\nHello, opstree<\/pre>\r\n\r\n\r\n\r\n<p>Similarly, you can use this format for commands also.<\/p>\r\n\r\n\r\n\r\n\r\n\r\n<p class=\"has-text-align-justify\"><strong>For extensive debugging, we use \u201cset\u201d which is Bash\u2019s built-in.<\/strong><\/p>\r\n<p><strong>[ Are you looking: <a href=\"https:\/\/opstree.com\/services\/devsecops-transformation-and-automation\/\">DevSecOps Solution Provider<\/a>]<\/strong><\/p>\r\n\r\n\r\n\r\n<h4 class=\"wp-block-heading\" id=\"set-x\">Set -x<\/h4>\r\n\r\n\r\n\r\n<p class=\"has-text-align-justify\">The most common one is the <strong>-x <\/strong>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.<\/p>\r\n\r\n\r\n\r\n<p class=\"has-text-align-justify\">There are two ways to use the -x option,\u00a0<\/p>\r\n\r\n\r\n\r\n<p class=\"has-text-align-justify\"><strong>Example1:<\/strong> bash -x script.sh \u00a0 ( while running the script )<\/p>\r\n\r\n\r\n\r\n<p><strong>Example2:<\/strong> #!\/bin\/bash -x\u00a0 \u00a0 \u00a0 \u00a0 ( adding -x option in shebang )<\/p>\r\n\r\n\r\n\r\n<p class=\"has-text-align-justify\">Similarly, if we want to debug a particular part of the script on which you have doubts then we use set bash built-in.<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-verse has-white-color has-dark-gray-background-color has-text-color has-background\">#!\/bin\/bash\r\n\r\nset -x       #Enabling the Debugging\r\necho \"foo\"\r\nset +x       #Disabling the Debugging\r\n\r\necho \"bar\"<\/pre>\r\n\r\n\r\n\r\n<p><strong>OUTPUT:<\/strong><\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-verse has-white-color has-dark-gray-background-color has-text-color has-background\">root@localhost$ .\/script.sh \r\n+ echo foo\r\nfoo\r\n+ set +x\r\nbar<\/pre>\r\n\r\n\r\n\r\n<p>And <strong>-o xtrace<\/strong> is another way to write <strong>-x<\/strong>.<\/p>\r\n<p><strong>[ Also Read: <a href=\"https:\/\/opstree.com\/blog\/2016\/08\/08\/stunnel-a-proxy-to-ship-the-log-on-ssl\/\">Stunnel a Proxy to ship the log on SSL<\/a> ]<\/strong><\/p>\r\n\r\n\r\n\r\n<h4 class=\"wp-block-heading\" id=\"set-u\"><strong>set -u<\/strong><\/h4>\r\n\r\n\r\n\r\n<p class=\"has-text-align-justify\">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, <a href=\"https:\/\/opstree.com\/blog\/2019\/03\/19\/best-practices-for-writing-a-shell-script-2\/\">Script<\/a> should report an error instead of continuing the execution silently.<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-verse has-white-color has-dark-gray-background-color has-text-color has-background\">#!\/bin\/bash\r\necho $foo\r\necho \"bar\"<\/pre>\r\n\r\n\r\n\r\n<p><strong>OUTPUT<\/strong>:<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-verse has-white-color has-dark-gray-background-color has-text-color has-background\">root@localhost$ .\/script.sh \r\n\r\nbar<\/pre>\r\n\r\n\r\n\r\n<p class=\"has-text-align-justify\">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.<\/p>\r\n\r\n\r\n\r\n<p class=\"has-text-align-justify\">As a <a href=\"https:\/\/opstree.com\/\"><strong>DevOps engineer<\/strong><\/a>, I don\u2019t want my script to continue running even after some errors, so here we use,<\/p>\r\n\r\n\r\n\r\n<p>Using, <strong>set -u<\/strong><\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-verse has-white-color has-dark-gray-background-color has-text-color has-background\">#!\/bin\/bash\r\nset -u\r\necho $foo\r\necho \"bar\"<\/pre>\r\n\r\n\r\n\r\n<p><strong>OUTPUT:<\/strong><\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-verse has-white-color has-dark-gray-background-color has-text-color has-background\">root@localhost$ .\/script.sh \r\n.\/script.sh: line 3: foo: unbound variable<\/pre>\r\n\r\n\r\n\r\n<p>Also, <strong>-o nounset<\/strong> is another way to write <strong>-u<\/strong>. They are equivalent.<\/p>\r\n\r\n\r\n\r\n<h4 class=\"wp-block-heading\" id=\"set-e\"><strong>set -e<\/strong><\/h4>\r\n\r\n\r\n\r\n<p class=\"has-text-align-justify\">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\u2019t want bash to accumulate the errors instead, it should stop executing the script on the first error, right away.<\/p>\r\n\r\n\r\n\r\n<p><strong>For Example:<\/strong><\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-verse has-white-color has-dark-gray-background-color has-text-color has-background\">#!\/bin\/bash\r\nfoo\r\necho \"bar\"<\/pre>\r\n\r\n\r\n\r\n<p><strong>OUTPUT:<\/strong><\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-verse has-white-color has-dark-gray-background-color has-text-color has-background\">root@localhost$ .\/script.sh \r\n.\/script.sh: line 2: foo: command not found\r\nbar<\/pre>\r\n\r\n\r\n\r\n<p>with <strong>set -e<\/strong><\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-verse has-white-color has-dark-gray-background-color has-text-color has-background\">#!\/bin\/bash\r\nset -e\r\nfoo\r\necho \"bar\"<\/pre>\r\n\r\n\r\n\r\n<p><strong>OUTPUT:<\/strong><\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-verse has-white-color has-dark-gray-background-color has-text-color has-background\">root@localhost$ .\/script.sh \r\n.\/script.sh: line 3: foo: command not found<\/pre>\r\n\r\n\r\n\r\n<p class=\"has-text-align-justify\"><strong><strong>Set -e<\/strong> <\/strong>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\u2019t indicate the running fails or you want the script to continue running even if the command fails, so for that, you can turn off \u201cset -e\u201d temporarily and enable \u201cset -e\u201d after the command ends.<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-verse has-white-color has-dark-gray-background-color has-text-color has-background\">#!\/bin\/bash\r\nset +e\r\ncommand1\r\ncommand2\r\nset -e<\/pre>\r\n\r\n\r\n\r\n<p><strong>set +e<\/strong> means to turn off the -e option, and set -e means to turn on <strong>set -e<\/strong> again.<\/p>\r\n\r\n\r\n\r\n<h4 class=\"wp-block-heading\" id=\"set-o-pipefail\"><strong>set-o pipefail<\/strong><\/h4>\r\n\r\n\r\n\r\n<p class=\"has-text-align-justify\">Set -e doesn\u2019t apply to pipe commands because as we know \u201cset -e\u201d is determined by the return value &amp; the pipe command will return the value of the last command, even if the first command fails.<\/p>\r\n\r\n\r\n\r\n<p><strong>Example:<\/strong><\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-verse has-white-color has-dark-gray-background-color has-text-color has-background\">#!\/bin\/bash\r\nset -e\r\nfoo | echo \"a\"\r\necho \"bar\"<\/pre>\r\n\r\n\r\n\r\n<p><strong>OUTPUT:<\/strong><\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-verse has-white-color has-dark-gray-background-color has-text-color has-background\">vikas.b4_ote$ .\/script.sh \r\na\r\n.\/script.sh: line 3: foo: command not found\r\nbar<\/pre>\r\n\r\n\r\n\r\n<p><br \/>So to overcome this issue, we can use \u201c<strong>set -o pipefail<\/strong>\u201d. As one of the subcommands fails, the script will terminate execution.<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-verse has-white-color has-dark-gray-background-color has-text-color has-background\">#!\/bin\/bash\r\nset -eo pipefail\r\nfoo | echo \"a\"\r\necho \"bar\"<\/pre>\r\n\r\n\r\n\r\n<p><strong>OUTPUT:<\/strong><\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-verse has-white-color has-dark-gray-background-color has-text-color has-background\">root@localhost$ .\/script.sh \r\na\r\n.\/script.sh: line 3: foo: command not found<\/pre>\r\n\r\n\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\" id=\"displaying-more-bash-options\"><strong><span style=\"text-decoration: underline;\">Displaying more bash options<\/span><\/strong><\/h2>\r\n\r\n\r\n\r\n<p class=\"has-text-align-justify\"><strong>-o<\/strong> 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.<\/p>\r\n\r\n\r\n\r\n<pre class=\"wp-block-verse has-white-color has-dark-gray-background-color has-text-color has-background\">root@localhost$ set -o\r\nallexport      \toff\r\nbraceexpand    \ton\r\nemacs          \ton\r\nerrexit        \toff\r\nerrtrace       \toff\r\nfunctrace      \toff\r\nhashall        \ton\r\nhistexpand     \ton\r\nhistory        \ton\r\nignoreeof      \toff\r\ninteractive-comments\ton\r\nkeyword        \toff\r\nmonitor        \ton\r\nnoclobber      \toff\r\nnoexec         \toff\r\nnoglob         \toff\r\nnolog          \toff\r\nnotify         \toff\r\nnounset        \toff\r\nonecmd         \toff\r\nphysical       \toff\r\npipefail       \toff\r\nposix          \toff\r\nprivileged     \toff\r\nverbose        \toff\r\nvi             \toff\r\nxtrace         \toff<\/pre>\r\n\r\n\r\n\r\n<h3 class=\"wp-block-heading\" id=\"conclusion\"><strong><span style=\"text-decoration: underline;\">Conclusion<\/span><\/strong><\/h3>\r\n\r\n\r\n\r\n<p class=\"has-text-align-justify\">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 &amp; general use cases, to see all the other methods we can use &#8220;set -o&#8221;.<\/p>\r\n\r\n\r\n\r\n<p class=\"has-text-align-justify\">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.<\/p>\r\n\r\n\r\n\r\n<p class=\"has-text-align-justify\">If you guys have any doubts or suggestions feel free to ask in the comment section below.<\/p>\r\n<p>If you want to learn more interesting <a href=\"https:\/\/www.buildpiper.io\/\" target=\"_blank\" rel=\"noopener\">Best DevOps Tools<\/a>.<\/p>\r\n\r\n\r\n\r\n\r\n\r\n\r\n","protected":false},"excerpt":{"rendered":"<p>The fastest way to debug a Bash script is to enable debug mode. This prints each command before execution, helping you find problems.\u00a0 In this tutorial, we&#8217;ll explore various techniques for debugging Bash shell scripts. Although the Bash shell doesn&#8217;t have a built-in debugger, we can effectively use certain commands and constructs to aid in &hellip; <a href=\"https:\/\/opstree.com\/blog\/2021\/11\/23\/debugging-in-shell-script\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;How To Debug a Bash Shell Script?&#8221;<\/span><\/a><\/p>\n","protected":false},"author":211160191,"featured_media":29545,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_coblocks_attr":"","_coblocks_dimensions":"","_coblocks_responsive_height":"","_coblocks_accordion_ie_support":"","jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false},"version":2}},"categories":[28070474],"tags":[44070,2674,768739285,2929,43912,343865],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/opstree.com\/blog\/wp-content\/uploads\/2021\/11\/How-To-Debug-a-Bash-Shell-Script.jpg","jetpack_likes_enabled":false,"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/pfDBOm-2iO","jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/opstree.com\/blog\/wp-json\/wp\/v2\/posts\/8854"}],"collection":[{"href":"https:\/\/opstree.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/opstree.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/opstree.com\/blog\/wp-json\/wp\/v2\/users\/211160191"}],"replies":[{"embeddable":true,"href":"https:\/\/opstree.com\/blog\/wp-json\/wp\/v2\/comments?post=8854"}],"version-history":[{"count":27,"href":"https:\/\/opstree.com\/blog\/wp-json\/wp\/v2\/posts\/8854\/revisions"}],"predecessor-version":[{"id":30897,"href":"https:\/\/opstree.com\/blog\/wp-json\/wp\/v2\/posts\/8854\/revisions\/30897"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/opstree.com\/blog\/wp-json\/wp\/v2\/media\/29545"}],"wp:attachment":[{"href":"https:\/\/opstree.com\/blog\/wp-json\/wp\/v2\/media?parent=8854"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/opstree.com\/blog\/wp-json\/wp\/v2\/categories?post=8854"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/opstree.com\/blog\/wp-json\/wp\/v2\/tags?post=8854"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}