Linux学习:Shell脚本(第12章)

Shell可以进行类似程序 (program) 的撰写,并且不需要经过编译 (compile) 就能够执行。

 shell script 是利用 shell 的功能所写的一个“程序 (program)”,这个程序是使用纯文本文件,将一些 shell 的语法与指令(含外部指令)写在里面, 搭配正则表达式、管线命令与数据流重导向等功能,以达到我们所想要的处理目的

1 脚本程序的攥写

[mcb@localhost ~]$ mkdir bin
[mcb@localhost ~]$ cd ./bin
[mcb@localhost bin]$ vim hello.sh
#!/bin/bash    #“ #! ”是用来宣告 shell 的
# Program:
#       This program shows "Hello World!" in your screen.
# History:
# 2023/06/21 MCB First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "Hello World! \a \n"
exit 0
[mcb@localhost bin]$ sh hello.sh
Hello World!

第一行 #!/bin/bash 在宣告这个 script 使用的 shell 名称: 因为我们使用的是 bash ,所以,必须要以“ #!/bin/bash ”来宣告这个文件内的语法使用 bash 的语法!

1.1 脚本编写习惯

在脚本的文件头处记录好: 

  •  script 的功能;
  •  script 的版本信息;
  •  script 的作者与联络方式;
  •  script 的版权声明方式;
  •  script 的 History (历史纪录);
  •  script 内较特殊的指令,使用“绝对路径”的方式来下达;
  •  script 运行时需要的环境变量预先宣告与设置。

2 shell脚本练习

2.1 交互式脚本 

使用read命令编写一个脚本,它可以让用户输入:1.firstname与2.lastname,最后在屏幕上显示:【your full name is:】的内容

[mcb@localhost bin]$ vim showname.sh
#!/bin/bash
# Program:
#       Wser inputs his first name and last name. Program shows his full name.
#history:
#2023/06/21 MCB First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "Please input your first name: " firstname      #提示使用者输入
read -p "Please input your last name: " lastname        #提示使用者输入
echo -e "\nYou full name is: ${firstname} ${lastname}"  #结果由屏幕输出

[mcb@localhost bin]$ sh showname.sh
Please input your first name: Changbao
Please input your last name: Ma

You full name is: Changbao Ma

利用shell脚本创建三个不同时间的文件 

[mcb@localhost bin]$ vim create_3_filename.sh
#!/bin/bash
# Program:
#       Program create three files.which named by user's input and date command.
# History:
# 2023/06/21    MCB     First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# 1.让使用者输入文件名称,并取得filename这个变量
echo -e "I will use touch command to create three files."       #纯粹显示信息
read -p "Please input your filename: " fileuser                 #提示使用者输入
#2.为了避免使用者随意按下enter,利用变量功能分析文件名是否有设置
filename=${fileuser:-"filename"}        #开始判断是否有配置文件名
#3.开始利用date命令来取得所需要的文件名
date1=$(date --date='2 days ago' +%Y%m%d)
date2=$(date --date='1 days ago' +%Y%m%d)
date3=$(date +%Y%m%d)
file1=${filename}${date1}
file2=${filename}${date2}
file3=${filename}${date3}
#4.建立文件名
touch "${file1}"
touch "${file2}"
touch "${file3}"

[mcb@localhost bin]$ sh create_3_filename.sh
I will use touch command to create three files.
Please input your filename: file
[mcb@localhost bin]$ ls
create_3_filename.sh  file20230619  file20230620  file20230621  hello.sh  showname.sh
:-是一起的;fileuser 如果有值的话,就用所拥有的值赋予给filename变量;无值的话,就把filenname赋予给fileuser,再赋予给filename变量

 数值运算:简单的加减乘除

[mcb@localhost bin]$ vim multiplying.sh
#!/bin/bash
# Program:
#       User input 2 integer numbers; program will cross these two numbers.
# History:
# 2023/06/21    MCB     First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "you should input two numbers,and i will multiplying them!\n"
read -p "first number:" firstnum
read -p "second number:" secnum
declare -i num=${firstnum}*${secnum}
echo -e "\nthe result of ${firstnum}x${secnum} is ==>${num}"

[mcb@localhost bin]$ sh multiplying.sh
you should input two numbers,and i will multiplying them!

first number:10
second number:20

the result of 10x20 is ==>200

其中:

declare -i num=${firstnum}*${secnum}
echo -e "\nthe result of ${firstnum}x${secnum} is ==>${num}"

