Let's take a look at the simple technique for processing command line independent variables, and then look at the basic programming structure of bash.
Receive independent variable
In the sample program in the introductory article, we use the environment variable "$1" to reference the first command line independent variable. Similarly, you can use "$2" and "$3" to reference the second and third independent variables passed to the script. Here is an example:
#!/usr/bin/env bash echo name of script is $0 echo first argument is $1 echo second argument is $2 echo seventeenth argument is $17 echo number of arguments is $# |
In addition to the following two details, this example is not required. First, "$0" is extended to the script name called from the command line, and "$ #" is extended to the number of independent variables passed to the script. Test the above script and learn how it works by passing different types of command line independent variables.
Sometimes a single reference is requiredAllCommand Line independent variable. For this purpose, bash implements the variable "$ @", which is extended to all command line parameters separated by spaces. In the "for" loop later in this article, you will see an example of using this variable.
Back to Top
Bash programming structure
If you have used programming in a procedural language like C, Pascal, Python, or Perl, you must be familiar with the standard programming structure like the "if" Statement and "for" loop. Bash has its own version for most of these standard structures. In the following sections, we will introduce several bash structures and demonstrate the differences between these structures and those in other programming languages that you are already familiar. Don't worry if you haven't programmed much before. I provide enough information and examples to keep up with the progress of this article.
Back to Top
Convenient condition statements
If you have used C to Write File-related code, you should know: To compare a specific file, whether it is more work than another file. That's because C does not have any built-in syntax for this comparison. Two Stat () calls and two stat structures must be used for manual comparison. Instead, Bash has built-in standard file comparison operators. Therefore, it is as easy to determine whether "/tmp/myfile is readable" as to check whether "$ myvar is greater than 4.
The following table lists the most common bash comparison operators. Examples of how to correctly use each option are also provided. The example must be followed by "if. For example:
if [ -z "$myvar" ]then echo "myvar is not defined"fi |
Operator |
Description |
Example |
File comparison operator |
-EFilename |
IfFilenameYes, true |
[-E/var/log/syslog] |
-DFilename |
IfFilenameIs a directory, it is true |
[-D/tmp/mydir] |
-FFilename |
IfFilenameIs a regular file, it is true |
[-F/usr/bin/grep] |
-LFilename |
IfFilenameIs a symbolic link, it is true |
[-L/usr/bin/grep] |
-RFilename |
IfFilenameReadable, true |
[-R/var/log/syslog] |
-WFilename |
IfFilenameWritable, true |
[-W/var/mytmp.txt] |
-XFilename |
IfFilenameExecutable, true |
[-L/usr/bin/grep] |
Filename1-NTFilename2 |
IfFilename1RatioFilename2New, true |
[/Tmp/install/etc/services-nt/etc/services] |
Filename1-OtFilename2 |
IfFilename1RatioFilename2Old, true |
[/Boot/bzimage-ot ARCH/i386/boot/bzimage] |
String comparison operator(Please pay attention to the use of quotation marks. This is a good way to prevent space from disturbing the code) |
-ZString |
IfStringIf the length is zero, it is true. |
[-Z "$ myvar"] |
-NString |
IfStringNon-zero length, true |
[-N "$ myvar"] |
String1=String2 |
IfString1AndString2Same, true |
["$ Myvar" = "One Two Three"] |
String1! =String2 |
IfString1AndString2Otherwise, true |
["$ Myvar "! = "One Two Three"] |
Arithmetic comparison operator |
Num1-EQNum2 |
Equal |
[3-EQ $ mynum] |
Num1-NeNum2 |
Not equal |
[3-ne $ mynum] |
Num1-LtNum2 |
Less |
[3-lt $ mynum] |
Num1-LeNum2 |
Less than or equal |
[3-Le $ mynum] |
Num1-GTNum2 |
Greater |
[3-GT $ mynum] |
Num1-GeNum2 |
Greater than or equal |
[3-ge $ mynum] |
Sometimes there are several different methods for specific comparison. For example, the following two code segments have the same functions:
if [ "$myvar" -eq 3 ]then echo "myvar equals 3"fi if [ "$myvar" = "3" ]then echo "myvar equals 3"fi |
The above two comparisons perform the same function, but the first uses arithmetic comparison operators, and the second uses string comparison operators.
Back to Top
String comparison
Most of the time, although double quotation marks that enclose strings and string variables are not used, this is not a good idea. Why? If the environment variable happens to have a space or tabulation key, Bash will not be able to distinguish and thus it will not work properly. Here is an error comparison example:
if [ $myvar = "foo bar oni" ]then echo "yes"fi |
In the above example, if myvar is equal to "foo", the code will work as expected without printing. However, if myvar is equal to "foo bar ONI", the Code fails due to the following error:
In this case, the spaces in "$ myvar" (equal to "foo bar ONI") confuse bash. After bash expands "$ myvar", the Code is as follows:
[ foo bar oni = "foo bar oni" ] |
Bash considers that there are too many independent variables in square brackets because the environment variables are not placed in double quotes. Double quotation marks can be used to enclose string independent variables to eliminate this problem. Keep in mind that many similar programming errors will be removed if all string independent variables are enclosed in double quotation marks. Comparison of "foo bar ONI"ShouldWritten:
if [ "$myvar" = "foo bar oni" ]then echo "yes"fi |
More references
To expand environment variables, you must useDouble quotation marksInstead of being enclosed in single quotes. Single quotesDisableVariable (and history) extension.
The above code will work as expected without any unpleasant surprises.
Back to Top
Loop Structure: ""
Well, we have already talked about conditional statements. Next we should explore the bash loop structure. We will start with the standard "for" loop. Here is a simple example:
#! /Usr/bin/ENV bash for X in one two three four do echo number $ X done output: Number one number two number three number four |
What happened? The "for X" section in the "for" loop defines a new environment variable named "$ X" (also called a loop control variable ), its values are set to "one", "two", "three", and "four" in sequence ". After each assignment, run the code between "do" and "done ). In a loop, like other environment variables, use the standard variable extension syntax to reference the loop control variable "$ X ". Note that the "for" loop always receives a certain type of word list after the "in" statement. In this example, four English words are specified, but the word list can also reference files on the disk, or even file wildcards. Take a look at the following example to demonstrate how to use the standard shell wildcard:
#! /Usr/bin/ENV bash for myfile in/etc/R * do if [-d "$ myfile"] Then ECHO "$ myfile (DIR) "Else echo" $ myfile "fi done output:/etc/rc. D (DIR)/etc/resolv. conf/etc/resolv. conf ~ /Etc/RPC |
The code above lists each file starting with "R" in/etc. To do this, bash first obtains the wildcard/etc/R * Before executing the loop, then extends it, and uses the string/etc/rc. d/etc/resolv. conf/etc/resolv. conf ~ /Etc/RPC replacement. Once a loop is entered, the "-d" condition operator is used to perform two different operations based on whether the myfile is a directory. If it is a directory, "(DIR)" is appended to the output line.
You can also use multiple wildcards or even environment variables in the word list:
for x in /etc/r--? /var/lo* /home/drobbins/mystuff/* /tmp/${MYPATH}/* do cp $x /mnt/mydir done |
Bash will execute wildcards and environment variable extensions in all correct locations, and may create a very long word list.
Although all wildcard extension examples UseAbsolutePath, but you can also use the relative path, as shown below:
for x in ../* mystuff/* do echo $x is a silly file done |
In the above example, bash executes wildcard extension relative to the current working directory, just like using relative paths in the command line. Study wildcard extension. You will notice that if an absolute path is used in the wildcard, bash expands the wildcard into an absolute path list. Otherwise, bash uses the relative path in the word list. If you only reference files in the current working directory (for example, if you enter "for X in *"), the generated file list will have no prefix for path information. Remember, you can use the "basename" executable program to remove the preceding path information, as shown below:
for x in /var/log/* do echo `basename $x` is a file living in /var/log done |
Of course, it is usually convenient to execute a loop on the command line independent variables of the script. Here is an example of how to use the variable "$ @" mentioned at the beginning of this article:
#! /Usr/bin/ENV bash for thing in "$ @" Do echo you typed $ {thing }. done output: $ allargs hello there you silly you typed hello. you typed there. you typed you. you typed silly. |
Back to Top
Shell Arithmetic
Before learning another type of loop structure, you 'd better familiarize yourself with how to execute shell arithmetic. Yes, indeed: You can use the shell structure to perform simple integer operations. Bash only needs to enclose a specific arithmetic expression with "$ (" and ")" to calculate the expression. Here are some examples:
$ echo $(( 100 / 3 )) 33 $ myvar="56" $ echo $(( $myvar + 12 )) 68 $ echo $(( $myvar - $myvar ))0 $ myvar=$(( $myvar + 1 ))$ echo $myvar 57 |
Back to Top
More loop structures: "while" and ""
If the specified condition is true, the "while" statement is executed in the following format:
while [ condition ] do statements done |
The "while" statement is usually used to loop for a certain number of times. For example, the following example loops 10 times:
myvar=0 while [ $myvar -ne 10 ] do echo $myvar myvar=$(( $myvar + 1 )) done |
We can see that in the above example, the arithmetic expression is used to make the condition finally false and cause the loop to terminate.
The "until" statement provides the opposite functionality of the "while" Statement: as long as the specific condition isFalse. The following is an "until" loop with the same function as the previous "while" loop:
myvar=0 until [ $myvar -eq 10 ] do echo $myvar myvar=$(( $myvar + 1 )) done |
Back to Top
Case statement
The case statement is another convenient condition structure. Here is an example snippet:
case "${x##*.}" in gz) gzunpack ${SROOT}/${x} ;; bz2) bz2unpack ${SROOT}/${x} ;; *) echo "Archive format not recognized." exit ;; esac |
In the preceding example, bash first expands "$ {X ##*.}". In the Code, "$ X" is the name of the file. "$ {X #. *}" removes all texts except the last periods and texts in the file. Bash then compares the generated string with the value listed on the left. In this example, "$ {X #. *}" is compared with "GZ" first, then "bz2", and finally "*". If "$ {X ##. *} "if it matches any of these strings or modes, the line following") "is executed, bash then continues executing the line after the end character "esac. If no pattern or string is matched, no code line is executed. In this special code segment, at least one code block must be executed, because any string that does not match "GZ" or "bz2" will match the "*" pattern.
Back to Top
Functions and namespaces
In Bash, you can even define functions similar to other procedural languages (such as Pascal and C. In bash, functions can even receive arguments in a way similar to the command line arguments received by scripts. Let's take a look at the sample function definition and continue from there:
tarview() { echo -n "Displaying contents of $1 " if [ ${1##*.} = tar ]then echo "(uncompressed tar)" tar tvf $1 elif [ ${1##*.} = gz ]then echo "(gzip-compressed tar)" tar tzvf $1 elif [ ${1##*.} = bz2 ]then echo "(bzip2-compressed tar)" cat $1 | bzip2 -d | tar tvf -fi } |
Another situation
You can use the "case" statement to write the above Code. Do you know how to write it?
We defined a function named "tarview" above, which receives an independent variable, that is, a certain type of tar file. When executing this function, it determines the tar file type of the independent variable (uncompressed, Gzip compressed, or Bzip2 compressed), prints an information message, and then displays the content of the TAR file. You should call the above function as follows (call it from a script or command line after you input, paste, or find the function ):
$ tarview shorten.tar.gz Displaying contents of shorten.tar.gz (gzip-compressed tar) drwxr-xr-x ajr/abbot 0 1999-02-27 16:17 shorten-2.3a/ -rw-r--r-- ajr/abbot 1143 1997-09-04 04:06 shorten-2.3a/Makefile -rw-r--r-- ajr/abbot 1199 1996-02-04 12:24 shorten-2.3a/INSTALL -rw-r--r-- ajr/abbot 839 1996-05-29 00:19 shorten-2.3a/LICENSE .... |
Use them interactively
Do not forget to place the function (the above function) IN ~ /. Bashrc or ~ /. Bash_profile to use them in bash at any time.
As you can see, you can use the same mechanism as referencing the command line independent variable to reference the independent variable within the function definition. In addition, the macro "$ #" is expanded to include the number of independent variables. The only variable that may not be exactly the same is "$0", which will be extended to the string "bash" (if the function is run interactively from the shell) or the name of the script that calls the function.
Back to Top
Namespace
You often need to create environment variables in functions. Although possible, there is still a technical detail to understand. In most compilation languages (such as C), when a variable is created inside a function, the variable is placed in a separate local namespace. Therefore, if you define a function named myfunction in C and define an independent variable named "X" in the function, then, any global variable named "X" (a variable other than a function) will not be impressed by it, thus eliminating negative effects.
This is true in C, but not in bash. In bash, every time an environment variable is created inside a function, it is addedGlobalNamespace. This means that this variable will overwrite the global variables outside the function and continue to exist after the function exits:
#!/usr/bin/env bash myvar="hello" myfunc() { myvar="one two three" for x in $myvar do echo $x done } myfunc echo $myvar $x |
When running this script, it will output "One Two Three three", which shows how "$ myvar" affects global variables "$ myvar" defined in the function ", and how the cyclic control variable "$ X" continues to exist after the function exits (if the global variable "$ X" exists, it will also be affected ).
In this simple example, it is easy to locate the error and correct it by using other variable names. But this is not the correct method. The best way to solve this problem is to use the "local" command to prevent the possibility of affecting global variables from the beginning. When "local" is used to create variables in the functionLocalAnd does not affect any global variables. Here we demonstrate how to implement the above Code so that global variables are not overwritten:
#!/usr/bin/env bash myvar="hello" myfunc() { local x local myvar="one two three" for x in $myvar do echo $x done } myfunc echo $myvar $x |
This function outputs "hello" -- does not override the global variable "$ myvar", and "$ X" does not exist outside of myfunc. In the first line of the function, we create the local variable X to be used later. In the second example (local myvar = "One Two Three, we created the local variable myvar,At the same timeAssign a value to it. When defining a cyclic control variable as a local variable, it is easy to use the first form, because it cannot be said: "For local X in $ myvar ". This function does not affect any global variables. We encourage you to design some functions in this way. Only when you explicitly want to modify global variablesNoUse "local ".
Back to Top
Conclusion
We have learned the most basic bash function. Now we need to look at how to develop the entire application based on Bash. The next part is about to be discussed. Goodbye!
References
- For more information, see the original article on the developerworks global site.
- Read the introductory bash article,Developerworks"Bash instance, Part 1"
- Visit the GNU's bash Homepage
- View bash online reference manual