Shell Scripting: A Beginner's Guide to Automating Command Line Tasks with Bash Scripting and Shell P
- zregafevat
- Aug 19, 2023
- 6 min read
If you want to learn how to write bash shell scripts like a pro, solve real-world problems, or automate repetitive and complex tasks, read on. By the end of this course you will be able to create bash scripts with ease. You'll learn how to take tedious and repetitious tasks and turn them into programs that will save you time and simplify your life on Linux, Unix, or MAC systems. What you learn in this course can be applied to any shell, however, the focus is on the bash shell and you'll learn some really advanced bash features. Again, whether you're using bash, bourne (sh), KornShell (ksh), C shell (csh), Z shell (zsh), or even the tcsh shell, you'll be able to put what you learn in this course to good use. Also, you'll be able to use these scripts on any Linux environment including Ubuntu, Debian, Linux Mint, RedHat, Fedora, OpenSUSE, Slackware, Kali Linux and more. You're scripts will even run on other operating systems such as Apple's Mac OS X, Oracle's Solaris, IBM's AIX, HP's HP-UX, FreeBSD, NetBSD, and OpenBSD. (Sorry, this course is NOT for Windows scripting or powershell scripting.)
Exit CodesExit codes are a number between 0 and 255, which is returned by any Unix command when it returnscontrol to its parent process.Other numbers can be used, but these are treated modulo 256, so exit -10 isequivalent to exit 246, and exit 257 is equivalent to exit 1.These can be used within a shell script to change the flow of execution depending on the successor failure of commands executed. This was briefly introduced in Variables- Part II. Here we shall look in more detail in the available interpretations of exit codes.if(typeof ez_ad_units!='undefined')ez_ad_units.push([[728,90],'shellscript_sh-box-3','ezslot_6',135,'0','0']);__ez_fad_position('div-gpt-ad-shellscript_sh-box-3-0');Success is traditionally represented with exit 0; failure is normallyindicated with a non-zero exit-code. This value can indicate different reasons for failure.For example, GNU grep returns 0 on success, 1 if nomatches were found, and 2 for other errors (syntax errors, non-existent inputfiles, etc).We shall look at three different methods for checking error status, and discuss the pros andcons of each approach.Firstly, the simple approach:if(typeof ez_ad_units!='undefined')ez_ad_units.push([[336,280],'shellscript_sh-banner-1','ezslot_2',143,'0','0']);__ez_fad_position('div-gpt-ad-shellscript_sh-banner-1-0');#!/bin/sh# First attempt at checking return codesUSERNAME=`grep "^$1:" /etc/passwdcut -d":" -f1`if [ "$?" -ne "0" ]; then echo "Sorry, cannot find user $1 in /etc/passwd" exit 1fiNAME=`grep "^$1:" /etc/passwdcut -d":" -f5`HOMEDIR=`grep "^$1:" /etc/passwdcut -d":" -f6`echo "USERNAME: $USERNAME"echo "NAME: $NAME"echo "HOMEDIR: $HOMEDIR"This script works fine if you supply a valid username in /etc/passwd.However, if you enter an invalid code, it does not do what you might at firstexpect - it keeps running, and just shows:USERNAME: NAME: HOMEDIR: Why is this? As mentioned, the $? variable is set to the return codeof the last executed command. In this case, that is cut. cuthad no problems which it feels like reporting - as far as I can tell from testing it, andreading the documentation, cut returns zero whatever happens! It was fed anempty string, and did its job - returned the first field of its input, which just happenedto be the empty string.So what do we do? If we have an error here, grep will report it, not cut.Therefore, we have to test grep's return code, not cut's.if(typeof ez_ad_units!='undefined')ez_ad_units.push([[250,250],'shellscript_sh-medrectangle-4','ezslot_3',134,'0','0']);__ez_fad_position('div-gpt-ad-shellscript_sh-medrectangle-4-0');#!/bin/sh# Second attempt at checking return codesgrep "^$1:" /etc/passwd > /dev/null 2>&1if [ "$?" -ne "0" ]; then echo "Sorry, cannot find user $1 in /etc/passwd" exit 1fiUSERNAME=`grep "^$1:" /etc/passwdcut -d":" -f1`NAME=`grep "^$1:" /etc/passwdcut -d":" -f5`HOMEDIR=`grep "^$1:" /etc/passwdcut -d":" -f6`echo "USERNAME: $USERNAME"echo "NAME: $NAME"echo "HOMEDIR: $HOMEDIR"This fixes the problem for us, though at the expense of slightly longer code.if(typeof ez_ad_units!='undefined')ez_ad_units.push([[728,90],'shellscript_sh-medrectangle-3','ezslot_4',320,'0','0']);__ez_fad_position('div-gpt-ad-shellscript_sh-medrectangle-3-0');That is the basic way which textbooks might show you, but it is far from beingall there is to know about error-checking in shell scripts. This method may notbe the most suitable to your particular command-sequence, or may be unmaintainable. Below, we shallinvestigate two alternative approaches.As a second approach, we can tidy this somewhat by putting the test into a separate function,instead of littering the code with lots of 4-line tests:#!/bin/sh# A Tidier approachcheck_errs() # Function. Parameter 1 is the return code # Para. 2 is text to display on failure. if [ "$1" -ne "0" ]; then echo "ERROR # $1 : $2" # as a bonus, make our script exit with the right error code. exit $1 fi### main script starts here ###grep "^$1:" /etc/passwd > /dev/null 2>&1check_errs $? "User $1 not found in /etc/passwd"USERNAME=`grep "^$1:" /etc/passwdcut -d":" -f1`check_errs $? "Cut returned an error"echo "USERNAME: $USERNAME"check_errs $? "echo returned an error - very strange!"This allows us to test for errors 3 times, with customised error messages,without having to write 3 individual tests. By writing the test routine once.we can call it as many times as we wish, creating a more intelligent script, atvery little expense to the programmer. Perl programmers will recognise thisas being similar to the die command in Perl.As a third approach, we shall look at a simpler and cruder method. I tend touse this for building Linux kernels - simple automations which, if they go well,should just get on with it, but when things go wrong, tend to require the operatorto do something intelligent (ie, that which a script cannot do!):if(typeof ez_ad_units!='undefined')ez_ad_units.push([[970,90],'shellscript_sh-box-4','ezslot_1',120,'0','0']);__ez_fad_position('div-gpt-ad-shellscript_sh-box-4-0');#!/bin/shcd /usr/src/linux && \make dep && make bzImage && make modules && make modules_install && \cp arch/i386/boot/bzImage /boot/my-new-kernel && cp System.map /boot && \echo "Your new kernel awaits, m'lord."This script runs through the various tasks involved in building a Linuxkernel (which can take quite a while), and uses the &&operator to check for success. To do this with if would involve:#!/bin/shcd /usr/src/linuxif [ "$?" -eq "0" ]; then make dep if [ "$?" -eq "0" ]; then make bzImage if [ "$?" -eq "0" ]; then make modules if [ "$?" -eq "0" ]; then make modules_install if [ "$?" -eq "0" ]; then cp arch/i386/boot/bzImage /boot/my-new-kernel if [ "$?" -eq "0" ]; then cp System.map /boot/ if [ "$?" -eq "0" ]; then echo "Your new kernel awaits, m'lord." fi fi fi fi fi fi fifi... which I, personally, find pretty difficult to follow.The && and operators are the shell's equivalent of AND and ORtests. These can be thrown together as above, or:#!/bin/shcp /foo /bar && echo Success echo FailedThis code will either echoSuccessorFaileddepending on whether or not the cp command was successful. Look carefully at this;the construct isif(typeof ez_ad_units!='undefined')ez_ad_units.push([[970,250],'shellscript_sh-large-leaderboard-2','ezslot_5',122,'0','0']);__ez_fad_position('div-gpt-ad-shellscript_sh-large-leaderboard-2-0');command && command-to-execute-on-success command-to-execute-on-failureOnly one command can be in each part.This method is handy for simple success / fail scenarios, but if you want tocheck on the status of the echo commands themselves, it is easy to quicklybecome confused about which && and applies to whichcommand. It is also very difficult to maintain. Therefore this construct is onlyrecommended for simple sequencing of commands.In earlier versions, I had suggested that you can use a subshell to execute multiple commands depending on whether the cp command succeeded or failed:if(typeof ez_ad_units!='undefined')ez_ad_units.push([[300,250],'shellscript_sh-leader-1','ezslot_7',123,'0','0']);__ez_fad_position('div-gpt-ad-shellscript_sh-leader-1-0');cp /foo /bar && ( echo Success ; echo Success part II; ) ( echo Failed ; echo Failed part II )But in fact, Marcel found that this does not work properly. The syntax for a subshell is:( command1 ; command2; command3 )The return code of the subshell is the return code of the final command (command3 in this example). That return code will affect the overall command. So the output of this script:if(typeof ez_ad_units!='undefined')ez_ad_units.push([[250,250],'shellscript_sh-large-mobile-banner-1','ezslot_8',131,'0','0']);__ez_fad_position('div-gpt-ad-shellscript_sh-large-mobile-banner-1-0');cp /foo /bar && ( echo Success ; echo Success part II; /bin/false ) ( echo Failed ; echo Failed part II )Is that it runs the Success part (because cp succeeded, and then - because /bin/false returns failure, it also executes the Failure part:SuccessSuccess part IIFailedFailed part IISo if you need to execute multiple commands as a result of the status of some other condition, it is better (and much clearer) to use the standard if, then, else syntax.
Shell Scripting: How To Automate Command Line Tasks Using Bash Scripting And Shell Programming Ebook
The Linux Command Line takes you from your very first terminal keystrokes to writing full programs in Bash, the most popular Linux shell (or command line). Along the way you'll learn the timeless skills handed down by generations of experienced, mouse-shunning gurus: file navigation, environment configuration, command chaining, pattern matching with regular expressions, and more.
Once you overcome your initial "shell shock," you'll find that the command line is a natural and expressive way to communicate with your computer. Just don't be surprised if your mouse starts to gather dust.
Bash 101 Hacks is a downloadable eBook that contains 101 practical examples on both Bash command line and shell scripting, and I promise it will help you understand everything you need to know about Bash. 2ff7e9595c
Comments