可以替换成

total=$((${firstnum}*${secnum}))
echo -e "\nthe result of ${firstnum}x${secnum} is ==>${total}"

推荐使用:var=$((运算内容))

bc: 含有小数的运算

利用 bc 命令(bc为计算器命令,可进入命令行也可利用管道,第四章有说明)可以实现含有小数的运算

[mcb@localhost bin]$ echo "3.14*5.26" | bc
16.51

2.2 脚本执行方式差异(source、sh script、./script)

直接执行脚本 sh filename.sh 其实是通过子进程执行,子进程产生的变量不会传递到父进程

利用 source filename.sh 会直接在父进程中执行,脚本结束后变量仍然存在

利用source来执行脚本是在父进程中执行,脚本结束后变量仍然存在,如下:

[mcb@localhost bin]$ source showname.sh
Please input your first name: Changbao
Please input your last name: Ma

You full name is: Changbao Ma

[mcb@localhost bin]$ echo ${firatname} ${lastname}
Changbao Ma

 3. 判断式

3.1 test命令

用来检查某个文件是否存在,返回值需要通过echo $?来查看

[mcb@localhost bin]$ test -e /wm && echo "exist" || echo "not exist"
not exist

test所有参数详见p396;

shell中test命令的用法详解https://blog.csdn.net/qq_36417677/article/details/104395917?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168735753816800215031012%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=168735753816800215031012&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-104395917-null-null.142%5Ev88%5Einsert_down1,239%5Ev2%5Einsert_chatgpt&utm_term=test%E5%91%BD%E4%BB%A4&spm=1018.2226.3001.4187

3.2 判断符号 [ ]

[mcb@localhost bin]$ [ -z "${MCB}"]; echo $?
0
//用于判断变量是否为空,非空则返回1,上面由于变量MCB为空,所以返回了0
  • 中括号内的每个组件都需要有空格来分隔
  • 中括号内的变量,最好都以双引号括起来
  • 中括号内的常数,最好都以单或双引号括起来
[mcb@localhost bin]$ name="maico"
[mcb@localhost bin]$ [ ${name} == "magic"]

$name不用双引号括起来,就会默认为 [ maico == "abc" ]

3.3 shell 脚本的默认变量($0、$1...)

重新启动系统的网络,可以这样做

[mcb@localhost ~]$ file /etc/init.d/network
/etc/init.d/network: Bourne-Again shell script, ASCII text executable
# 使用 file 来查询后,系统告知这个文件是个 bash 的可执行 script 喔! 
[mcb@localhost ~]$ /etc/init.d/network restart  

restart 是重新启动的意思,上面的指令可以『重新启动 /etc/init.d/network 这支程序』的意思

通过read 功能虽然可以给予一些变量去执行不同任务,但存在的问题是你得要手动由键盘输入一些判断式;而透过指令后面接参数, 那么一个指令就能够处理完毕而不需要手动再次输入一些变量行为:如下

/path/to/scriptname opt1 opt2 opt3 opt4
   $0               $1    $2   $3   $4 

执行的脚本档名为 $0 这个变量,第一个接的参数就是 $1 

  •  $# ::代表后接的参数『 个数』,以上为例这里显示『 4 』  
  •  $@ :代表『 "$1" 2" 3" 4" 』之意,每个变量是独立的 (用双引号括起来 );  
  •  $* :代表『 "$1 c$2 c$3 c$4" $4" 』,其中 c 为分隔字符, 默认空格键为分隔字符, 所以本例中代表『 "$1 $2 $3 $4"  』 之意。

例题:执行一个可以携带参数的 script ,执行该脚本后屏幕会显示如下的数据:
程序的文件名是什么?
共有几个参数?
若参数的个小于 2 则告知使用者参数量太少
全部的参数内容是什么?
第一个参数是什么?
第二个参数是什么?

[mcb@localhost ~]$ vim how_paras.sh
#!/bin/bash
# Program:
#        Program shows the script name, parameters...
# History:
# 2023/06/22    mcb     first release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo "the script name is        ==>${0}"
echo "total paramter number is==>$#"
[ "$#" -lt 2 ] && echo "The number of parameter is less than 2. stop here."&&exit 0
echo "your whole paraneter is ==> $@"
echo "The 1st parameter ==> ${1}"
echo "The 2nd parameter ==> ${2}"

执行结果:

