1. Shell overview
Shell is a command interpreter that receives application/user commands and then invokes the operating system kernel.
Shell is a powerful programming language, easy to write, easy to debug, and flexible.
- The shell parsers provided by Linux are
cat /etc/shells
- The relationship between bash and sh
cd /bin
ll | grep bash
[root@lys bin]# echo $SHELL
/bin/bash
2. Getting Started with Shell Scripting
(1) Script format
The script starts with #!/bin/bash (specify parser)
(2) The first shell script helloworld.sh
touch helloworld.sh
vim helloworld.sh
# 内容
# !/bin/bash
echo "hellow world"
(3) Common execution methods of scripts
- bash or sh + the relative path or absolute path of the script (no need to give the script +x permission) (restart a process to execute the bash command)
sh ./helloworld.sh
bash ./helloworld.sh
- Use the absolute path or relative path of the input script to execute the script (must have executable permission + x) (essentially use the current bash process to execute the command)
./helloworld.sh
- [Understand] Add "." or source before the path of the script
source helloworld.sh
. helloworld.sh
reason:
The first two methods are to find the one in the current shell to open a subshell to execute the script content. When the script content ends, the subshell relationship returns to the parent shell.
The third method, which is to add "." or source before the script path, can make the script content executed in the current shell without opening a subshell. This is why we need to source it every time we modify the /etc/profile file.
The difference between opening a subshell and not opening a subshell is that the integration relationship of environment variables, such as the current variable set in the subshell, is not visible to the parent shell.
child shell
[root@lys shell]# ps -f
UID PID PPID C STIME TTY TIME CMD
root 1907 10679 0 12:46 pts/1 00:00:00 ps -f
root 10679 10672 0 09:47 pts/1 00:00:00 -bash
[root@lys shell]# bash
[root@lys shell]# ps -f
UID PID PPID C STIME TTY TIME CMD
root 1970 10679 0 12:46 pts/1 00:00:00 bash
root 2006 1970 0 12:46 pts/1 00:00:00 ps -f
root 10679 10672 0 09:47 pts/1 00:00:00 -bash
[root@lys shell]# exit
exit
[root@lys shell]# ps -f
UID PID PPID C STIME TTY TIME CMD
root 2034 10679 0 12:46 pts/1 00:00:00 ps -f
root 10679 10672 0 09:47 pts/1 00:00:00 -bash
3. Variables
3.1 System predefined variables
Common System Variables
H O M E 、 HOME、 HOME、PWD、 S H E L L 、 SHELL、 SHELL、USER
(1) View the value of the system variable
echo $HOME
(2) Display all variables in the current shell:
env
set # 包含所有系统自定义和用户自定义的变量
printenv $USER
printenv USER
3.2 Custom variables
(1) Basic grammar
- Define variables: variable name = variable, note that there must be no spaces before and after =
- Undo variable: usset variable name
- Declare static variable: readonly variable, note: cannot unset
(2) Variable definition rules
- Variable names can be composed of letters, numbers and underscores, but cannot start with a number. It is recommended that the environment variable name be capitalized.
- There can be no spaces on either side of the equal sign
- In bash, the default type of variables is string type, and numerical operations cannot be performed.
- If the value of the variable has spaces, it needs to be enclosed in double quotes or single quotes.
[root@lys shell]# a=2
[root@lys shell]# echo $a
2
# 提升为全部变量
[root@lys shell]# export a
new_var='hello linux'
vim helloworld.sh
# 追加
echo $new_var
sh helloworld.sh
#发现没有值
# 使用一下命令就有值
. hellworld.sh
sourcce hellowrld.sh
calculate
a=$((1+5))
a=$[1+5]
Define a read-only variable
readonly=5f
undo variable
unset a
3.3 Special variables
3.3.1 $n
$nn is a number, $0 represents the name of the script, $1-9 represents the first to ninth parameters, and parameters above ten need to be marked with braces, such as 9 represents the first to ninth parameters, and parameters above ten It needs to be marked with braces, such as9 represents the first to ninth parameters, and more than ten parameters need to be marked with braces, such as {10}
vim helloworld.sh
echo "hello,$1"
# 执行
sh helloworld.sh a
# 输出
hello,a
Example 2
#!/bin/bash
echo '======$n====='
echo script name: $0
echo 1st paramater: $1
echo 2nd paramater: $2
[root@lys shell]# sh parameter.sh 0 1 2
======$n=====
script name: parameter.sh
1st paramater: 0
2nd paramater: 1
# 获取调用文件名称
basename
3.3.2 $#
$# Get the number of all input parameters, often used in loops, to determine whether the number of parameters is correct and to enhance the robustness of the script
vim parameter.sh
#!/bin/bash
echo '======$n====='
echo script name: $0
echo 1st paramater: $1
echo 2nd paramater: $2
echo '======$#====='
echo paramter numbers: $#
sh parameter.sh ab cd
# 结果
======$n=====
script name: parameter.sh
1st paramater: ab
2nd paramater: cd
======$#=====
paramter numbers: 2
3.3.3 ∗ 、 *、 ∗、@
∗ This variable represents all the parameters in the command line, * This variable represents all the parameters in the command line,∗ This variable represents all the parameters in the command line, *Treat all the parameters as a whole
The @@ This variable also represents all the parameters in the command line, but @ treats each parameter differently
vim paramter.sh
#!/bin/bash
echo '======$n====='
echo script name: $0
echo 1st paramater: $1
echo 2nd paramater: $2
echo '======$#====='
echo paramter numbers: $#
echo $*
echo $@
[root@lys shell]# sh parameter.sh ab cd
======$n=====
script name: parameter.sh
1st paramater: ab
2nd paramater: cd
======$#=====
paramter numbers: 2
ab cd
ab cd
3.3.4 $?
$? 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 this variable is non-zero (the specific number is determined by the command itself), it proves that the previous command was executed incorrectly)
[root@lys shell]# ./helloworld.sh
helloworld
hello,
[root@lys shell]# echo $?
0
# 如果错误执行,就非0
[root@lys shell]# $?
-bash: 0: command not found
[root@lys shell]# echo $?
127
4 operators
$((expression)) or $[expression]
Calculate (2+3)*4
[root@lys shell]# echo $[(2+3)*4]
20
expr uses
expr 1 + 2
3
# 乘法需转义
expr 5 \* 2
10
command substitution
[root@lys shell]# a=$(expr 2 + 2)
[root@lys shell]# echo $a
4
[root@lys shell]# a=`expr 5 + 2`
[root@lys shell]# echo $a
7
additive script
#!/bin/bash
sum=$[$1 + $2]
echo $sum
5 condition judgment
basic grammar
test condition
[ condition] (Note that there must be spaces before and after the condition)
Note: The condition is true if it is not empty, [lys] returns true, [ ] returns false
Common Judgment Conditions
(1) Comparison between two integers
-eq equal to (equal)
-ne is not equal to (not equal)
-lt less than (less than)
-le less than or equal to (less equal)
-gt greater than (greater than)
-ge greater than or equal to (greater equal)
Note: If it is a comparison between strings, use the equal sign "=" to judge equality; use "!=" to judge unequal.
(2) Judge according to file permissions
-r has read permission (read)
-w has write permission (write)
-x has permission to execute (execute)
# 判断文件是否有可执行权限
[root@lys shell]# [ -x helloworld.sh ]
[root@lys shell]# echo $?
0
# 结果0代表有
(3) Judging by file type
-e file exists (existence)
-f file exists and is a regular file (file)
-d file exists and is a directory (directory)
Case Practice
(1) Whether 23 is greater than or equal to 22
$ [ 23 -ge 22 ]
(2) Determine whether the file has executable permissions
$ [ -x helloworld.sh ]
(3) Whether the file exists
$ [ -e helloworld.sh]
6 Process control
6.1 if judgment
(1) Single branch
if [条件判断]; then
程序
fi
or
if [ 条件判断式 ]
then
程序
fi
if [ "$1"x = "lys"x ]
then
echo 'welcome, lys'
fi
if [ $a -gt 18 -a $a -lt 35]; then echo OK; fi
-a and
-o or
multi-branch
if [ 条件判断式 ]
then
程序
if [ $2 -lt 18 ]
then
echo "未成年人"
elif [ $2 -lt 35 ]
then
echo "中年人"
else
echo "成年人"
fi
6.2 case statement
basic grammar
case $变量名 in
"值1")
;;
"值2")
;;
*)
如果变量的值都不是以上的值,则执行此程序
;;
esac
Precautions:
(1) The end of the case line must be the word "in", and each pattern match must end with brackets ")"
(2) The double semicolon ";;" indicates the end of the command sequence, which is equivalent to java's breakl
(3) The last "*)" indicates the default mode, which is equivalent to default in java
case $1 in
1)
echo "one"
;;
2)
echo "two"
;;
3)
echo "three"
;;
*)
echo "number else"
;;
esac
sh case_test.sh 1
6.3 for loop
basic grammar
for ((初始值; 循环控制条件; 变量变化 ))
do
程序
done
#!/bin/bash
for (( i=1; i <= $1 ; i++ ))
do
sum=$[ $sum + $i ]
done;
echo $sum
for os in linux windows macos; do echo $os; done
for i in {
1..100}; do sum=$[$sum+$i]; done; echo $sum
for with ∗ and * and∗ and @
echo '$#'
for para in $*
do
echo $para
done
echo '$@'
for para in $@
do
echo $para
done
[root@lys shell]# sh for_test2.sh 1 2 3
$#
1
2
3
$@
1
2
3
If surrounded by quotes
echo '$#'
for para in "$*"
do
echo $para
done
echo '$@'
for para in "$@"
do
echo $para
done
[root@lys shell]# sh for_test2.sh 1 2 3
$#
1 2 3
$@
1
2
3
6.4 while loop
while [ 条件判断式 ]
do
程序
done
The while loop realizes 1+ 100
i=1
sum=0
while [ $i -le 100 ]
do
sum=$[ $sum + $i ]
i=$[$i + 1]
# let sum+=i
# let i++ let 实现方式
done;
echo $sum
7 read reads console input
basic grammar
read (options) (parameters)
-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
Parameters:
Variable: Specify the variable name to read the value
Case Practice
Prompt within 7 seconds, read the name entered by the console
read -t 10 -p "请输入您的名称:" name
echo "welcome $name"
8 functions
8.1 System functions
8.1.1 basename
basename [string/pathname] [suffix] 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.
filename="$1"_log_$(date +%s)
echo $filename
basename basic usage
[root@lys shell]# basename helloworld.sh
helloworld.sh
[root@lys shell]# basename helloworld.sh .sh
helloworld
8.1.2 address
dirname file absolute path Get 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
Get the directory name of hellowolrd.sh
[root@lys shell]# dirname /java-project/shell/helloworld.sh
/java-project/shell
8.2 Custom functions
[function] funname[()]
{
Action;
[return int;]
}
Skills
(1) The function must be declared before calling the function, and the shell script is run line by line. Doesn't compile first like other languages.
(2) 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 debriefing n (0–255)
Calculate the sum of two numbers
function add(){
s=$[$1+$2]
echo "sum=$s"
}
read -p "请输入第一个整数" a
read -p "请输入第二个整数" b
add $a $b
use#? The maximum can only return 255
function add(){
s=$[$1+$2]
return $s
}
read -p "请输入第一个整数" a
read -p "请输入第二个整数" b
add $a $b
echo "sum="$?
Use $() to get the value
function add(){
s=$[$1+$2]
echo $s
}
read -p "请输入第一个整数" a
read -p "请输入第二个整数" b
sum=$(add $a $b)
echo "sum=$sum"
11 Comprehensive case
11.1 Archive files
In actual production, it is often necessary to archive and backup important data.
Requirements: 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 /root /archive.
The archive command is used here: tar
You can add -c to indicate archiving. Adding the -z option means compressing at the same time, and the resulting file suffix is tar.gz
Script implementation:
#!/bin/bash
#首先判断输入参数个数是否为1
if [ $# -ne 1 ]
then
echo "参数个数错误!应该输入一个参数,作为归档目录"
exit
fi
# 从参数中获取目录名称
if [ -d $1 ]
then
echo
else
echo
echo "目录不存在!"
exit
fi
DIR_NAME=$(basename $1)
DIR_PATH=$(cd $(dirname $1); pwd)
# 获取当前日期
DATE=$(date +%y%m%d)
# 生成的归档文件名称
FILE=archive_${DIR_NAME}_$DATE.tar.gz
DEST=/root/archive/$FILE
# 开始归档
echo "开始归档..."
echo
tar -zcf $DEST $DIR_PATH/$DIR_NAME
if [ $? -eq 0 ]
then
echo
echo "归档成功"
echo "归档文件为:$DEST"
else
echo "归档出现问题"
echo
fi
exit
implement
mkdir /root/archive
sh tar.sh ../shell/
Timed execution
crontab -e
0 2 * * * /java-project/shell/tar.sh /java-project/shell
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 do not contain regular characters matches itself, for example:
cat /etc/passwd | grep lys
will match all lines containing lys
9.2 Commonly used special characters
-
Special characters: ^
^ matches the beginning of a line, for example:
cat /etc/passwd | grep ^a
will match all lines starting with a
-
Special characters: $
$ matches the end of a line, for example:
cat /etc/passwd | grep t$
find empty lines
cat helloworld.sh | grep ^$
-
Special characters:.
cat /etc/passwd | grep r..t
will match all lines containing rabbit, rbbt, rxdt, root, etc.
-
Special characters:*
* Not used alone, it is used in conjunction with the previous character to indicate the previous character 0 or more times, for example
cat /etc/passwd | grep ro*t
Will match rt, rot, root, roooor, etc.
.* will match all
-
Character range (brackets): []
[] means match a character in a range, for example
[6,8]-------match 6 or 8,
[0-9]------Match a 0-9 number
[0-9]*-----Match a string of numbers of any length
[az] — match a string between az
[az]* ------ matches an alphabetic string of any length
[az,ef]-- matches ac, or any character between ef
cat /etc/passwd | grep r[a,b,c]*t
Will match rt, rat, raat, etc.
6 Special characters: \
\Identifies escape and will not be used alone. Since all special characters have their specific matching patterns, when we want to match a special character itself (for example, I want to find all lines containing '$'), we need to use escaped special characters to represent special the character itself, e.g.
cat /etc/passwd | grep 'a\$b'
will match all lines containing a$b. Mainly need to use single quotes to enclose the expression,
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 a file and outputs these bytes, characters, and fields.
basic usage
cut [option parameter] filename
Description: The default delimiter is tab
option parameter | Function |
---|---|
-f | Column number, which column in advance |
-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 |
data preparation
dong shen
guan zhen
wo wo
lai lai
le le
Cut the first column of cut.txt
cut -d " " -f 1 cut.sh
intercept the second column
cut -d " " -f 2 cut.sh
Intercept interval
cut -d " " -f 1-4 cut.sh
cut -d " " -f -4 cut.sh
cut -d " " -f 4- cut.sh
cut guan in cut
cat cut.sh | grep guan | cut -d " " -f 1
Select the system PATH variable value, all paths after the second ":" start:
echo $PATH
/java-project/jdk1.8.0_321/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bi
echo $PATH | cut -d ":" -f 3-
/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
IP address printed after cutting ifconfig
ifconfig ens33 | grep netmask | cut -d " " -f 10
10.2 awk
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.
basic usage
awk [option parameter] '/pattern1/{action}' '/patten2/{action2}' filename
pattern: Indicates what awk is looking for in the data, which is the matching pattern
action: A series of commands executed when matching content
option parameter | Function |
---|---|
-F | specify the input file separator |
-v | Assign a value to a user-defined variable |
Case Practice
(1) Data preparation
Search for all lines starting with the root keyword in the passwd file, and output column 7 of the line
#cut 实现
cat /etc/passwd | grep ^root | cut -d ";" -f 7
# awk实现
cat /etc/passwd | awk -F ":" '/^root/ {print $7}'
ps -ef | awk '{print $}'
# 打印文件大小
ll -l | awk '{print $5}'
Search for all lines starting with the root keyword in the passwd file, and output the first and seventh columns of the line, separated by "," in the middle.
cat /etc/passwd | awk -F ":" '/^root/ {print $1","$7}'
(4) Only the first and seventh columns of /etc/passwd are displayed, separated by commas, and the column name "begin" is added in front of all lines and "end" is added to the last line
cat /etc/passwd | awk -F ":" 'BEGIN{print "begin"}{print $1","$7}END{print "end"}'
(5) Increase the user id in the passwd file by 1 and output
cat /etc/passwd | awk -F ":" '{print $3+1}'
# 使用变量
cat /etc/passwd | awk -v i=1 -F ":" '{print $3+i}'
Awk's built-in variables
variable | illustrate |
---|---|
FILENAME | file name |
NR | Number of records read (line number) |
DF | The number of domains in the browse record (after cutting, the number of columns) |
Case Practice
(1) Count the passwd file name, the line number of each line, and the number of columns of each line
cat /etc/passwd | awk -F ":" '{print "文件名 :"FILENAME "行号:" NR "列数: "NF }'
(2) Query the line number of the blank line in the ifconfig command and the output result
ifconfig | awk '/^$/ {print "空行: " NR}'
11 Comprehensive application
We can use the mesg and write tools that come with Linux to send messages to other users.
Requirement: To implement a script that quickly sends a message to a certain user, enter the user name as the first parameter, followed by the message to be sent directly. The script needs to detect whether the user is logged in to the system, whether the message function is enabled, and whether the currently sent message is empty.
Pre-knowledge
who 查看有多少控制台
[root@bogon logs]# who
root pts/0 2022-05-26 17:34 (124.64.252.49)
root pts/1 2022-05-27 10:12 (120.244.202.117)
root pts/2 2022-05-27 11:37 (58.34.52.34)
root pts/5 2022-05-27 10:16 (120.244.202.117)
root pts/6 2022-05-27 19:10 (120.245.102.201)
root pts/8 2022-05-25 10:18 (124.64.252.49)
# 向某个控制台发出消息
write root pts/1
hi
The script is implemented as follows:
#!/bin/bash/
# 查看用户是否登录
login_user=$(who | grep -i -m 1 $1 | awk '{print $1}' )
if [ -z $login_user ]
then
echo "$1 不在线!"
echo "脚本退出"
exit
fi
# 查看用户是否开启消息功能
is_allowed=$(who -T | grep -i -m 1 $1 | awk '{print $2}' )
if [ $is_allowed != "+" ]
then
echo "$1 没有开启消息功能"
echo "脚本退出..."
exit
fi
# 确认是否有消息发送
if [ -z $2 ]
then
echo "没有消息发送"
echo "脚本退出"
exit
fi
# 从参数中获取要发送的消息
whole_msg=$(echo $* | cut -d " " -f 2-)
# 获取用户登录的终端
user_terminal=$(who | grep -i -m 1 $1 | awk '{print $2}' )
# 写入要发送的消息
echo $whole_msg | write $login_user $user_terminal
if [ $? != 0 ]
then
echo "发送失败!"
else
echo "发送成功!"
fi
exit
get file size
filename="XXXX.jar"
filesize=`ls -l $filename | awk '{ print $5 }'`
filesize=$(stat -c%s "$filename")