本文共 27072 字,大约阅读时间需要 90 分钟。
A Bash script is a plain text file which contains a series of commands. These commands are a mixture of commands we would normally type ouselves on the command line (such as ls or cp for example) and commands we could type on the command line but generally wouldn’t (you’ll discover these over the next few pages). An important point to remember though is:
Anything you can run normally on the command line can be put into a script and it will do exactly the same thing. Similarly, anything you can put into a script can also be run normally on the command line and it will do exactly the same thing.
You don’t need to change anything. Just type the commands as you would normally and they will behave as they would normally. It’s just that instead of typing them at the command line we are now entering them into a plain text file. In this sense, if you know how to do stuff at the command line then you already know a fair bit in terms of Bash scripting.
#!/bin/bash# A simple variable examplemyvariable=Helloanothervar=Fredecho $myvariable $anothervarechosampledir=/etcls $sampledir
When we enclose our content in quotes we are indicating to Bash that the contents should be considered as a single item. You may use single quotes(‘) or double quotes(“”)
* Single quotes will treat every character literally * Double quotes will allow you to do substitution (that is include variables within the settting of the value)myvar='Hello World'echo $myvarHello Worldnewvar="More $myvar"echo $newvarMore Hello Worldnewvar='More $myvar'echo $newvarMore $myvar
Command substitution allows us to take the output of a command or program(what would normally be printed to the screen) and save it as the value of a variable. To do this we place it within brackets, preceded by a $ sign.
myvar=$(ls /etc | wc -l)echo There are $myvar entries in the directory /etc
Commmand substitution is nice and simple if the output of the command is a single word or line. if the output goes serveral lines then the newlines are simply removed and all the output ends up on a single line.
lsbin Documents Desktop ...Downloads public_html ...myvar=$( ls )echo $myvarbin Documents Desktop Downloads public_html ...
myvar=helloworldexport myvar...
When we export a variable is that we are telling Bash that every time a new process is created (to run another script or such) the make a copy of the variable and hand it over to the new process. So although the variables will have the same name they exist in separate process and so are unrelated to each other.
Exporting variables is a one way process. The original process may pass variables over to the new process but anything that process does with the copy of the variables has no impact on the original variables.
If we would like to ask the user for input then we use a command called read. This command takes the input and will save it into a variable.
Let’s look at a simple example:
#!/bin/bash# Ask the user for their nameecho Hello, who am I talking to?read varnameecho It\'s nice to meet you $varname
You are able to alter the behaviour of read with a variety of command line options.(See the man page for read to see all of them.) Two commonly used options however are -p which allows you to specifiy a prompt and -s which makes the input silent. This can make it easy to ask for a username and password combination like the example below:
#!/bin/bash# Ask the user for login detailsread -p 'Username: ' uservarread -sp 'Password: ' passvarechoecho Thankyou $uservar we now have your login details
So for we’ve looked at a single word as input. We can do more than that however.
#!/bin/bash# Demonstrate how read actually worksecho What cars do you like?read car1 car2 car3echo Your first car was: $car1echo Your second car was: $car2echo Your third car was: $car3output:What cars do you like?Jaguar Maserati BentleyYour first car was: JaguarYour second car was: MaseratiYour third car was: BentleyWhat cars do you like?Jaguar Maserati Bentley LotusYour first car was: JaguarYour second car was: MaseratiYour third car was: Bentley Lotus
The general mechanism is that you can supply several variable name to read. Read will then take your input and split it on whitespace.
The first item will then be assigned to the first variable name, the second item to the second variable name and so on. if there are more items than variable names then the remaining items will all be added to the last variable name. If there are less items than variable naems then the remaining variable names will be set to blank or null.It’s common in Linux to pipe a series of simple, single purpose commands together to create a larger solution tailored to our exact needs.
Bash accommodates piping and redirection by way of special files. Each process gets it’s own set of files(one for STDIN, STDOUT and STDERR respectively) and they are linked when piping or redirection is invoked.
Each process gets the following files: * STDIN - /proc//fd/0 * STDOUT - /proc//fd/1 * STDERR - /proc//fd/2To make life more convenient the system creates some shortcuts for us:
* STDIN - /dev/stdin or /proc/self/fd/0 * STDOUT - /dev/stdout or /proc/self/fd/1 * STDERR - /dev/stderr or /proc/self/fd/2 fd in the paths above stands for file descriptor.So if we would like to make our script able to process the data that is piped to it all we need to do is read the relevant file. All of the files mentioned above behave like normal file:
#!/bin/bash# A basic summary of my sales reportecho Here is a summary of the sales data:echo ====================================echocat /dev/stdin | cut -d' ' -f 2,3 | sort
Outputs:
cat salesdata.txtFred apples 20 November 4Susy oranges 5 November 7Mark watermelons 12 November 10Terry peaches 7 November 15cat salesdata.txt | ./summaryHere is a summary of the sales data:====================================apples 20oranges 5peaches 7watermelons 12
So we now have 3 methods for getting input from the user:
* Command line arguments * Read input during script execution * Accept data input has been redirection into the bash script vis STDINlet is a builtin function of Bash that allows us to do simple arithmetic.
let#!/bin/bash# Basic arithmetic using letlet a=5+4echo $a # 9let "a = 5 + 4"echo $a # 9let a++echo $a # 10let "a = 4 * 5"echo $a # 20let "a = $1 + 30"echo $a # 30 + first command line argument
Expr is similar to let except instead of saving the result to a variable it instead prints the answer. Unlike let you don’t need to enclose the expression in quotes. You also must have spaces between the items of the expression. It is also common to use expr within command substitution to save the output to a variable.
#!/bin/bash# Basic arithmetic using exprexpr 5 + 4expr "5 + 4"expr 5+4expr 5 \* $1expr 11 % 2a=$( expr 10 - 3 )echo $a # 7
Outputs:
95 + 45+46017
Notes:
* If we do put quotes around the expression then the expression will not be evaluated but printed instead. * If we do not put spaces between the items of the expression then the expression will not be evaluated but printed instead. * Some characters have a special meaning to Bash so we have to escape them (put a backslash in front of ) to remove their special meaning. * This time we’re using the expr within command substitution in order to save the result to the variable a.In the section on Variables we saw that we can save the output of a command easily to a variable. It turns out that this mechanism is also able to do basic arithmetic for us if we tweak the syntax a little. We do so by using double brackets like so:
$((expression))
Here’s an example to illustrate:
#!/bin/bash# Basic arithmetic using double parenthesesa=$(( 4 + 5 ))echo $a # 9a=$((3+5))echo $a # 8b=$(( a + 3 ))echo $b # 11b=$(( $a + 4 ))echo $b # 12(( b++ ))echo $b # 13(( b += 3 ))echo $b # 16a=$(( 4 * 5 ))echo $a # 20Outputs:981112131620
So as you can see double parentheses is quite flexible in how you format it’s expression. This is part of why we prefer this method. As double parentheses is builtin to Bash it also runs slightly efficiently.
This isn’t really arithmetic but it can be quite useful. If you want to find out the length of a variable (how many characters) you can do the following:
${ #vaiable}
Here’s an example:
#!/bin/bash# Show the length of a variable.a='Hello World'echo ${#a} # 11b=4953echo ${#b} # 4Outputs:114
Important Concepts:
* Arithmetic - There are several ways in which to do arithmetic in Bash scripts. Double parentheses is the preferred method. * Formatting - When doing arithmetic, the presence or absence of space (and quotes) is often important.Bash if statements are very useful. In this section of our Bash script tutorial you will learn the ways you may use if statements in your Bash script to help automate tasks.
A basic if statement effectively say, if a particular test is true, then perform a given set of actions. if it is not true then don’t perform those actions. If follows the format below:
if []then fi
Here’s an example:
#!/bin/bash# Basic if statementif [ $1 -gt 100 ]thenecho Hey that\'s a large number.pwdfidate
Tips: It is always good practice to test your script with input that cover the different scenarios that are possible.
The square brackets ([]) in the if statement above are actually a reference to the command test. This means that all of the operators that test allows may be used in here as well. Look up the man page for test to see all of the possible operators, but some of the more common ones are list below:
Operator | Description |
---|---|
! EXPRESSION | The EXPRESSION is false. |
-n STRING | The length of STRING is greater than zero. |
-z STRING | The lengh of STRING is zero (ie it is empty). |
STRING1 = STRING2 | STRING1 is equal to STRING2 |
STRING1 != STRING2 | STRING1 is not equal to STRING2 |
INTEGER1 -eq INTEGER2 | INTEGER1 is numerically equal to INTEGER2 |
INTEGER1 -gt INTEGER2 | INTEGER1 is numerically greater than INTEGER2 |
INTEGER1 -lt INTEGER2 | INTEGER1 is numerically less than INTEGER2 |
-d FILE | FILE exists and is a directory. |
-e FILE | FILE exists. |
-r FILE | FILE exists and the read permission is granted. |
-s FILE | FILE exists and it’s size is greater than zero (ie. it is not empty). |
-w FILE | FILE exists and the write permission is granted. |
-x FILE | FILE exists and the execute permission is granted. |
A few points to note:
* = is slightly different to -eq. [ 001 = 1 ] will return false as = does a string comparison(ie. character for character the same) whereas -eq does a numerical comparison meaning [ 001 -eq 1] will return true. * When we refer to FILE above we are actually meaning path. Remember that a path may be absolute or relative and may refer to a file or a directory * Because [] is just a reference to the command test we may experiment and trouble shoot with test on the command line to make sure our understanding of its behaviour is correcttest 001 = 1echo $?1test 001 -eq 1echo $?0touch myfiletest -s myfileecho $?1ls /etc > myfiletest -s myfileecho $?0
Here is an example:
#!/bin/bash# Nested if statementsif [ $1 -gt 100 ]then echo Hey that is a large number. if (( $1 % 2 == 0 )) then echo And is also an even number. fifi
Let’s break it down:
* if (( $1 % 2 == 0 )) This is a ligth variation on the if statement. If we would like to check an expression then we may use the double brackets just like we did for variables.Tips: You can nest as many if statement as you like but as a general rule of thumb if you need to nest more than 3 levels deep then you should probably have a think about reorganising your logic.
Sometimes we want to perform a certain set of actions if a statement is true, and another set of actions if it is false. We can accommodate this with the else mechanism
if []then else fi
Example:
#!/bin/bash# else exampleif [ $# -eq 1 ]then nl $1else nl /dev/stdinfi
Sometimes we may have a series of conditions that may lead to different paths
if []then elif [ ] then else fi
Example:
#!/bin/bash# elif statementsif [ $1 -ge 18 ]then echo You may go to the party.elif [ $2 == 'yes' ]then echo You may go to the party but be back before midnight.else echo You may not go to the party.fi
Sometimes we only want to do something if multiple conditions are met. Other times we would like to perform the action if one of several condition is met. We can accommodate these with boolean operators.
and - &&or - ||
Examples:
One:#!/bin/bash# and exampleif [ -r $1 ] && [ -s $1 ]then echo This file is useful.fiTwo:#!/bin/bash# or exampleif [ $USER == 'bob' ] || [ $USER == 'andy' ]then ls -alhelse lsfi
Sometimes we may wish to take different paths based upon a variable matching a series of patterns.
casein ) ;; ) ;;esac
Example:
#!/bin/bash# case examplecase $1 in start) echo starting ;; stop) echo stoping ;; restart) echo restarting ;; *) echo do not know ;;esac
Now let’s look at a slightly more complex example where patterns are used a bit more.
disk_useage.sh#!/bin/bash# Print a message about disk useage.space_free=$( df -h | awk '{ print $5 }' | sort -n | tail -n 1 | sed 's/%//' )case $space_free in [1-5]*) echo Plenty of disk space available ;; [6-7]*) echo There could be a problem in the near future ;; 8*) echo Maybe we should look at clearing out old files ;; 9*) echo We could have a serious problem on our hands soon ;; *) echo Something is not quite right here ;;esac
Bash loops are very useful. In this section of our Bash script tutorial we’ll look at the different loop formats available to us as well as discuss when and why you may want to use each of them
There are 3 basic loop structures in Bash scripting which we’ll look at below. There are also a few statements which we can use to control the loops operation.
One of the easiest loops to work with is while loops.
while []do done
Example:
#!/bin/bash# Basic while loopcounter=1while [ $counter -le 10 ]do echo $counter // Using the double brackets // we can increase the value of counter by 1. ((counter++)) doneecho All doneOutputs:12345678910All done
The until loop is quite similar to the while loop. The difference is that it will execute the commands within it until the test becomes true.
until []do done
Example:
#!/bin/bash# Basic until loopcounter=1until [ $counter -gt 10 ]do echo $counter ((counter++))doneecho All done
The for loop is a little bit different from the previous two loops.
It has the following syntax:for var indo
done
The for loop will take each item in the list(in order, one after the other). assign that item as the value of the variable var, execute the commands between do and done then go back to the top, grab the next item in the list and then repeat over.
The list is defined as a series of strings, separated by spaces. Example:#!/bin/bash# Basic for loopnames='Stan Kyle Cartman'for name in $namesdo echo $namedoneecho All done
We can also process a series of numbers.
#!/bin/bash# Basic range in for loop// It's important when specifying a range like this that there // are no spaces present between the curly brackets. If there // are then it not be seen as a range but as a list of items.for value in { 1..5}do echo $valuedoneecho All doneOutput:12345
One of the more useful applications of for loop is in the processing of a set of files. To do this we may use wildcards. Let’s say we want to convert a series of .html files over to .php files.
#!/bin/bash# Make a php copy of any html filesfor value in $1/*.htmldo cp $value $1/$( basename -s .html $value ).phpdone
Break :
The break statement tells Bash to leave the loop straight away.#!/bin/bash# Make a backup set of filesfor value in $1/*do used=$( df $1 | tail -1 | awk '{ print $5 }' | sed 's/%//' ) if [ $used -gt 90 ] then echo Low disk space 1>&2 break fi cp $value $1/backup/done
Continue:
The continue statement tells Bash to stop running through this iteration of the loop and begin the next iteration.#!/bin/bash# Make a backup set of filesfor value in $1/*do if [ ! -r $value ] then echo $value not readable 1>&2 continue fi cp $value $1/backup/done
The select mechanism allows you to create a menu system. It has the following format:
select var indo
done
When invoked it will take all the items in list (similar to other loops this is a spaces separated set of items) and present them on the screen with a number before each item. A prompt will be printed after this allowing user to select a number. When they select a number and hit enter the corresponding the value will be assigned to the variable var and the commands between the do and done will run. Once finished a prompt will be displayed again so the user may select another option.
A few points to note:
* No error checking is done. If the user enters something other than a number or a number not corresponding to an item then var becomes null(empty). * If the user hits enter without entering any data then the list of options will be displayed again. * The loop will be ended when an EOF signal is entered or the break statement is issued. * You may change the system variable PS3 to change the prompt that is displayed.Here is a simple example to illustrate it’s usage:
#!/bin/bash# A simple menu systemnames='Kyle Cartman Stan Quit'// Change the value of the system variable PS3 so that the // prompt is set to something a little more descriptive.PS3='Select character: 'select name in $namesdo if [ $name == 'Quit' ] then break fi echo Hello $namedoneecho Bye
Functions in Bash Scripting are a great way to reuse code. In this section of our Bash scripting tutorial you’ll learn how they work and what you can do with them.
Creating a function is fairly easy. They may be written in two different formats:
function_name () {}orfunction function_name { }
A few points to note:
Let’s look at a simple example:
#!/bin/bash# Basic functionprint_something () { echo Hello I am a function}print_somethingprint_something\Outputs:./function_example.shHello I am a functionHello I am a function
It is often case that we would like the function to process some data for us. We may send data to the function in a similar way to passing command line arguments to a script. We supply the arguments directly after the function name. Within the function they are accessible as 1, 2, etc.
#!/bin/bash# Passing arguments to a functionprint_something () {echo Hello $1}print_something Marsprint_something JupiterOutputs:Hello MarsHello Jupiter
Most other programming language have the concept of a return value for function, a means for the function to send data back to the original calling location. Bash function does’t allow us to do this. They do however allow us to set a return status. Similar to how a program or command exits with an exit status which indicates whether it succeeded or not. We use the keyword return to indicate a return status.
#!/bin/bash# Setting a return status for a functionprint_something () {echo Hello $1return 5}print_something Marsprint_something Jupiterecho The previous function has a return value of $?Outputs:./return_status_example.shHello MarsHello JupiterThe previous function has a return value of 5
Let’s break it down:
Notes:
One way to get around this is to use command substitution and have the function print the result (and only the result).
#!/bin/bash# Setting a return value to a functionlines_in_file () {cat $1 | wc -l}num_lines=$( lines_in_file $1 )echo The file $1 has $num_lines lines in it.Outputs:cat myfile.txtTomatoLettuceCapsicum./return_hack.sh myfile.txtThe file myfile.txt has 3 lines in it.
Let’s break it down:
Scope refers to which part of a script can see which variables. By default a variable is global. This means that it is visible everywhere in the script. We may also create a variable as a local variable. When we create a local variable within a function. It is only visible within that function. To do that we use the keyword “local” in front of the variable the first time we set it’s value.
local var_name=
It is generally considered good practice to use the local variables within functions so as to keep everything within the function contained. This way variables are safer from being inadvertently modified by another part of the script which happens to have a variable with the same name.
#!/bin/bash# Experimenting with variable scopevar_change () {local var1='local 1'echo Inside function: var1 is $var1 : var2 is $var2var1='changed again'var2='2 changed again'}var1='global 1'var2='global 2'echo Before function call: var1 is $var1 : var2 is $var2var_changeecho After function call: var1 is $var1 : var2 is $var2Outputs:./local_variables.shBefore function call: var1 is global 1 : var2 is global 2Inside function: var1 is local 1 : var2 is global 2After function call: var1 is global 1 : var2 is 2 changed again
Tips:
Always use local variables within functions. Use global variables as a last resort and consider if there is a better way to do it before using them.It is possible to name a function as the same name as the command you would normally use on the command line. This allows us to create a wrapper. eg. Maybe every time we call the ls command in our script, what we actually want is ls -lh. We could do the following:
#!/bin/bash# Create a wrapper around the command lsls () {command ls -lh}ls
Sometimes you maybe want to use functions that defined in another script file. To solve this issue, you can use “.” or source keyword to import a file which the functions are defined in
Here is an example:
#!/bin/bash. /home/username/yourscript.sh # The file that you want to importorsource /home/username/yourscript.sh # The file that you want to import# do something ......
This is the final section in the tutorial and I’d like to use it to discuss a very important topic (which is often neglected) the user interface.
When most people think about the user interface they think about the bits the end user see and how they interact with the tool. For Bash scripts I like to think about the layout and structure of the commends inside the script as well. Bash scripts are often small tools used to automate tedious and repeated tasks. They are always readable by the user and often modified to suit changing requirements. Therefore the ease with which the user may modify and extend the script is also very important.
TPut is a command which allows you to control the cursor on the terminal and the format of content that is printed. It is quite a powerful and complex tool so I’ll introduce some of the basics here but leave it up to you to do further research.
Here is an example printing a message in the middle of the screen.
#!/bin/bash# Print message in center of terminal# tput cols will tell us how many columns the terminal hascols=$( tput cols )# tput lines will tell us how many lines the terminal has.rows=$( tput lines )# Take all the command line arguments and assign them to a single variable messagemessage=$@# Find out how many characters in the string message.input_length=${#message}half_input_length=$(( $input_length / 2 ))middle_row=$(( $rows / 2 ))middle_col=$(( ($cols / 2) - $half_input_length ))# tput clear will clear the terminaltput clear# tput cup will place the cursor at the given row and column.tput cup $middle_row $middle_col# tput bold will make everything printed to the screen bold.tput boldecho $@# tput sgr0 will turn bold off (and any other changes we may have made)tput sgr0# Place the prompt at the bottom of the screen.tput cup $( tput lines ) 0
Outputs:
user@bash: ./center_message.sh Hello there Hello thereuser@bash:
转载地址:http://ieyvb.baihongyu.com/