[mcb@localhost ~]$ sh how_paras.sh mcb mn gm
the script name is      ==>how_paras.sh
total paramter number is==>3
your whole paraneter is ==> mcb mn gm
The 1st parameter ==> mcb
The 2nd parameter ==> mn

4 条件判断式

4.1 if...then

4.1.1 单层、简单条件判断式

if [ 条件判断式 ]; then
    当条件判断式成立时,可以进行的指令工作内容;
fi  <==将 if 反过来写,就成为 fi ,结束 if 之意;
[ "${yn}" == "Y" -o "${yn}" == "y" ] 
上式可替换为
[ "${yn}" == "Y" ] || [ "${yn}" == "y" ]
其中:-o在test命令中表示两个条件任意一个成立,就可以返回true
[mcb@localhost ~]$ vim ans_yn.sh
#!/bin/bash
# Program:
#       This program shows the user's choice
# History:
# 2023/06/22    mcb   First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "Please input (Y/N): " yn
if [ "${yn}" == "Y" ] ||[  "${yn}" == "y" ]; then
    echo "OK, continue"
    exit 0
fi
if [ "${yn}" == "N" ] || [ "${yn}" == "n" ]; then
    echo "Oh, interrupt!"
    exit 0
fi
echo "I don't know what your choice is" && exit 0

运行结果:
[mcb@localhost ~]$ sh ans_yn.sh
Please input (Y/N): y
OK, continue

4.1.2 多重、复杂条件判断式:if[ ];then...elif[ ];then...else..

# 一个条件判断,分成功进行与失败进行 (else)
if [ 条件判断式 ]; then
    当条件判断式成立时,可以进行的指令工作内容;
else
    当条件判断式不成立时,可以进行的指令工作内容;
fi

  更复杂情况时:

# 多个条件判断 (if ... elif ... elif ... else) 分多种不同情况执行
if [ 条件判断式一 ]; then
    当条件判断式一成立时,可以进行的指令工作内容;
elif [ 条件判断式二 ]; then
    当条件判断式二成立时,可以进行的指令工作内容;
else
    当条件判断式一与二均不成立时,可以进行的指令工作内容;
fi

netstat: 用来查询目前主机有打开的网络服务端口

[mcb@localhost bin]$ netstat -tuln
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 0.0.0.0:111             0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN
tcp6       0      0 :::111                  :::*                    LISTEN
tcp6       0      0 :::22                   :::*                    LISTEN
tcp6       0      0 ::1:631                 :::*                    LISTEN
tcp6       0      0 ::1:25                  :::*                    LISTEN
udp        0      0 0.0.0.0:111             0.0.0.0:*
udp        0      0 0.0.0.0:5353            0.0.0.0:*
udp        0      0 0.0.0.0:745             0.0.0.0:*
udp        0      0 0.0.0.0:67              0.0.0.0:*
udp        0      0 0.0.0.0:68              0.0.0.0:*
udp        0      0 0.0.0.0:53321           0.0.0.0:*
udp6       0      0 :::111                  :::*
udp6       0      0 :::745                  :::*

常见的 port 与相关网络服务的关系是:

  • 80: WWW
  • 22: ssh
  • 21: ftp
  • 25: mail
  • 111: RPC(远端程序调用)
  • 631: CUPS(打印服务功能)

如何通过 netstat 去侦测我的主机是否有打开21, 22, 25及 80这四个主要的网络服务端口?

[mcb@localhost bin]$ vim netstat.sh
#!/bin/bash
# Program:
#       using nastat ang grep to detect WWW,SSH,FTP and MAIL services.
# History:
# 2023/06/22    mcb   First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
#1.先写一些告知操作
echo "Now I will detect your linux server's services!"
echo -e "The www, ftp, ssh, and mail(smtp) will be detect!\n"
#2.开始执行一些测试任务,并且输出一些信息
testfile=/dev/shm/netstat_checking.txt
netstat -tuln > ${testfile}
testing=$(grep ":80" ${testfile})
if [ "${testing}" != " "]; then
        echo "WWW is running in your system."
fi
testing=$(grep ":22" ${testfile})
if [ "${testing}" != " "]; then
        echo "SSH is running in your system."
fi
testing=$(grep ":21" ${testfile})
if [ "${testing}" != " "]; then
        echo "FTP is running in your system."
fi
testing=$(grep ":25" ${testfile})
if [ "${testing}" != " "]; then
        echo "MAIL is running in your system."
fi

        echo "FTP is running in your system."
