Script Security
All of my bash scripts start with the following lines:
The code is as follows |
Copy Code |
#!/bin/bash Set-o Nounset Set-o Errexit |
Doing so avoids two common problems:
Reference to an undefined variable (the default value is "")
Commands that failed to execute are ignored
It should be noted that some of the parameters of some Linux commands can be enforced to ignore errors such as "Mkdir-p" and "rm-f".
Also note that in the "Errexit" mode, although it is effective to catch errors, but not all failed to capture the command, in some cases, some failed commands are not detected. (For more details, please refer to this post.) )
Script functions
In bash you can define functions that, like any other command, can be used arbitrarily; they make your script more readable:
The code is as follows |
Copy Code |
Extractbashcomments () { Egrep "^#" } Cat myscript.sh | extractbashcomments | Wc comments=$ (Extractbashcomments < myscript.sh) |
There are also some examples:
code is as follows |
copy code |
Sumlines () { # iterating over Stdin-similar to awk local sum=0 ; local line= "" while read line; does sum=$ ((${sum} + ${line})) done echo ${sum} } Sumlines < Data_one _number_per_line.txt Log () { # Classic Logger local prefix= "[$ (date +%y/%m/%d%h:%m:%s)]:" echo "${prefix} $@" >&2 } Log "INFO" "a Message" |
Move your bash code as far as possible into a function, placing only the global variables, constants, and statements that are called to "main" on the outermost layer.
Variable annotation
In bash, you can make limited annotations to variables. The most important two annotations are:
Local (function internal variables)
ReadOnly (read-only variable)
The code is as follows |
Copy Code |
# A useful idiom:default_val can be overwritten # with an environment variable of the same name ReadOnly Default_val=${default_val:-7} MyFunc () { # Initialize a local variable with the global default Local Some_var=${default_val} ... } |
In this way, you can declare a variable that was not a read-only variable as a read-only variable:
The code is as follows |
Copy Code |
X=5 X=6 ReadOnly x X=7 # failure |
Try to annotate all variables in your bash script with local or readonly.
Use $ () instead of inverted single quotes (')
Inverted single quotes are hard to read and are similar in some fonts to positive single quotes. $ () can be used inline and avoids the hassle of escape characters.
The code is as follows |
Copy Code |
# both commands below print out:a-b-c-d echo "A-' echo-B ' echo c-\ ' echo d\ '" " echo "a-$ (Echo b-$ (echo D))" |
Replace with [[]] (double bracket) []
using [[]] avoids problems like the file name extension of an exception, and it can lead to a lot of grammatical improvements, and adds a lot of new features:
operator function Description
|| Logical OR (used in double brackets only)
&& logical AND (used in double brackets only)
< string comparisons (no transfer required in double brackets)
-LT Digital Comparison
= String Equal
= = globbing string comparison (only used in double brackets, reference below)
=~ uses regular expressions for string comparisons (use in double brackets only, reference below)
-N Non-empty string
-Z Empty string
-eq numbers equal
-ne numbers Range
Single bracket:
The code is as follows |
Copy Code |
["${name}" > "A"-O ${name} < "M"] |
Double Middle bracket:
The code is as follows |
Copy Code |
[["${name}" > "a" && "${name}" < "M"]] |
Regular Expression/globbing
The benefits of using double brackets are best illustrated with the following examples:
The code is as follows |
Copy Code |
t= "Abc123" [[$t ' = = abc*]] # True (globbing comparison) [[' $t ' = = ' abc* ']] # false (literal comparison) [[' $t ' =~ [abc]+[123]+]] # True (regular expression comparison) [[' $t ' =~ ' abc* ']] # false (literal comparison) |
Note that, starting with Bash version 3.2, regular and globbing expressions cannot be wrapped in quotes. If you have a space in your expression, you can store it in a variable:
The code is as follows |
Copy Code |
R= "a B +" [["A BBB" =~ $r]] # True |
String comparisons by globbing can also be used in a case statement:
The code is as follows |
Copy Code |
Case $t in abc*) <action>; Esac |
String manipulation
There are a variety of ways to manipulate strings in bash, many of which are undesirable.
Basic User
The code is as follows |
Copy Code |
f= "Path1/path2/file.ext" Len= "${#f}" # = 20 (string length) # slice operation: ${<var>:<start>} or ${<var>:<start>:<length>} slice1= "${f:6}" # = "Path2/file.ext" Slice2= "${f:6:5}" # = "path2" Slice3= "${f:-8}" # = "File.ext" (Note: "-" before a space) Pos=6 Len=5 Slice4= "${f:${pos}:${len}}" # = "path2" |
Replace operation (using globbing)
The code is as follows |
Copy Code |
f= "Path1/path2/file.ext" Single_subst= "${f/path?/x}" # = "X/path2/file.ext" Global_subst= "${f//path?/x}" # = "X/x/file.ext" # string Split ReadOnly dir_sep= "/" Array= (${f//${dir_sep}/}) Second_dir= "${arrray[1]}" # = path2 |
Remove head or tail (using globbing)
The code is as follows |
Copy Code |
f= "Path1/path2/file.ext" # Delete String headers Extension= "${f#*.}" # = ' ext ' # Remove string headers in greedy match Filename= "${f##*/}" # = "File.ext" # Delete String tail Dirname= "${f%/*}" # = "Path1/path2" # to remove the tail of a string in a greedy match root= "${f%%/*}" # = "path1" |
Avoid using temporary files
Some commands require a file name as a parameter, so you cannot use the pipe. This time < () is useful, it can accept a command and convert it into something that can be a filename or something:
#
The code is as follows |
Copy Code |
Download and compare two pages Diff < (Wget-o-URL1) < (Wget-o-URL2) |
Another very useful place is "here documents", which allows you to enter multiple lines of string on the standard input. The following ' MARKER ' can be replaced with any word.
The code is as follows |
Copy Code |
# any word can be used as a delimiter Command << MARKER ... ${var} $ (CMD) ... MARKER |
If there are no inline variable substitution operations in the text, you can wrap the first marker in single quotes:
The code is as follows |
Copy Code |
Command << ' MARKER ' ... No substitution is happening here. $ (dollar sign) is passed through verbatim. ... MARKER |
Built-in variables
Variable description
$ script Name
The nth argument passed to the script/function $n
PID for $$ scripts
$! PID of the last executed command (process running in background)
$? Exit status of previous command (Pipe command using ${pipestatus})
$# number of arguments passed to a script/function
$@ all parameters passed to the script/function (identifying each parameter)
$* all arguments passed to the script/function (take all parameters as a string)
Tips
Using $* is rarely the right choice.
$@ can handle space parameters, and the spaces between the parameters can be handled correctly.
Use $@ should be enclosed in double quotes, like "$@".
Debugging
To perform a syntax check on a script:
The code is as follows |
Copy Code |
Bash-n myscript.sh |
Trace the execution of each command in the script:
The code is as follows |
Copy Code |
Bash-v myscripts.sh |
Trace the execution of each command in the script and append the extension information:
The code is as follows |
Copy Code |
Bash-x myscript.sh |
You can use Set-o verbose and set-o xtrace to permanently specify-V and-O in the script header. This is useful when executing a script on a remote machine, using it to output remote information.
When should you not use bash scripts
Your script is too long, up to hundreds of lines
You need more complex data structures than arrays.
A complex escape problem has occurred
There are too many string operations
Less need to invoke other programs and interact with other program pipelines
Worry about performance
At this point, you should consider a scripting language, such as Python or Ruby.