قالب وردپرس درنا توس
Home / Tips and Tricks / Basics of Bash Automation and Scripting (Part 3) – CloudSavvy IT

Basics of Bash Automation and Scripting (Part 3) – CloudSavvy IT



Shutterstock / Mopic

In this final article in our three-part series of Bash automation and scripting basics, we’ll explore script debugging, run scripts as a background process, and import other scripts using the source command.

Bash automation and scripting basics

If you want to start at the beginning, read our article Bash Automation and Scripting Basics, Part 1

. This final article in our three-part series on Bash automation and scripting basics looks at scripting as a background process.

We also want to quickly debug our scripts with minimal hassle and high-quality results. This can be done with the help of a very handy tracing feature built right into the Bash command interpreter. We will examine this in the second topic. You may also want to keep an eye out for our upcoming article on the related shell check.

And finally, we will explore how scripts can be run as a background process. While this can provide modest immediate benefits such as starting multiple tasks at once, it also lays some of the groundwork for later, more advanced multi-threaded scripts.

Debug script

Debugging a script in Bash doesn’t have to be difficult! Keep an eye out on the CloudSavvyIT website as we will be reviewing the more comprehensive shell check debugging tool for Bash shortly, but right now I want to introduce you to a great way to debug Shell scripts in a simple and easy to understand way .

Within the Bash scale, which is, after all, one “easy” binary file running on your computer – namely the bash binary, an option is provided (-x) which, according to man bash (running this in your terminal will display a Bash guide) is described as Print commands and their arguments as they are executed, and this is exactly what it does! How does this help with debugging? Consider the following example:

#!/bin/bash

A=1
B=2
if [ "${AA}" == "1" -o "${B}" == "1" ]; then 
  echo "One ('1') was definitely stored in either the variable A, or the variable B"
  exit 0
else
  echo "Assert: could not locate the value '1' in the variables A and B"
  exit 1
fi

A small Bash script with a bug

Here we check the variables A and B against the value 1The -o idiom in the if statement stands for OR, i.e. either the first part (A, or rather AA here is 1) is true, or the second part (B is 1) is true and in that case success is achieved.

The output of the script is the programmed statement and the program ends with the exit code 1, which usually means that an error has occurred. If the script had worked correctly, a confirmation message would be displayed and the script would end with the exit code 0, which generally means there was success in whatever the script or utility was intended for.

So why does the script run into the claim? You may have noticed that the variable A encountered a typo in us if statement, registered in the code as AA: an insect! We could go and check the script, and if it is as short and straightforward as the script shown here, the bug would be found quickly. But for a program with 5000 lines, the solution is not that simple, especially if it uses multiple threads, complex subshells, etc.

Now let’s debug this with the -x option to Bash. You may remember from the second part of our basic Bash automation and scripting course that a subshell can be started by an inline $( ... ) set of idioms. It can also be started by simply typing bash, or in this case bash -x in our top shell. In this case, we’ll run the script in our Bash subshell, with the -x possibility to observe what is happening step by step.

Run our smalls script with bash -x

So we executed bash -x ./test_debugging.sh and note that the following conditional check is performed: '[' '' == 1 -o 2 == 1 ']'We notice that something is not right: the value of 2 is being compared to 1 in the second part of our conditional check, but what happens in the first part? Something is compared to it 1, but that something is … empty (as indicated by the empty string ''

We then check our script why that empty space is there, and why it wasn’t filled with the value of us A variable. We quickly realize the AA instead of A error, correct the error and the script now works fine!

Fixed the corrected script with the bug

A very cool thing to remember when using bash -x is that you can tee (read this as ‘to copy’) the output of the Bash command by redirecting the stderr (the error output) to stdout (the default output) and capturing the same with tee

Use tee in combination with bash -x

Here we run our regular script and send the error output (bash -x sends all its informational debug output to stderr, the default error output, and not to stdout) with 2>&1 (which redirects our stderr output to stdout – our default output – instead). We then capture stdout using tee and this will save the output in the specified file, namely bash_-x_output.txt

This allows a Bash developer to slowly revise his or her written code in a step-by-step format. Especially when programs become complex, have functions, become multi-threaded, start background processes, etc. this method of debugging can be very valuable. As an example I use bash -x about once every fortnight to debug complex scripts.

Run scripts as background processes

Running a script as a background process is easy: just paste & at the end of the script name (with a space in between). We define background.sh as follows:

#!/bin/bash

sleep 2

We then start it in the following way – to emphasize that it runs in the background:

Flow of a number of Bash commands where one such command is a background process

What we can see happening here is as follows: the background.sh script will run in the background (given the & appended to the script name with a space), and immediately the command prompt will return. We use this here by issuing the following command (sleep 1) right after the & background idiom, which also ends that command as a single / single command (in other words, sleep 1 is a completely new command).

We also end our sleep 1 command with a usual end of command Bash idiom, after which we get one echo that the sleep 1 is complete / ready. Next, let’s see what happens when executing this rule.

Our background process / script (background.sh) will start, which will take approximately 2 seconds. The PID (process identification) of the started background process is displayed visually (namely 773183 for our first ([1]) background process – and this PID will be different every time you start a background program / process), and our sleep 1 (the next statement to execute) can now be executed because the other program returned our prompt (although this is not directly shown here, this will happen if you start a background process; you will immediately get the command prompt back).

The sleep 1 begins (with the sleep 2 or rather the background.sh script that is still running in the background, as a different process, in a subshell started under this top or higher level shell), and ends after 1 second. Hereafter our echo is executed and shows us the sleep 1 is complete. A second later, our background.sh process finishes with 2 seconds wait, and ends.

We don’t see it terminated while the Bash shell is waiting for any interaction to show us status messagesSo as soon as we hit enter, anytime after the two second sleep is over, we will see the background process termination as a [1]+ Done ./background.sh status message. If you go back to the second part of our miniseries, you might also see how we could have used it wait here to wait for completion / termination of background process PID. It also shows how many commands and utilities can be used in Bash in a combinatorial way.

Import scripts with source

Importing another script can be done easily with the Bash source order. Consider the following script testsource.sh

#!/bin/bash

source mysource.sh

echo "${MYVAR}"

And the matching mysource.sh

#!/bin/bash

MYVAR="Hello CloudSavvyIT Readers!"

If we just make the first script (testsource.sh) executable (by chmod +x testsource.sh), but not the second script (in fact, we disabled the executable flag to clearly show that this is working through chmod -x On mysource.sh), the second script is still successfully invoked due to the source command, and executed as part of the testsource.sh script:

Sourcing a script with the Bash source command

In the mysource.sh script, we set the variable MYVAR to Hello CloudSavvyIT Readers!This script is then sourced from the testsource.sh script using the statement source mysource.shThis will be the mysource.sh to run at that point, the code, and when completed, the testsource.sh script continues to run, although all things contained in it mysource.sh are preserved (think from another script to make it easier to remember its operation).

In summary

We’ve looked at script debugging using bash -x to display all executed commands. We also explored how to run a script as a background process and learned how to import scripts with source code. Thank you to keep up to date with this three-part series, of which this was the last article!

If you want to learn more about Bash, check out our or articles Primer: Bash Loops: for, while, and till, Conditional Testing in Bash: if, then, else, elif and Bash Functions and Local Variables


Source link