fi
testing=$(grep ":25" ${testfile})
if[ "${testing}" != ""];then
        echo "MAIL is running in your system."
fi

注意:if与[ ]之间有空格,if [ ]; 

grep在多个文件中检索某个字符串

命令格式:

  • grep "被查找的字符串t" filename1 filename2 filename3 ...
  • grep "被查找的字符串" *.log

Linux命令中的$()和${}的区别_$ ${}_liaowenxiong的博客-CSDN博客https://blog.csdn.net/liaowenxiong/article/details/121056826

4.2 case ..... esac 判断

case  $变量名称 in  <==关键字为 case ,还有变量前有钱字号
  "第一个变量内容")   <==每个变量内容建议用双引号括起来,关键字则为小括号 )
    程序段
    ;;            <==每个类别结尾使用两个连续的分号来处理!
  "第二个变量内容")
    程序段
    ;;
  *)                  <==最后一个变量内容都会用 * 来代表所有其他值
    不包含第一个变量内容与第二个变量内容的其他程序执行段
    exit 1
    ;;
esac                  <==最终的 case 结尾!“反过来写”思考一下!
[mcb@localhost bin]$ vim hello_1.sh
#!/bin/bash
# program
#       this script only accept the following paraments: one two three!
# History
# 2023/06/22    MCB     First release
path=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo "This program will print your selection !"
read -p "Input your choice" choice
#case ${1} in  可以替换
#echo "This program will print your selection !"
#read -p "Input your choice" choice
case ${choice} in
 "one")
        echo "your choice is one"
        ;;
 "two")
         echo "your choice is two"
        ;;
 "three")
         echo "your choice is three"
        ;;
*)
        echo "your input is error!"
        ;;
esac

运行结果:
[mcb@localhost bin]$ sh hello_1.sh
This program will print your selection !
Input your choice: one
your choice is one

4.3 function功能

function fname() 
{
    程序段
}
[mcb@localhost bin]$ vim show.sh
#!/bin/bash
# program
#       this script only accept the following paraments: one two three!
# History
# 2023/06/22    MCB     First release
path=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
function printit(){
echo -n "your choice is "       #加上-n可以不换行在同一行显示
}
echo "This program will print your selection !"
read -p "Input your choice: " choice
#case ${1} in  可以替换
#echo "This program will print your selection !"
#read -p "Input your choice" choice
case ${choice} in
 "one")
        printit; echo ${choice} | tr 'a-z' 'A-Z'
        ;;
 "two")
        printit; echo ${choice} | tr 'a-z' 'A-Z'
        ;;
 "three")
         printit; echo ${choice} | tr 'a-z' 'A-Z'
        ;;
*)
        echo "your input is error!"
        ;;
esac

运行结果:
[mcb@localhost bin]$ sh show.sh
This program will print your selection !
Input your choice: two
your choice is TWO

补充:tr命令详解

tr命令详解https://blog.csdn.net/m0_37814112/article/details/80492076?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168742230516800225562266%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=168742230516800225562266&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-2-80492076-null-null.142%5Ev88%5Einsert_down1,239%5Ev2%5Einsert_chatgpt&utm_term=tr%E5%91%BD%E4%BB%A4&spm=1018.2226.3001.4187

5 loop 循环

5.1 while do done, until do done (不定循环)

while [ condition ]  <==中括号内的状态就是判断式
do           <==do 是循环的开始!
    程序段落
done         <==done 是循环的结束

当 condition 条件成立时,就进行循环,直到 condition 的条件不成立才停止的意思。还有另外一种不定循环的方式:

[mcb@localhost bin]$ vim yes_to_stop.sh
#!/bin/bash
# program
#       repeat question until user input correct answer!
# History
# 2023/06/22    MCB     First release
path=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "please input yes or YES to stop this program: " yn
while [ "${yn}" != "yes" -a "${yn}" != "YES" ]
do
        read -p "Please input yes or YES to stop this program: " yn
done
echo "ok! you input the correct answer."


运行结果:
[mcb@localhost bin]$ sh yes_to_stop.sh
Please input yes or YES to stop this program: yes
ok! you input the correct answer.
until [ condition ]
do
    程序段落
done

这种方式恰恰与 while 相反,它说的是“当 condition 条件成立时,就终止循环, 否则就持续进行循环的程序段。”

[mcb@localhost bin]$ vim yes_to_stop1.sh
#!/bin/bash
# program
#       repeat question until user input correct answer!
# History
# 2023/06/22    MCB     First release
path=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
until [ "${yn}" == "yes" ] || [ "${yn}" == "YES" ]
do
        read -p "Please input yes or YES to stop this program: " yn
