Write a robust bash script

Source: Internet
Author: User

Write a robust bash script

Many people use shell scripts to accomplish simple tasks and become part of their lives. Unfortunately, shell scripts can be very much affected by running exceptions. It is necessary to minimize such problems when writing scripts. In this article I'll cover some of the techniques that make bash scripts robust.

Using Set-u www.ahlinux.com

How many times have you crashed the script because you didn't initialize the variable? For me, many times.

Chroot=$1

...

RM-RF $chroot/usr/share/doc

If the above code does not run on the parameters, you will not just delete the document from the chroot, but delete all of the system's documents. So what should you do? Fortunately, Bash provides set-u, so bash automatically exits when you use uninitialized variables. You can also use a more readable set-o nounset.

david% bash/tmp/shrink-chroot.sh

$chroot =

david% Bash-u/tmp/shrink-chroot.sh

/tmp/shrink-chroot.sh:line 3: $1:unbound variable

david%

Using SET-E

The beginning of every script you write should contain set-e. This tells Bash to exit bash if any one of the statements returns a value that is not true. The advantage of using-e is to avoid making the error snowball into a serious error and catching the error as early as possible. More readable version: Set-o errexit

Use-E to free you from the error of inspection. If you forget to check, bash will do it for you. But you have no way of using $? To get the command execution status, because bash cannot get any return values other than 0. You can use a different structure:

Command

If ["$?" -ne 0]; Then echo "Command failed"; Exit 1; Fi

Can be replaced by:

Command | | {echo "command failed"; exit 1;}

or use:

if! Command Then echo "Command failed"; Exit 1; Fi

If you have to use a command that returns a value other than 0, or you are not interested in the return value? You can use command | | True, or you have a very long code, you can temporarily turn off the error checking function, but I suggest you use it sparingly.

Set +e

Command1

Command2

Set-e

The documentation indicates that bash returns the value of the last command in the pipeline by default, perhaps the one you don't want. such as execution false | True will be considered a successful execution of the command. If you want such a command to be considered an execution failure, you can use Set-o Pipefail

Program defense-think of unexpected things

