It's the graduation season again, and I have to find an internship job again. . .
Recently, I was looking for an internship job on a direct employment software. I saw that there are many jobs that require Shell programming, and I don’t know much about Shell programming, so I still have half a month of school time to learn it!
Shell is a command line interpreter, which receives application or user commands, and then calls the operating system kernel to perform operations!
Shell is also a very powerful programming language, easy to write, easy to debug, and flexible.
Shell can also be said to be a scripting language!
Shell is divided into bash and sh, both of them are actually the same thing from a macro perspective;
Table of contents
1. Getting Started with Shell Script
1.1 Three execution methods of Shell script
2.1 System predefined variables
2.2.2 Variable definition rules
4.2 Common Judgment Conditions
4.2.1 Comparison between strings
4.2.2 Comparison between integers
4.2.3 Judging by file permissions
4.3 Multi-condition judgment && ||
5.3.2 for in (enhance the for loop, use a little more)
5.3.3 Difference between $* and $@
8. Comprehensive application - realize file archiving
9. Getting Started with Regular Expressions
9.2 Commonly used special characters
9.2.5 Character range (brackets): [ ]
9.3 Exercise, matching mobile phone numbers
10.1.2 Description of option parameters
10.2.2 Description of option parameters
10.2.4 gawk's built-in variables
1. Getting Started with Shell Script
Shell scripts generally start with #!/bin/bash (specify parser)
And the general script files are suffixed with .sh by default (everyone uses it like this)
# is its comment symbol;
As follows, write an output hello world! the script:
helloworld.sh
#!/bin/bash
echo "hello world!"
1.1 Three execution methods of Shell script
1.1.1 bash script relative path or absolute path or sh script relative path or absolute path
For example:
absolute path to bash + script
ygt@YGT:~/share/knowledge$ bash /home/ygt/share/knowledge/Shell/helloworld.sh
hello world!
ygt@YGT:~/share/knowledge$
bash + relative paths to scripts
ygt@YGT:~/share/knowledge/Shell$ bash ./helloworld.sh
hello world!
ygt@YGT:~/share/knowledge/Shell$
sh + absolute path to the script
ygt@YGT:~/share/knowledge/Shell$ sh /home/ygt/share/knowledge/Shell/helloworld.sh
hello world!
ygt@YGT:~/share/knowledge/Shell$
sh + relative path to the script
ygt@YGT:~/share/knowledge/Shell$ sh ./helloworld.sh
hello world!
ygt@YGT:~/share/knowledge/Shell$
1.1.2 Directly enter the absolute path or relative path of the script to execute the script (in this way, the script must have the execution permission 'x')
Give the script execution permission: chmod +x helloworld.sh
Execute the script relative to the path:
ygt@YGT:~/share/knowledge/Shell$ ./helloworld.sh
hello world!
ygt@YGT:~/share/knowledge/Shell$
Absolute path execution script:
ygt@YGT:~/share/knowledge/Shell$ /home/ygt/share/knowledge/Shell/helloworld.sh
hello world!
ygt@YGT:~/share/knowledge/Shell$
Note : The first way to execute the script, the essence is that the bash parser helps you execute the script, so the script itself can be executed without the execution permission 'x'; the second way to execute the script, the essence is that the script executes itself, so it needs to be executed Permission 'x'.
3. Use '.' or source
with the first way type
ygt@YGT:~/share/knowledge/Shell$ . helloworld.sh
hello world!
ygt@YGT:~/share/knowledge/Shell$
ygt@YGT:~/share/knowledge/Shell$ source helloworld.sh
hello world!
ygt@YGT:~/share/knowledge/Shell$
Note : The first method and the second method both open a sub-shell in the current shell to execute the script content. When the script content ends, the sub-shell closes and returns to the parent shell; the third method is in the current shell Execute scripts without opening a subshell.
The difference between opening a sub-shell and not opening a sub-shell is that the inheritance relationship of environment variables, such as the current variable set in the sub-shell, is invisible to the parent shell.
2. Variables
Variables are divided into local variables and global variables. Local variables can only be used in this shell, while global variables can be used in this shell or its sub-shells. (The system predefined variables are global variables, and the customized ones are local variables)
2.1 System predefined variables
Commonly used system variables: $HOME, $PWD, $SHELL, $USER
You can use echo or printenv to print out the value in the variable
ygt@YGT:~/share/knowledge/Shell$ printenv HOME
/home/ygt
ygt@YGT:~/share/knowledge/Shell$
ygt@YGT:~/share/knowledge/Shell$ echo $HOME
/home/ygt
ygt@YGT:~/share/knowledge/Shell$
You can use set to view all variables in the current shell;
2.2 Custom variables
2.2.1 Basic syntax
- Define variables: variable name = variable value Note that there must be no spaces before and after the = sign
- Unset variable: unset variable name
- Declare a static variable (constant): readonly variable name = variable value Note, cannot unset
2.2.2 Variable definition rules
- The variable name can be composed of letters, numbers and underscores, but cannot start with a number, and the environment variable name is recommended to be capitalized;
- There cannot be spaces on both sides of the equal sign;
- In bash, the default type of variables is string type, which cannot be directly operated by value;
- If the value of the variable has spaces, it needs to be enclosed in double quotes or single quotes.
2.2.3 Case
2.2.3.1 Define variable name
ygt@YGT:~/share/knowledge/Shell$ name=hello
ygt@YGT:~/share/knowledge/Shell$ echo $name
hello
ygt@YGT:~/share/knowledge/Shell$
2.2.3.2 Reassign the variable name
ygt@YGT:~/share/knowledge/Shell$ name=world
ygt@YGT:~/share/knowledge/Shell$ echo $name
world
ygt@YGT:~/share/knowledge/Shell$
2.2.3.3 Undo variable name
ygt@YGT:~/share/knowledge/Shell$ unset name
ygt@YGT:~/share/knowledge/Shell$ echo $name
ygt@YGT:~/share/knowledge/Shell$
2.2.3.4 Declare the static variable (constant) age, the static variable cannot be modified, nor can unset
ygt@YGT:~/share/knowledge/Shell$ readonly age=24
ygt@YGT:~/share/knowledge/Shell$ echo $age
24
ygt@YGT:~/share/knowledge/Shell$ age=25
-bash: age: 只读变量
ygt@YGT:~/share/knowledge/Shell$ unset age
-bash: unset: age: 无法取消设定: 只读 variable
ygt@YGT:~/share/knowledge/Shell$
2.2.3.5 In bash, the default type of variables is string type, which cannot perform direct numerical operations
ygt@YGT:~/share/knowledge/Shell$ sum=1+2
ygt@YGT:~/share/knowledge/Shell$ echo $sum
1+2
ygt@YGT:~/share/knowledge/Shell$
2.2.3.6 If the value of the variable has spaces, it needs to be enclosed in double quotes or single quotes
ygt@YGT:~/share/knowledge/Shell$ name="hello world!"
ygt@YGT:~/share/knowledge/Shell$ echo $name
hello world!
ygt@YGT:~/share/knowledge/Shell$
Note that all the variables created above are local variables, which can only be used in this shell!
2.2.3.7 Promote local variables to global variables, which can be used by other shell programs
export variable name
ygt@YGT:~/share/knowledge/Shell$ export sum
Output the value of sum in the helloworld.sh file
ygt@YGT:~/share/knowledge/Shell$ vim helloworld.sh
#!/bin/bash
echo "hello world!"
echo $sum
ygt@YGT:~/share/knowledge/Shell$ ./helloworld.sh
hello world!
1+2
Note that modifying the global variables of the parent shell in the sub-shell can only see the effect in the sub-shell, but cannot affect the parent shell;
For example, modify the value of sum in the helloworld.sh file, then output to view the effect, execute the helloworld.sh script, and then execute echo $sum to view the value of sum
#!/bin/bash
echo "hello world!"
echo $sum
sum=100
echo $sum
ygt@YGT:~/share/knowledge/Shell$ ./helloworld.sh
hello world!
1+2
100
ygt@YGT:~/share/knowledge/Shell$
ygt@YGT:~/share/knowledge/Shell$ echo $sum
1+2
ygt@YGT:~/share/knowledge/Shell$
It can be seen that the value of sum has not been modified, it is still 1+2
But if you use '.' or source to execute the script, you can modify it normally!
ygt@YGT:~/share/knowledge/Shell$ . helloworld.sh
hello world!
1+2
100
ygt@YGT:~/share/knowledge/Shell$
ygt@YGT:~/share/knowledge/Shell$ echo $sum
100
ygt@YGT:~/share/knowledge/Shell$
If you want to add two numbers, you can use brackets ( ) or square brackets [ ]!
ygt@YGT:~/share/knowledge/Shell$ age=$((1+2))
ygt@YGT:~/share/knowledge/Shell$ echo $age
3
ygt@YGT:~/share/knowledge/Shell$ age=$[2+3]
ygt@YGT:~/share/knowledge/Shell$ echo $age
5
ygt@YGT:~/share/knowledge/Shell$
ygt@YGT:~/share/knowledge/Shell$ a=10
ygt@YGT:~/share/knowledge/Shell$ b=20
ygt@YGT:~/share/knowledge/Shell$ age=$[a+b]
ygt@YGT:~/share/knowledge/Shell$ echo $age
30
ygt@YGT:~/share/knowledge/Shell$
ygt@YGT:~/share/knowledge/Shell$ age=$[a+10]
ygt@YGT:~/share/knowledge/Shell$ echo $age
20
ygt@YGT:~/share/knowledge/Shell$
2.3 Special variables
a = 10
Note that if you wrap the variable $a with single quotes ' ', the shell will parse it into a string $a; if you wrap the variable $a with double quotes " ", the shell will parse it into a variable $a, that is, Get the value 10.
2.3.1 $n
$n (function description: n is a number, $0 represents the name of the script, $1-$9 represents the first to ninth parameters, and more than ten parameters need to be enclosed in braces, such as ${10}, ${11}, etc.)
example:
Write a script with the following content:
#!/bin/bash
echo '以下为$n的用法'
echo "文件名:$0"
echo "第一个参数:$1"
echo "第二个参数:$2"
echo "第三个参数:$3"
ygt@YGT:~/share/knowledge/Shell$ ./special.sh one 二 3
以下为$n的用法
文件名:./special.sh
第一个参数:one
第二个参数:二
第三个参数:3
ygt@YGT:~/share/knowledge/Shell$
2.3.2 $#
$# (Function description: Get the number of all input parameters, often used in loops, to judge whether the number of parameters is correct and to strengthen the robustness of the script)
example:
Write a script with the following content:
#!/bin/bash
echo "参数个数:$#"
ygt@YGT:~/share/knowledge/Shell$ ./special.sh one 二 3
参数个数:3
ygt@YGT:~/share/knowledge/Shell$
2.3.3 $*
$* (Function description: this variable represents all the parameters in the command line, $* regards all parameters as a whole )
example:
Write a script with the following content:
#!/bin/bash
echo '输出$*:'
echo $*
ygt@YGT:~/share/knowledge/Shell$ ./special.sh one 二 3
输出$*:
one 二 3
ygt@YGT:~/share/knowledge/Shell$
2.3.4 $@
$@ (Function description: This variable represents all parameters in the command line, but $@ treats each parameter differently )
That is, what $@ gets is the list that stores all the parameters, or the array that stores all the parameters, you can use the for loop to traverse one by one to get them out (we will talk about how to traverse later)
example:
Write a script with the following content:
#!/bin/bash
echo '输出$@'
echo $@
ygt@YGT:~/share/knowledge/Shell$ ./special.sh one 二 3
输出$@
one 二 3
ygt@YGT:~/share/knowledge/Shell$
2.3.5 $?
$? (Function description: the return status of the last executed command. If the value of this variable is 0, it proves that the previous command was executed correctly; if the value of this variable is not 0 (the specific number is determined by the command itself), then Prove that the previous command was executed incorrectly)
example:
Determine whether the helloworld.sh script is executed successfully
root@YGT:/home/ygt/share/knowledge/Shell# ./helloworld.sh
hello world!
100
root@YGT:/home/ygt/share/knowledge/Shell# echo $?
0
root@YGT:/home/ygt/share/knowledge/Shell#
root@YGT:/home/ygt/share/knowledge/Shell# ./helloworld1.sh
bash: ./helloworld1.sh: 没有那个文件或目录
root@YGT:/home/ygt/share/knowledge/Shell# echo $?
127
root@YGT:/home/ygt/share/knowledge/Shell#
3. Operators
From the above, we know that Shell cannot directly perform calculations. For example, a=1+2 outputs the result of traversing a is 1+2;
Shell has two ways to run: "$((compression expression))" or "$[compression expression]"
example:
root@YGT:/home/ygt/share/knowledge/Shell# a=$((10 + 20))
root@YGT:/home/ygt/share/knowledge/Shell# echo $a
30
root@YGT:/home/ygt/share/knowledge/Shell# a=$[10*20]
root@YGT:/home/ygt/share/knowledge/Shell# echo $a
200
root@YGT:/home/ygt/share/knowledge/Shell#
root@YGT:/home/ygt/share/knowledge/Shell# a=10
root@YGT:/home/ygt/share/knowledge/Shell# b=20
root@YGT:/home/ygt/share/knowledge/Shell#
root@YGT:/home/ygt/share/knowledge/Shell# c=$(($a + $b))
root@YGT:/home/ygt/share/knowledge/Shell# echo $c
30
root@YGT:/home/ygt/share/knowledge/Shell# a=$[$b + $c]
root@YGT:/home/ygt/share/knowledge/Shell# echo $a
50
root@YGT:/home/ygt/share/knowledge/Shell#
Or c=$((a + b)) a=$[b + c] This is also possible!
Case, create a new add.sh file, input two numbers as parameters, and output after adding
#!/bin/bash
sum=$[$1+$2]
echo sum=$sum
root@YGT:/home/ygt/share/knowledge/Shell# ./add.sh 10 20
sum=30
root@YGT:/home/ygt/share/knowledge/Shell#
4. Condition judgment
4.1 Basic syntax
(1). Test conditions
(2). [Condition] (Note that there must be spaces before and after the condition)
( After the comparison is complete, use $? to view the result, 0 means match (true), 1 means no match (false) )
4.2 Common Judgment Conditions
4.2.1 Comparison between strings
(Note: If it is a comparison between strings, use the equal sign "=" to judge equality; use "!=" to judge unequal.)
4.2.1.1 = equal to
ygt@YGT:~/share/knowledge/Shell$ a=hello
ygt@YGT:~/share/knowledge/Shell$
ygt@YGT:~/share/knowledge/Shell$ test $a = hello # 注意,等号两边需要有空格
ygt@YGT:~/share/knowledge/Shell$ echo $?
0
ygt@YGT:~/share/knowledge/Shell$ test $a = Hello # 注意,等号两边需要有空格
ygt@YGT:~/share/knowledge/Shell$ echo $?
1
ygt@YGT:~/share/knowledge/Shell$
ygt@YGT:~/share/knowledge/Shell$ a=hello
ygt@YGT:~/share/knowledge/Shell$ b=world
ygt@YGT:~/share/knowledge/Shell$
ygt@YGT:~/share/knowledge/Shell$ [ $a = $b ] # 注意,[ ] 内部也需要有空格与条件分割
ygt@YGT:~/share/knowledge/Shell$ echo $?
1
ygt@YGT:~/share/knowledge/Shell$ b=hello
ygt@YGT:~/share/knowledge/Shell$ [ $a = $b ] # 注意,[ ] 内部也需要有空格与条件分割
ygt@YGT:~/share/knowledge/Shell$ echo $?
0
ygt@YGT:~/share/knowledge/Shell$
Note that if there are no spaces on both sides of the equal sign, the shell will parse it into a value, so the result will be 0 (true); when there is nothing in the condition, the result will be 1 (false), as follows:
ygt@YGT:~/share/knowledge/Shell$ a=hello
ygt@YGT:~/share/knowledge/Shell$ test $a=Hello
ygt@YGT:~/share/knowledge/Shell$ echo $?
0
ygt@YGT:~/share/knowledge/Shell$
ygt@YGT:~/share/knowledge/Shell$ a=hello
ygt@YGT:~/share/knowledge/Shell$ [ $a=hello ]
ygt@YGT:~/share/knowledge/Shell$ echo $?
0
ygt@YGT:~/share/knowledge/Shell$
ygt@YGT:~/share/knowledge/Shell$ [ sdfjogwoegjowg ]
ygt@YGT:~/share/knowledge/Shell$ echo $?
0
ygt@YGT:~/share/knowledge/Shell$
ygt@YGT:~/share/knowledge/Shell$ [ ]
ygt@YGT:~/share/knowledge/Shell$ echo $?
1
ygt@YGT:~/share/knowledge/Shell$
4.2.1.2 != not equal to
ygt@YGT:~/share/knowledge/Shell$ a=hello
ygt@YGT:~/share/knowledge/Shell$ [ $a != hello ]
ygt@YGT:~/share/knowledge/Shell$ echo $?
1
ygt@YGT:~/share/knowledge/Shell$ [ $a != Hello ]
ygt@YGT:~/share/knowledge/Shell$ echo $?
0
ygt@YGT:~/share/knowledge/Shell$
4.2.2 Comparison between integers
-eq | equal to (equal) |
- is | not equal |
-lt | less than (less than) |
- the | Less than or equal to (less equal) |
-gt | greater than |
-ge | greater than or equal to |
ygt@YGT:~$ a=10
ygt@YGT:~$ b=20
ygt@YGT:~$ [ $a -eq $b ] # 判断10是否等于20
ygt@YGT:~$ echo $?
1
ygt@YGT:~$ [ $a -lt $b ] # 判断10是否小于20
ygt@YGT:~$ echo $?
0
ygt@YGT:~$ [ 10 -ge 10 ] # 判断10是否大于等于10
ygt@YGT:~$ echo $?
0
ygt@YGT:~$
4.2.3 Judging by file permissions
-r | Determine whether the file has read permission (read) |
-w | Determine whether the file has write permission (write) |
-x | Determine whether the file has execution permission (execute) |
ygt@YGT:~$ touch test.txt
ygt@YGT:~$ ll
总用量 1
drwxr-xr-x 9 ygt ygt 4096 6月 5 23:07 ./
drwxr-xr-x 3 root root 4096 11月 16 2022 ../
-rw-rw-r-- 1 ygt ygt 0 6月 5 23:07 test.txt
ygt@YGT:~$
ygt@YGT:~$ [ -r test.txt ] # 判断test.txt文件是否有读的权限
ygt@YGT:~$ echo $?
0
ygt@YGT:~$ [ -x test.txt ] # 判断test.txt文件是否有执行的权限
ygt@YGT:~$ echo $?
1
ygt@YGT:~$
4.2.4 Judging by file type
-e | Determine whether the file exists (existence) |
-f | Determine whether the file exists and is a regular file (file) |
-d | Determine whether the file exists and is a directory (directory) |
ygt@YGT:~$ ll
总用量 2
drwxr-xr-x 10 ygt ygt 4096 6月 5 23:17 ./
drwxr-xr-x 3 root root 4096 11月 16 2022 ../
drwxrwxr-x 2 ygt ygt 4096 6月 5 23:17 test/
-rw-rw-r-- 1 ygt ygt 0 6月 5 23:07 test.txt
ygt@YGT:~$
ygt@YGT:~$ [ -e test.txt ]
ygt@YGT:~$ echo $?
0
ygt@YGT:~$ [ -e test ]
ygt@YGT:~$ echo $?
0
ygt@YGT:~$ [ -f test ]
ygt@YGT:~$ echo $?
1
ygt@YGT:~$ [ -d test ]
ygt@YGT:~$ echo $?
0
ygt@YGT:~$ [ -f test.txt ]
ygt@YGT:~$ echo $?
0
ygt@YGT:~$
4.3 Multi-condition judgment && ||
Multi-condition judgment (&& indicates that the next command will be executed only when the previous command is successfully executed; || indicates that the next command will be executed only after the previous command fails)
Can also be used for short-circuit execution!
ygt@YGT:~$ a=10
ygt@YGT:~$ [ $a -gt 5 ] && echo "$a > 5"
10 > 5
ygt@YGT:~$ [ $a -lt 5 ] || echo "$a 不小于 5"
10 不小于 5
ygt@YGT:~$
ygt@YGT:~$ [ $a -lt 20 ] && echo "$a < 20" || echo "$a >= 20"
10 < 20
ygt@YGT:~$
ygt@YGT:~$ a=30
ygt@YGT:~$ [ $a -lt 20 ] && echo "$a < 20" || echo "$a >= 20"
30 >= 20
ygt@YGT:~$
Can also be used in commands!
ygt@YGT:~$ ls || ll
5 test test.txt
ygt@YGT:~$
When the ls command is executed successfully, the subsequent ll command will not be executed!
5. Process control
Flow control is very important in any program, such as if, case, for, while;
5.1 if judgment
5.1.1 Single branch
if [ 条件判断式 ]; then # 注意,这里的 ; 是有的
程序
fi
或者
if [ 条件判断式 ]
then
程序
fi
Create a new file and write the following content: (Input an integer to determine whether it is less than 10)
#!/bin/bash
if [ $1 -lt 10 ]; then
echo "$1 < 10"
fi
if [ "$2"x = "abc"x ] # 特殊用法,即使没有参数2,也可以正常运行
then
echo "相等"
fi
ygt@YGT:~/share/knowledge/Shell$ ./process.sh 5
5 < 10
Of course, you can also use && or || to judge multiple conditions;
ygt@YGT:~/share/knowledge/Shell$ a=20
ygt@YGT:~/share/knowledge/Shell$ if [ $a -gt 18 ] && [ $a -lt 40 ]; then echo OK; fi
OK
&& and || can be replaced by -a and -o , and then the condition can be written in a []; (-a is equal to and -o is equal to or)
ygt@YGT:~/share/knowledge/Shell$ a=20
ygt@YGT:~/share/knowledge/Shell$ if [ $a -gt 18 -a $a -lt 40 ]; then echo OK; fi
OK
5.1.2 Multi-branch
if [ 条件判断式 ]; then
程序
elif [ 条件判断式 ]; then
程序
else
程序
fi
Create a new file with the following content:
#!/bin/bash
if [ $1 -lt 18 ]; then
echo "未成年"
else
echo "已成年"
fi
if [ $2 -ge 0 -a $2 -lt 60 ]; then
echo "不及格"
elif [ $2 -ge 60 -a $2 -le 90 ]; then
echo "及格"
elif [ $2 -gt 90 -a $2 -le 100 ]; then
echo "优秀"
else
echo "成绩有误"
fi
[centos7@localhost Shell]$ ./process.sh 24 70
已成年
及格
[centos7@localhost Shell]$ ./process.sh 16 -1
未成年
成绩有误
[centos7@localhost Shell]$
5.1.3 Use of (( )) in if
Use (( )) to use C-like usage in parentheses; of course, it is generally not recommended to use this way!
ygt@YGT:~/share/knowledge/Shell$ a=10
ygt@YGT:~/share/knowledge/Shell$ if (( $a > 20 )); then echo OK; else echo notOK; fi
notOK
5.2 case statement
5.2.1 Basic syntax
case $变量名 in
"值 1")
如果变量的值等于值 1,则执行程序 1
;;
"值 2")
如果变量的值等于值 2,则执行程序 2
;;
*)
如果变量的值都不是以上的值,则执行此程序(defualt)
;;
esac
Precautions:
- The case line must end with the word "in", and each pattern match must end with a closing bracket ")";
- The double semicolon ";;" indicates the end of the command sequence, which is equivalent to break in C/C++;
- The last "*)" indicates the default mode, which is equivalent to default in C/C++;
Mainly note that the above syntax is different, and other usages are consistent with C/C++ switch case usage.
5.2.2 Case
Create a new file with the following content:
#!/bin/bash
case $1 in
"1")
echo "one"
;;
"2")
echo "two"
;;
"3")
echo "three"
;;
*)
echo "other"
;;
esac
[centos7@localhost Shell]$ ./process_case.sh 2
two
5.3 for loop
There are two types of for loops, for and for in
5.3.1 for
Use (( )) to use C-like usage in parentheses; for loops can be used like this!
for (( 初始值; 循环控制条件; 变量变化 ))
do
程序
done
Create a new file and write the following: (calculate 1+...+n)
#!/bin/bash
sum=0
for (( i=1; i <= $1; i++ )); do
sum=$[ $sum + $i ]
#sum=$[ sum + i ] # 貌似这样写也是可以的
#sum=$((sum + i)) # 貌似这样写也是可以的
#let sum=sum+i
done
echo "sum = $sum"
Note that with the update of the Linux Shell, the let keyword can be used to implement the current high-level language usage
For example: let sum=sum+i let sum++ etc.
ygt@YGT:~/share/knowledge/Shell$ ./sum.sh 10
sum = 55
5.3.2 for in (enhance the for loop, use a little more)
for 变量 in 值 1 值 2 值 3…
do
程序 # 变量 的取值为 in 后面的值
done
#!/bin/bash
for os in Linux Windows Macos; do
echo $os;
done
ygt@YGT:~/share/knowledge/Shell$ ./sum.sh
Linux
Windows
Macos
ygt@YGT:~/share/knowledge/Shell$
Of course, you can also give him a collection after in, wrapped with { .. }, for example:
ygt@YGT:~/share/knowledge/Shell$ for i in {1..100}; do sum=$[ $sum + $i ]; done; echo "sum = $sum"
sum = 5050
ygt@YGT:~/share/knowledge/Shell$
5.3.3 Difference between $* and $@
Both $* and $@ represent all the parameters passed to the function or script, and when they are not included by double quotes "", all parameters are output in the form of $1 $2 ... $n.
$*: Treat parameters as a whole;
$@: treat the parameters as an independent individual
Create a new file with the following content:
(If $* and $@ in the code are not enclosed in double quotes, they will achieve the same effect, and you will not be able to see the difference)
#!/bin/bash
echo '$*:'
for pro in "$*"; do # 注意这里$*一定要用双引号括起来,就可以看出其将参数看作是一个整体
echo $pro
done
echo '$@:'
for pro in "$@"; do # 注意这里$@一定要用双引号括起来,就可以看出其将参数看作是分开的独立个体
echo $pro
done
ygt@YGT:~/share/knowledge/Shell$ ./process_for.sh 1 2 3 a b c
$*:
1 2 3 a b c
$@:
1
2
3
a
b
c
ygt@YGT:~/share/knowledge/Shell$
$* regards the parameter as a string as a whole, while $@ regards the parameter as an independent individual
5.4 while loop
while [ 条件判断式 ]
do
程序
done
#!/bin/bash
i=1
sum=0
#while (( i <= $1 )); do # 也可以使用 (( )) 替代[ ]
while [ $i -le $1 ]; do
sum=$[ $sum + $i ]
i=$[ $i + 1 ]
#let sum=sum+i
#let i++
done
echo "sum = $sum"
(You can use the let keyword to modify the usage of the high-level language!)
In the code, both (( )) and [ ] can be used interchangeably!
Generally while is used []
ygt@YGT:~/share/knowledge/Shell$ ./process_while.sh 100
sum = 5050
6. read read console input
read (option) (argument)
①Options:
-p: Specify the prompt when reading the value;
-t: Specify the waiting time (seconds) when reading the value. If -t is not added, it means waiting all the time
(After the -t time is added, the read operation will end and continue to execute)
② parameters
Variable: Specify the variable name to read the value from
Create a new file with the following content:
#!/bin/bash
read -t 7 -p "请输入您的名字:" name
#if [ "$name" == "" ]; then
if [ ! $name ]; then
echo "Name is NULL"
else
echo "Your name is $name"
fi
ygt@YGT:~/share/knowledge/Shell$ ./process_read.sh
请输入您的名字:李华
Your name is 李华
ygt@YGT:~/share/knowledge/Shell$ ./process_read.sh
请输入您的名字:Name is NULL
ygt@YGT:~/share/knowledge/Shell$
Seven, function
Small case, splice file name (date function), the format is: file name_log_timestamp
#!/bin/bash
filename="$1"_log_$(date +%s)
echo $filename
date is a system function, +%s is the parameter passed in to it, so as to get the timestamp and return
ygt@YGT:~/share/knowledge/Shell$ ./date.sh test
test_log_1686280637
ygt@YGT:~/share/knowledge/Shell$
How to call a system function: $(system function) aka: command substitution
7.1 System functions
Here are two commonly used system functions, basename and dirname;
7.1.1 basename
7.1.1.1 Basic syntax
basename [string / pathname] [suffix]
(Function description: The basename command will delete all prefixes including the last ('/') character, and then display the string.
basename can be understood as taking the file name in the path
Options: suffix is the suffix, if suffix is specified, basename will remove the suffix in pathname or string.
Note that basename can only intercept strings with /!
7.1.1.2 Case
ygt@YGT:~/share/knowledge/Shell$ basename /home/ygt/share/knowledge/Shell/date.sh
date.sh
ygt@YGT:~/share/knowledge/Shell$ basename /home/ygt/share/knowledge/Shell/date.sh .sh
date
ygt@YGT:~/share/knowledge/Shell$ basename ./date.sh
date.sh
ygt@YGT:~/share/knowledge/Shell$ basename /abcsdf/asdgwr/erg43
erg43
7.1.2 address
7.1.2.1 Basic syntax
dirname file absolute path
(Function description: Remove the file name (non-directory part) from the given file name containing the absolute path, and then return the remaining path (directory part))
dirname can be understood as the absolute path name of the file path
7.1.2.2 Case
ygt@YGT:~/share/knowledge/Shell$ dirname /home/ygt/share/knowledge/Shell/date.sh
/home/ygt/share/knowledge/Shell
ygt@YGT:~/share/knowledge/Shell$ dirname ./date.sh
.
ygt@YGT:~/share/knowledge/Shell$ dirname /sadfsg/gwgwrga/agwgg
/sadfsg/gwgwrga
There is also a more interesting way to call the script in any path, and output the full path where the script is located:
#!/bin/bash
echo $(cd $(dirname $0); pwd)
ygt@YGT:~/share/knowledge/Shell$ ./date.sh
/home/ygt/share/knowledge/Shell
ygt@YGT:~/share/knowledge/Shell$ cd /
ygt@YGT:/$ /home/ygt/share/knowledge/Shell/date.sh
/home/ygt/share/knowledge/Shell
ygt@YGT:/$ cd /home/ygt/share/knowledge/shared_bike/src/
ygt@YGT:~/share/knowledge/shared_bike/src$ ../../Shell/date.sh
/home/ygt/share/knowledge/Shell
7.2 Custom functions
7.2.1 Basic syntax
[ function ] funname [()]
{
Action;
[return int;]
}
7.2.2 Precautions
- The function must be declared before calling the function, and the shell script is run line by line. Does not compile first like other languages;
- The return value of the function can only be obtained through the $? system variable, which can be displayed and added: return return, if not added, the result of the last command will be used as the return value. return followed by the value n(0-255);
- Because the return value obtained through $? can only be between 0-255; so you can use echo to output the data, and then use $() to call the function, you can get the result of the echo output, that is, the return value of the function;
- There is no need to specify parameters when defining a function, and the parameters are passed directly when calling the function, and $1, $2...$n are used inside the function to obtain parameter values!
7.2.3 Case
Create a new file with the following content:
#!/bin/bash
function add() {
s=$[ $1 + $2 ]
return $s # 注意,只能返回0-255之间的数
}
read -p "请输入第一个整数:" a
read -p "请输入第二个整数:" b
add $a $b
echo "$a + $b = $?"
ygt@YGT:~/share/knowledge/Shell$ ./func.sh
请输入第一个整数:10
请输入第二个整数:20
10 + 20 = 30
ygt@YGT:~/share/knowledge/Shell$ ./func.sh
请输入第一个整数:100
请输入第二个整数:200
100 + 200 = 44
ygt@YGT:~/share/knowledge/Shell$
Because $? can only get the value between 0-255, so when it exceeds, the correct value cannot be returned;
In order to solve such a problem, you can use echo to output the value, and then use $() to call the function to receive it; as follows:
#!/bin/bash
function add() {
s=$[ $1 + $2 ] # s=$(( $1 + $2 ))
echo $s
}
read -p "请输入第一个整数:" a
read -p "请输入第二个整数:" b
sum=$( add $a $b )
echo "$a + $b = $sum"
ygt@YGT:~/share/knowledge/Shell$ ./func.sh
请输入第一个整数:100
请输入第二个整数:200
100 + 200 = 300
Note that if there are multiple echo outputs in the function, only the value of the last echo output can be obtained at the place where the function is called!
8. Comprehensive application - realize file archiving
This is a comprehensive application of the knowledge points in the first seven chapters above to realize the file archiving operation!
In actual production applications, it is often necessary to archive and backup important data;
Requirement: Implement a script that archives and backs up the specified directory every day, enter a directory name (without / at the end), archive and save all files in the directory by day, and append the archive date to the archive file name, and put it in /hong/ ygt down;
The archive command is used here: tar
The -c option can be added later to indicate archiving, and the -z option can be added to indicate simultaneous compression, and the resulting file extension is .tar.gz.
The script is implemented as follows:
#!/bin/bash
# 首先判断输入的参数个数是否为1
if [ $# -ne 1 ]; then
echo "参数个数错误!应输入一个参数,作为归档目录名"
exit
fi
# 从参数中获取目录名称
if [ -d $1 ]; then
echo
else
echo
echo "目录不存在!"
echo
exit
fi
DIR_NAME=$(basename $1)
DIR_PATH=$(cd $(dirname $1); pwd)
# 获取当前日期
DATE=$(date +%y%m%d)
# 定义生成的归档文件名
FILE=Shell_${DIR_NAME}_${DATE}.tar.gz
DEST=/home/ygt/$FILE
# 开始归档目录文件
echo "开始归档..."
echo
tar -zcf $DEST $DIR_PATH/$DIR_NAME
if [ $? -eq 0 ]; then
echo
echo "归档成功!"
echo "归档文件为:$DEST"
echo
else
echo "归档失败!"
echo
fi
9. Getting Started with Regular Expressions
Regular expressions use a single string to describe and match a series of strings that meet a certain grammatical rule. In many text editors, regular expressions are usually used to retrieve and replace text that matches a certain pattern. In Linux, text processing tools such as grep, sed, and awk all support pattern matching through regular expressions.
9.1 Regular matching
A string of regular expressions that contain no special characters matches itself, for example:
ygt@YGT:~/share/knowledge/Shell$ cat archive.sh | grep DATE
DATE=$(date +%y%m%d)
FILE=Shell_${DIR_NAME}_${DATE}.tar.gz
You can match the row containing DATE!
9.2 Commonly used special characters
9.2.1 ^
^ matches the beginning of a line, for example:
ygt@YGT:~/share/knowledge/Shell$ cat archive.sh | grep ^i
if [ $# -ne 1 ]; then
if [ -d $1 ]; then
if [ $? -eq 0 ]; then
ygt@YGT:~/share/knowledge/Shell$
9.2.2 $
$ matches the end of a line, for example:
ygt@YGT:~/share/knowledge/Shell$ cat archive.sh | grep gz$
FILE=Shell_${DIR_NAME}_${DATE}.tar.gz
ygt@YGT:~/share/knowledge/Shell$
9.2.3 .
. matches an arbitrary character, for example:
ygt@YGT:~/share/knowledge/Shell$ cat archive.sh | grep h.me
DEST=/home/ygt/$FILE
ygt@YGT:~/share/knowledge/Shell$
ygt@YGT:~/share/knowledge/Shell$ cat archive.sh | grep h...
if [ $# -ne 1 ]; then
echo "参数个数错误!应输入一个参数,作为归档目录名"
echo "目录不存在!"
FILE=Shell_${DIR_NAME}_${DATE}.tar.gz
DEST=/home/ygt/$FILE
echo "开始归档..."
echo "归档成功!"
echo "归档文件为:$DEST"
echo "归档失败!"
ygt@YGT:~/share/knowledge/Shell$
9.2.4 *
* is not used alone, it is used together with the previous character, which means matching the previous character 0 or more times, for example:
ygt@YGT:~/share/knowledge/Shell$ cat /etc/passwd | grep ro*t
root:x:0:0:root:/root:/bin/bash
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
ygt@YGT:~/share/knowledge/Shell$
will match all lines such as rt, rot, root, rooot, roooot, etc.
There is also + , which matches 1 or n times; ? Matches 0 or 1 times!
In addition: .* used in combination means, match any number of arbitrary characters!
9.2.5 Character range (brackets): [ ]
[ ] means match a character within a certain range, for example:
[6,8] —— match 6 or 8 [0-9]------match a number of 0-9;
[0-9]* —— match a string of numbers of any length;
[az] - match a character between az;
[az]* —— matches an alphabetic string of any length;
[ac, ef] - match any character between ac or ef;
ygt@YGT:~/share/knowledge/Shell$ echo "541rooabcbbat" | grep r[o,a,b,c]*t
541rooabcbbat
ygt@YGT:~/share/knowledge/Shell$
ygt@YGT:~/share/knowledge/Shell$ cat /etc/passwd | grep r[a-z]*t
root:x:0:0:root:/root:/bin/bash
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
ygt@YGT:~/share/knowledge/Shell$
9.2.6 \
\ means escape and will not be used alone. Since all special characters have their specific matching pattern, when we want to match a special character itself (for example, I want to find all lines containing '$'), we will encounter difficulties. At this point, we need to use escape characters and special characters together to represent the special characters themselves, for example:
ygt@YGT:~/share/knowledge/Shell$ cat archive.sh | grep '\$'
if [ $# -ne 1 ]; then
if [ -d $1 ]; then
DIR_NAME=$(basename $1)
DIR_PATH=$(cd $(dirname $1); pwd)
DATE=$(date +%y%m%d)
FILE=Shell_${DIR_NAME}_${DATE}.tar.gz
DEST=/home/ygt/$FILE
tar -zcf $DEST $DIR_PATH/$DIR_NAME
if [ $? -eq 0 ]; then
echo "归档文件为:$DEST"
ygt@YGT:~/share/knowledge/Shell$
ygt@YGT:~/share/knowledge/Shell$ cat archive.sh | grep '/\$'
DEST=/home/ygt/$FILE
tar -zcf $DEST $DIR_PATH/$DIR_NAME
ygt@YGT:~/share/knowledge/Shell$
You can match the line containing $! You can match the line containing /$!
9.3 Exercise, matching mobile phone numbers
ygt@YGT:~/share/knowledge/Shell$ echo "15712345678" | grep ^1[3,4,5,7,8][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]&
ygt@YGT:~/share/knowledge/Shell$ 15712345678
ygt@YGT:~/share/knowledge/Shell$
ygt@YGT:~/share/knowledge/Shell$ echo "15712345678" | grep -E ^1[3,4,5,7,8][0-9]{9}&
ygt@YGT:~/share/knowledge/Shell$ 15712345678
In addition, there are many other advanced usages of regular expressions, which have not been introduced here, and I will add them later when I have time to learn!
10. Text processing tools
10.1 cut
The job of cut is to "cut", specifically, it is responsible for cutting data in the file. The cut command cuts bytes, characters and fields from each line of the file and outputs these bytes, characters and fields;
10.1.1 Basic usage
cut [option parameter] filename
Description: The default delimiter is tab
10.1.2 Description of option parameters
option parameter | Function |
---|---|
-f | Column number, which column to extract |
-d | Delimiter, split columns according to the specified delimiter, the default is tab character "\t" |
-c | After cutting by character, add n to indicate which column to take, such as -c 1 |
10.1.3 Case
10.1.3.1 Prepare data
ygt@YGT:~/share/knowledge/Shell$ vim cut.txt
ygt@YGT:~/share/knowledge/Shell$ cat cut.txt
guang shen dong
zhou zhen guan
wo wo wo
lai lai lai
le le le
10.1.3.2 Split by space to get the first column
ygt@YGT:~/share/knowledge/Shell$ cut -d " " -f 1 cut.txt
guang
zhou
wo
lai
le
10.1.3.3 Split by spaces to get the second and third columns
ygt@YGT:~/share/knowledge/Shell$ cut -d " " -f 2,3 cut.txt
shen dong
zhen guan
wo wo
lai lai
le le
10.1.3.4 Cut out guang in the cut.txt file
ygt@YGT:~/share/knowledge/Shell$ cat cut.txt | grep guang | cut -d " " -f 1
guang
10.1.3.5 Cut out the user, path and bash of the /etc/passwd file
ygt@YGT:~/share/knowledge/Shell$ cat /etc/passwd | grep bash$
root:x:0:0:root:/root:/bin/bash
ygt:x:1000:1000:ygt,,,:/home/ygt:/bin/bash
ygt@YGT:~/share/knowledge/Shell$
ygt@YGT:~/share/knowledge/Shell$ cat /etc/passwd | grep bash$ | cut -d ":" -f 1,5,7
root:root:/bin/bash
ygt:ygt,,,:/bin/bash
ygt@YGT:~/share/knowledge/Shell$
10.1.3.6 Intercepting a range of columns
cat /etc/passwd | grep bash$ | cut -d ":" -f 2-4
cat /etc/passwd | grep bash$ | cut -d ":" -f -4
cat /etc/passwd | grep bash$ | cut -d ":" -f 3-
10.1.3.7 Intercept ip address
ygt@YGT:~/share/knowledge/Shell$ ifconfig | grep "广播"
inet 地址:192.168.160.128 广播:192.168.160.255 掩码:255.255.255.0
ygt@YGT:~/share/knowledge/Shell$
ygt@YGT:~/share/knowledge/Shell$ ifconfig | grep "广播" | cut -d " " -f 12 | cut -d ":" -f 2
192.168.160.128
ygt@YGT:~/share/knowledge/Shell$
10.2 gawk
A powerful text analysis tool that reads files line by line, slices each line with spaces as the default delimiter, and then analyzes the cut parts;
10.2.1 Basic usage
gawk [option parameter] '/pattern1/{action1} /pattern2/{action2}...' filename
pattern: Indicates what gawk is looking for in the data, which is the matching pattern;
action: A sequence of commands to execute when a match is found;
10.2.2 Description of option parameters
option parameter | Function |
---|---|
-F | specify the input file separator |
-v | Assign a value to a user-defined variable |
10.2.3 Case
Note: only the lines that match the pattern will execute the action
10.2.3.1 Search the /etc/passwd file for all lines beginning with the root keyword, and output column 7 of the line
ygt@YGT:~$ cat /etc/passwd | gawk -F ":" '/^root/{print $7}'
/bin/bash
10.2.3.2 Search for all lines in the /etc/passwd file that start with the root keyword, and output the first and seventh columns of the line, separated by ","
ygt@YGT:~$ cat /etc/passwd | gawk -F ":" '/^root/{print $1","$7}'
root,/bin/bash
10.2.3.3 Only display the first and seventh columns of /etc/passwd, separated by commas, and add the column name user and shell in front of all lines, and add "end!!!" to the last line
ygt@YGT:~$ cat /etc/passwd | gawk -F ":" 'BEGIN{print "user, shell"}/bash$/{print $1","$7} END{print "end!!!"}'
user, shell
root,/bin/bash
ygt,/bin/bash
end!!!
ygt@YGT:~$
Note: BEGIN is executed before all data read rows; END is executed after all data is executed.
10.2.3.4 Increase the user id in the /etc/passwd file by 1 and output
cat /etc/passwd | gawk -F ":" '{print $3}' # 这是没有+1的
cat /etc/passwd | gawk -v i=1 -F ":" '{print $3+i}' # 这是有+1的
10.2.4 gawk's built-in variables
variable | illustrate |
---|---|
FILENAME | file name |
NR | Number of records read (line number) |
NF | The number of domains in the browse record (after cutting, the number of columns) |
10.2.5 Case
10.2.5.1 Statistics /etc/passwd file name, line number of each line, number of columns of each line
ygt@YGT:~$ gawk -F : '{print "文件名:"FILENAME", 行号:"NR", 列号:"NF}' /etc/passwd
文件名:/etc/passwd, 行号:1, 列号:7
文件名:/etc/passwd, 行号:2, 列号:7
文件名:/etc/passwd, 行号:3, 列号:7
# 。。。中间省略。。。
文件名:/etc/passwd, 行号:24, 列号:7
文件名:/etc/passwd, 行号:25, 列号:7
10.2.5.2 Query the line number of the blank line in the output result of the ifconfig command
ygt@YGT:~$ ifconfig | gawk '/^$/ {print "空行:"NR}'
空行:9
空行:18
ygt@YGT:~$
11. Summary
So far, the basic introductory tutorial of Linux Shell is completed!
Although there is no great knowledge point, but after learning these, it is enough to cope with daily work!
In addition, this tutorial is learned from watching the video of "Silicon Valley" on Bilibili!