done
echo "ok! you input the correct answer."

运行结果:
[mcb@localhost bin]$ sh yes_to_stop1.sh
Please input yes or YES to stop this program: no
Please input yes or YES to stop this program: yes
ok! you input the correct answer.

$()、$(())、${}、(())区别https://blog.csdn.net/K_Ssunny/article/details/70154288?ops_request_misc=&request_id=&biz_id=102&utm_term=$()%E3%80%81$(())%E4%B8%8E$%7B%7D%E7%9A%84%E5%8C%BA%E5%88%AB&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-70154288.142%5Ev88%5Einsert_down1,239%5Ev2%5Einsert_chatgpt&spm=1018.2226.3001.4187

求1-100的和:

[mcb@localhost bin]$ vim yes_to_stop.sh
#!/bin/bash
# program
#       对1-100进行求和!
# History
# 2023/06/22    MCB     First release
path=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
i=0
s=0
while [ "${i}" != "100" ]
do
        i=$((${i}+1))
        s=$((${s}+${i}))
done
echo "ok! the result of 1+2+...100= $s"

运行结果:
[mcb@localhost bin]$ sh yes_to_stop.sh
ok! the result of 1+2+...100= 5050

5.2 for...do...done (固定循环)

相对于 while, until 的循环方式是必须要“符合某个条件”的状态, for 这种语法,则是“ 已经知道要进行几次循环”的状态。

for var in con1 con2 con3 ...
do
    程序段
done

以上面的例子来说,这个 $var 的变量内容在循环工作时:

  1. 第一次循环时, $var 的内容为 con1 ;
  2. 第二次循环时, $var 的内容为 con2 ;
  3. 第三次循环时, $var 的内容为 con3 ;
  4. ....

由于系统上面的各种帐号都是写在 /etc/passwd 内的第一个字段,你能不能通过管线命令的 cut 捉出单纯的帐号名称后,以 id 分别检查使用者的识别码与特殊参数呢?由于不同的 Linux 系统上面的帐号都不一样!此时实际去捉 /etc/passwd 并使用循环处理,就是一个可行的方案了!程序可以如下:

[[email protected] bin]$ vim userid.sh
#!/bin/bash
# Program
#       Use id, finger command to check system account's information.
# History
# 2015/07/17    VBird   first release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
users=$(cut -d ':' -f1 /etc/passwd)    # 撷取帐号名称
for username in ${users}               # 开始循环进行!
do
        id ${username}
done

除了使用 $(seq 1 100) 之外,你也可以直接使用 bash 的内置机制来处理喔!可以使用 {1..100} 来取代 $(seq 1 100) ! 那个大括号内的前面/后面用两个字符,中间以两个小数点来代表连续出现的意思!例如要持续输出 a, b, c...g 的话, 就可以使用“ echo {a..g} ”这样的表示方式!

5.3 for...do...done 的数值处理

推荐使用以下方法:

for (( 初始值; 限制值; 执行步阶 ))
do
    程序段
done

这种语法适合于数值方式的运算当中,在 for 后面的括号内的三串内容意义为:

  • 初始值:某个变量在循环当中的起始值,直接以类似 i=1 设置好;
  • 限制值:当变量的值在这个限制值的范围内,就继续进行循环。例如 i<=100;
  • 执行步阶:每作一次循环时,变量的变化量。例如 i=i+1。
[mcb@localhost bin]$ vim cal_1_100.sh
#!/bin/bash
# program
#       对1-100进行求和!
# History
# 2023/06/22    MCB     First release
path=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "请输入一个数字,将计算从1到该数字的累加值: " num
s=0
for (( i=1; i<=$num; i=i+1 ))
do
        s=$((${s}+${i}))
done
echo "the results is: ${s}"

运行结果:
[mcb@localhost bin]$ sh cal_1_100.sh
请输入一个数字,将计算从1到该数字的累加值: 20
the results is: 210

6 shell script 的追踪与 debug

直接以 bash 的相关参数来进行判断可能产生的语法错误

sh [-nvx] scripts.sh
选项与参数:
-n  :不要执行 script,仅查询语法的问题;
-v  :再执行 sccript 前,先将 scripts 的内容输出到屏幕上;
-x  :将使用到的 script 内容显示到屏幕上,这是很有用的参数!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值