Your script may be run under an "unexpected" account, such as missing files or directories not being created. You can do something to prevent these mistakes. For example, when you create a directory, the mkdir command returns an error if the parent directory does not exist. If you add the-P option to the mkdir command when you create a directory, it will create the desired parent directory before creating the desired directory. Another example is the RM command. If you want to delete a nonexistent file, it will "spit" and your script will stop working. (Because you're using the-e option, right?) You can use the-f option to solve this problem and let the script continue to work when the file does not exist.

Ready to handle spaces in file names

Some people use spaces in filenames or command-line arguments, and you'll need to remember that when you're scripting. You need to always remember to enclose the variable in quotation marks.

if [$filename = "Foo"];

When the $filename variable contains a space, it is hung out. This can be resolved:

If ["$filename" = "foo"];

When using the [email protected] variable, you also need to use quotation marks, because two parameters separated by spaces are interpreted as two separate parts.

david% foo () {for i in [email protected]; does echo $i; done}; Foo bar "Baz Quux"

Bar

Baz

Quux

david% foo () {for i in "[email protected]", do echo $i; Foo bar "Baz Quux"

Bar

Baz Quux

I didn't think of any use of "[email protected]", so when you have questions, use quotes without errors.

If you use Find and Xargs at the same time, you should use-PRINT0 to let the characters split the file name instead of line breaks.

david% Touch "Foo bar"

david% Find | Xargs ls

LS:./foo:no such file or directory

Ls:bar:No such file or directory

david% find-print0 | xargs-0 ls

./foo Bar

Set of traps

When you write a script that hangs out, the file system is in an unknown state. For example, lock file status, temporary file status, or update a file before updating the next file before you hang up. If you can solve these problems, either deleting the lock file or rolling back to a known state when the script encounters a problem, you are great. Fortunately, Bash provides a way to run a command or a function when bash receives a UNIX signal. You can use the Trap command.

Trap command signal [signal ...]

You can link multiple signals (the list can be obtained using kill-l), but to clean up the mess, we only use three of them: Int,term and exit. You can use-as to get traps back to its original state.

Signal description

Int

Interrupt-triggered when someone uses CTRL-C to terminate a script

Term

Terminate-triggered when someone kills a script process using kill

EXIT

Exit-This is a pseudo-signal that is triggered when the script exits normally or after a set-e because of an error

When you use the lock file, you can write this:

if [!-e $lockfile]; Then

Touch $lockfile

Critical-section

RM $lockfile

Else

echo "Critical-section is already running"

Fi

When the most important part (Critical-section) is running, what happens if the script process is killed? The lock file will be dropped there, and your script won't run until it's deleted. Workaround:

if [!-e $lockfile]; Then

Trap "Rm-f $lockfile; Exit "INT term exit

Touch $lockfile

Critical-section

RM $lockfile

Trap-int term EXIT

Else

echo "Critical-section is already running"

Fi

Now when you kill the process, the lock file is deleted together. Note that the script is explicitly exited in the trap command, otherwise the script will continue to execute the command behind the trap.

Race condition (Wikipedia)

In the example of the lock file above, there is a race condition that has to be pointed out, which exists between the decision lock file and the creation lock file. One possible workaround is to redirect to nonexistent files using IO redirection and bash's noclobber (wikipedia) mode. We can do this:

if (Set-o noclobber; echo "$$" > "$lockfile") 2>/dev/null;

Then

Trap ' rm-f ' $lockfile "; Exit $? ' INT term EXIT

Critical-section

Rm-f "$lockfile"

Trap-int term EXIT

Else

echo "Failed to acquire Lockfile: $lockfile"

Echo "held by $ (cat $lockfile)"

Fi

The more complicated question is if you want to update a bunch of files and if there is a problem with the update process, can you make the script more elegant. You want to make sure that those updates are correct and which don't change at all. For example, you need a script to add users.

ADD_TO_PASSWD $user

Cp-a/etc/skel/home/$user

Chown $user/home/$user-R

This script is problematic when there is not enough disk space or the process is killed in the middle. In this case, you may wish that the user account does not exist and that his files should be deleted.

Rollback () {

DEL_FROM_PASSWD $user

If [-e/home/$user]; Then

rm-rf/home/$user

Fi

Exit

}

Trap rollback INT Term EXIT

ADD_TO_PASSWD $user

Cp-a/etc/skel/home/$user

Chown $user/home/$user-R

Trap-int term EXIT

At the end of the script you need to use the trap to close the rollback call, otherwise rollback will be called when the script exits gracefully, then the script equals nothing.

Remain atomized

Again you need to update a lot of files in the catalog, such as you need to rewrite the URL to another site's domain name. You may write:

For file in $ (find/var/www-type f-name "*.html"); Do

Perl-pi-e ' s/www.example.net/www.example.com/' $file

Done

If the modification to half of the script is problematic, part uses www.example.com, and the other part uses www.example.net. You can use Backup and trap resolution, but your site URL is inconsistent during the upgrade process.

The workaround is to make this change an atomic operation. Make a copy of the data, update the URL in the copy, and replace the current version with the copy. You need to make sure that the copy and the working version directory are on the same disk partition so you can take advantage of the Linux system, which moves the directory just to update the INODE nodes that the directory points to.

Cp-a/var/www/var/www-tmp

For file in $ (find/var/www-tmp-type-f-name "*.html"); Do

Perl-pi-e ' s/www.example.net/www.example.com/' $file

Done

Mv/var/www/var/www-old

Mv/var/www-tmp/var/www

This means that if there is a problem with the update process, the online system will not be affected. The affected time of the online system is reduced to two MV operation time, which is very short because the file system only updates the inode without actually replicating all the data.

The disadvantage of this technique is that you need twice times more disk space, and that the process of opening files for a long time takes a long time to upgrade to a new file version, and it is recommended that these processes be restarted after the update is complete. This is not a problem for Apache servers because it re-opens the file every time. You can use the lsof command to view the files that are currently open. The advantage is that you have a previous backup that comes in handy when you need to restore it.

    • This article is from: Linux Learning Network

Write a robust bash script

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.