shell学习

linux常用目录:
/ 根目录
/bin 存放用户级的工具(可执行文件)
/boot 启动目录,存放启动文件
/dev 设备目录,系统在此创建设备节点
/etc 系统配置文件目录
/home 主目录
/lib 库目录,存放系统和应用的库目录
/media 媒体目录,一般可移动设备挂载在此
/mnt 挂载目录
/opt 可选目录,存放一些可选软件
/root root用户的主目录
/sbin 系统二进制目录,存放系统或者管理员级的工具
/tmp 临时目录
/usr 用户安装软件的目录
/var 可变目录,存放日志等
==========================================
命令行参数,有三种不同风格的参数:
unix风格参数,前面加单破折号
BSD风格参数,前面不加破折线
GNU风格参数,前面加双破折线
==========================================
find 和 grep的区别:
find是在文件系统中查找是否存在某一个文件
grep是在某一个文件中查找是否存在某一个字符串
==========================================
解压缩数据:
bzip2工具:
bzip2命令用于压缩直接压缩文件,但是压缩后的文件会替换源文件
bunzip2命令用于解压文件,解压后的文件会替换压缩文件
bzcat命令用于查看压缩文件的内容

gzip工具:
gzip命令用于直接压缩文件,压缩后的文件会替换源文件
gunzip命令用于解压文件,解压后的文件会替换源文件
gzcat命令用于查看压缩文件的内容

zip工具(最方便使用):
zip命令:zip -r 压缩文件名 原始文件名(或者文件夹名)
unzip命令解压文件:unzip 压缩文件名

打包工具,在压缩文件之前通常需要对若干文件进行打包tar
tar -cvf test.tar test/ test2/ 打包文件
tat -xvf test.tar 解包文件

遇到tgz结尾的文件,使用tar -zxvf 压缩包来解压缩
==========================================
环境变量:
printenv命令用于打印环境变量,也可以使用echo $HOME来打印指定的环境变量

set显示所有的环境变量,包括局部和全局的

shell脚本内的变量就是局部环境变量

取得某一个变量的值要在变量的前面加上$,给某个变量复制,那么=两边不能有空格

创建局部变量之后,可以使用export让这个变量变成全局变量

使用unset来删除变量

一些内置的变量:
BASH 运行当前shell实例的全路径名
BASH_ALIASES 当前已设置别名的关联数组
BASH_ARGC 含有传给子函数或shell脚本的参数总数的可变数组
BASH_ARGV 含有传给子函数或设立了脚本的参数的可变数组
BASH_CMDS shell执行过的命令和所在位置的关联数组
BASH_COMMAND shell正在执行或者将要执行的命令
BASH_ENV 设置了的话,每个shell脚本会在执行之前先尝试运行下这个变量定义的启动文件
BASH_EXECUTION_STRING
--未完待续

定位环境变量:
每当登录linux的时候,bash shell会作为登录shell启动。登录shell会从4个不同的启动文件里读取命令:
/etc/profile
$HOME/.bash_profile
$HOME/.bash_login
$HOME/.profile
用户可以在$HOME/.bash_profile,$HOME/.bash_login,$HOME/.profile中添加属于自己的环境变量,而在/etc/profile中添加属于全局的环境变量

或者也可以在$HOME/.bashrc中添加属于自己的变量

====================================================

数组:
数组的定义:myarray=(var1 var2 var3),数组使用括号括起,数组的变量之间使用空格隔开

取得数组的第一个元素:$myarray活在${myarray[0]}
取得数组的所有元素:${myarray[*]}
=====================================================

变量(命令)别名:
alias -p命令可以查看系统中已经存在的别名
alias可以设置命令(变量)的别名:alias li="ls -il",但是他们通常只在定义他们的shell进程中有效
=====================================================
终端上的复制粘贴:ctrl+shift+c是复制,ctrl+shift+v是粘贴
=====================================================
用户和组:
useradd命令可以查看或者添加用户

useradd -m username 可以使用默认参数创建一个用户
userdel -r username 删除账户

passwd username 为账户修改密码

groupadd name 新增组

usermod -G groupname username 可以把某一个用户添加到某个组中

====================================================
文件权限:
umask命令可以设置用户创建文件和目录的默认权限

文件的权限由四个部分构成:
第一部分(第一位)是文件的类型:
第二部分(三位:读写执行)是用户的权限:
第二部分(三位:读写执行)是同一组内用户的权限:
第三部分(三位:读写执行)是其他的权限
例如:
drwxrwxrwx 1 root root    344 10月 27 15:34 test
第一位d表示文件是文件夹类型
第一个rwx表示用户自己的读写执行权限
第二个rwx表示同一组内其他用户的读写执行权限
第三个rwx表示其他的用户的读写执行权限

用二进制表示为:
rwx:111
r--:100
-w-:010
r-x:101

普通文件的全权限为666(110 110 110)
目录的全权限为777(111 111 111)

umask的目的就是设置一个数字,这个数字是三位的整数,例如012,024等


umask设置了之后,用户创建的文件的权限就等于666或777 - umask设置的数字,例如是024,那么用户创建出来的普通文件的权限就是666 - 024 = 642

chown usrname filename 可以改变文件的属主


共享文件:
1、创建文件夹:mkdir testdir
2、改变文件夹所属的组:chgrp shared testdir
3、设置目录的SGID位:chmod g+s testdir
4、所有shared组下的用户设置权限为可写,即umask 002

=========================================================

管理文件系统

1ext文件系统(扩展文件系统)

2ext2文件系统(ext的升级)

3、日志文件系统

1ext3文件系统,linux的默认文件系统

2ext4文件系统,已经被很多发行版本的linux支持

3Reiser文件系统,linux上最快的文件系统

4JFS文件系统,古老的文件系统

5XFS另外一个日志文件系统


必须先在存储设备上创建分区来容纳文件系统。分区可以是整个硬盘,也可以是硬盘的一部分,来容纳虚拟目录的一部分


fdisk是一个用来帮助管理分区的工具,他是一个交互式的命令,有下面的一些选项:

a 设置一个标志,说明这个分区是可以启动的

b 编辑BSD Unix系统用的磁盘标签

c 设置DOS兼容标志

d 删除分区

l 显示可用的分区类型

m 显示命令选项

n 增加一个新分区

o 创建DOS分区表

p 显示当前分区表

q 退出,不保存更改

s Sun Unix系统创建一个新的磁盘标签

t 修改分区的系统id

u 改变使用的存储单位

v 验证分区表

w 将分区表写入磁盘

x 高级功能



创建完分区就要在分区上创建文件系统,常用的工具:

mkfs.ext3 创建一个ext3文件系统

mkfs.ext4 创建一个ext4文件系统



为分区创建了文件系统之后,下一步将它挂载到虚拟目录下的某个挂载点,这样就可以将数据存储到新的文件系统中了。可以用mkdir在虚拟目录中创建一个挂载点,然后使用mount将新的文件系统挂载到这个挂载点上



fsck命令用来检查和修复任意类型的linux文件系统,格式为fsck options filesystem,常用的命令行选项如下:

-a 如果检测到错误,自动修复文件系统

-A 检查/etc/fstab文件中列出的所有文件系统

-C 给支持进度条的文件系统显示一个进度条

-N 不进行检查,只显示那些检查会执行

-r 出现错误时提示

-R 使用-A选项时跳过根文件系统

-s 检查多个文件系统时一次进行检查

-t 指定要检查的文件系统

-T 启动时不显示头信息

-V 在检查时产生详细输出

-y 检测到错误时自动修复文件系统



在现实当中,我们需要将另外一个硬盘上的分区加到已有文件系统,来动态地向已有的我呢旧系统添加空间——这个时候我们需要LVMlinux逻辑卷管理器)


逻辑卷管理的核心是它如何处理安装在系统上的硬盘分区。


在逻辑卷的世界里,硬盘被称为物理卷(PV)。每一个物理卷都会被映射到硬盘上创建的某一物理分区。



多个物理卷集中在一起可以组成一个卷组(VG)。逻辑卷管理系统会把卷组当作物理硬盘一样对待,但是事实上卷组可以由分布在多个物理硬盘上的多个物理分区组成。


卷组提供了一个创建逻辑分区的平台,而这些逻辑分区事实上包含了文件系统


整个结构最后的一层是逻辑卷(LV)。逻辑卷为linux提供了创建文件系统的分区环境。linux将逻辑卷当作物理分区对待。可以使用任意一种标准的linux文件系统来格式化逻辑卷,然后再将它在某个挂载点添加进linux虚拟目录中




在上图中,卷组横跨了三个物理硬盘,覆盖了5个独立的物理分区。在卷组上有两个独立的逻辑卷。linux将每个逻辑卷当作物理分区,每个逻辑卷可以被格式化成ext4文件系统,然后挂载到某个点上。

未使用的分区或者新增的硬盘可以被添加到存在逻辑卷上。


linux上使用LVM:

第一步:定义物理卷

1、使用fdisk命令创建物理分区

2、通过t选项来改变分区的类型

3、用分区来创建真实的物理卷,可以使用pvcreate命令

第二步:创建卷组

1、使用vgcreate命令来创建卷组

第三步:创建逻辑卷,使用lvcreate命令,有下面的一些选项:

-c 指定快照逻辑卷的单位大小

-C 设置或重置连续分配策略

-i 指定条带数

-I 指定每条条带的大小

-l 指定分配给新逻辑卷的逻辑块数

-L 指定分配给新逻辑卷的硬盘大小

-m 创建设备的镜像数

-M 让次设备号一直有效

-n 指定新逻辑卷的名称

-p 为逻辑卷设置读写权限

-r 设置预读扇区数

-R 指定将镜像分成多大的区

-s 创建镜像逻辑卷

-Z 设置在新逻辑卷的前1KB数据为0

第四步:创建文件系统



安装软件程序


包管理系统:PMS


PMS利用数据来记录:

1linux上已经安装了什么软件

2、每个包安装了什么文件

3、每个已安装软件包的版本


基于Debian的发行版,例如ubuntu等,使用的基础命令是dpkg

基于Red Hat的发行版,如Fedora等,使用的基础命令是rpm


基于Debian的系统,dpkg命令是核心,包含在这个PMS中的其他工具有:

apt-get

apt-cache

aptitude


使用aptitude命令可以帮助你避免常见的软件安装问题,如软件依赖关系缺失。aptitude有个很方便的交互式界面。


Aptitude show packname 快速显示包的信息


dpkg -L packname 可以查看某个包的关联的所有文件的列表


dpkg -search 绝对路径 可以查看某个文件属于哪个软件包


aptitude search packname 查找将要安装的某个包


aptitude install packname 安装软件


aptitude safe-upgrade 安全的更新所有包


aptitude remove packname 删除软件包但是不删除数据和配置文件


aptitude purge packname 删除软件包和数据以及配置文件


从源文件进行安装的步骤:

1、首先执行源文件根目录下的configure命令

2、然后进行make

3make install




使用编辑器


vim编辑器

有两种模式:插入模式(编辑模式),普通模式(命令模式)


进入vim的时候是命令模式,按下i之后进入编辑模式,按下esc之后回到命令模式


一些常用的vim命令:

h:左移一个字符

j:下移一行

k: 上移一行

l:左移一个字符


pagedownctrl+F):下翻一屏

pageupctrl+B):上翻一屏

G:移到缓冲区的最后一行

num G:移到缓冲区的第num

gg:移到缓冲区第一行


q!:不保存退出

wq:保存退出

w filename:保存为filename


x:删除当前光标所在位置的字符

dd:删除光标所在行

dw:删除当前(即光标处)单词

d$:删除当前至行尾的内容

J:删除当前行的换行符

u:撤销命令

a:在当前位置之后添加数据

A:在当前行的行尾添加数据

r char:用char替换当前单词

R text text替换当前位置的数据,直到按下esc


复制和粘贴:

在命令模式下按下v,然后上下移动即可选择文本,然后按下y旧复制了文件内容

按下p将复制的内容粘贴到当前光标处


查找和替换:

在命令模式下按下/,然后输入单词,即可进行查找,按n跳到下一个查找单词处


在命令模式下按下:/s/old/new/g或:$s/old/new/g来替换所有单词



构建基础脚本


echo $var == echo “$var” != echo '$var'


echo -n $var 不换行输出


反引号(注意不是单引号,是小键盘1(!)旁边的那个健)用于引用命令,执行命令之后获取到命令的返回值,如:

strime=`date`


重定向输出符号> ,重定向是将一个命令的输出发送到一个文件中


追加符号>>,将命令的输出追加到一个文件中


重定向输入符号<,将文件内容作为某个命令的输入


管道|,将某一个命令的输出作为一个命令的输入


使用方括号来计算数学表达式:

var=$[5 + 1]

var=$[$var1+1]



但是方括号只能计算整数不能计算浮点数,要计算浮点数需要使用bc命令,常用的使用方法:

var=`echo “scale=5; 4 / 5” | bc`

上面的语句是将计算4/5,保留位数为4,如果不舍值scale,那么将按照整数计算,加入echo的目的是将bc的计算结果输出到变量var

或者也可以这样:

var2=`bc << EOF

scale=4

4*5.6

EOF`

效果是一样的


$?保存了上个脚本(或命令)的退出码



使用结构化命名


if command; then

command

elif command

command

elif command

command

else

command

fi


在方括号中进行条件判断,方括号即结构化判断语句(ifwhilefor)的test命令


数值比较(建议使用字母类型的比较符,即前面的):

-eq 是否相等 (可以使用==)

-ge 大于等于

-gt 大于(可以使用>

-le 小于等于

-lt 小于(可以使用<

-ne 不等于(可以使用!=



注意test命令不能处理浮点型



字符串比较:

= 是否相同

!= 是否不同

< 小于

\> 大于(在shenll中不能直接使用>,这会被当成重定向符号,因此需要转义字符)

-n 长度是否不为0

-z 长度是否为0



文件比较:

-d file 是否存在且为一个目录

-e file 是否存在

-f file 是否存在且为一个文件

-r file 是否存在并可读

-s file 是否存在且非空

-w file 是否存在且可写

-x file 是否存在且可执行

-O file 是否存在并且是否属于当前用户

-G file 是否存在并检查默认组是否和当前用户相同

file1 -nt file2 file1是否比file2

file1 -ot file2 file1是否比file2



复合条件测试:

[ condition1 ] && [ condition2 ]

[ condition1 ] || [ condition2 ]



由于test命令(即方括号)只能进行简单的算数操作,因此出现了双括号命令用于较复杂的算数操作

((exp)),在双括号中可以使用下面命令:

var++

var--

++var

--var

!

~

** 幂运算

<< 左偏移

>> 右偏移

&

|

&&

||



针对字符串的高级操作

[[ exp ]],可以使用模式匹配,而且大于号也不用转义




case语句:

case $var in

p1) command1;;

p2) command2;;

p3|p4) command3;;

*) default command4;;

esac


for循环的用法:

1、读取列表的值。典型用法如下:

for var in I "don't" know if "this'll" work

do

echo $var

done

其中列表有些技巧:

list=”i am mobinsheng ”

list=$list” name” #往列表中添加值


2、循环。典型用法如下:

for ((i=0;i < 10;++i))

do

echo $i

done


3、遍历目录下的文件

for v in /var/*

do

if [ -d "$v" ];then

echo $v " is Dir"

elif [ -f "$v" ];then

echo $v " is file"


#echo $v

fi

done



字段分隔符:

shell中默认的字段分隔符是空格、制表符、换行符。如果你需要自己的字段分割符号,那么要像下面这样:(@作为分隔符)

var=$IFS

IFS=$"@"

for var in I@"don't"@know@if@"this'll"@work

do

echo $var

done

IFS=$var



while循环。常见的结构如下:

var=1

while [ $var -lt 10 ];

do

echo $var

var=$[$var+1];

done



until循环。常见用法:

var=1

until [ $var -eq 10 ];

do

echo $var;

var=$[$var+1];

done



在循环中可以使用breakcontinue命令(可以指定跳过的循环层数)


用户输入



位置参数:

$0表示脚本名字

$1表示第一个参数

。。。

$9表示第九个参数

${10}第十个参数



使用位置参数之前先测试相应的位置参数是否有数据,一般使用if [ -n “$1” ]


$# 表示参数的个数

$@ 表示单词的列表,可以使用for进行遍历

$* 把多有单词当作一个单词


shift命令会移动命令参数,每执行一次,所有参数就会左移一个位置$2变成$1,以此类推




处理选项:

简单的选项:

while [ -n "$1" ]

do

case "$1" in

-a) echo "option a";;

-b) echo "option b";;

*) ;;

esac

shift

done



复杂的带参数的选项:

set -- `getopt -q ab:c "$@"`

while [ -n "$1" ]

do

case "$1" in

-a) echo "option a";;

-b) echo "option b" " value = $2";;

-c) echo "option c";;

*) ;;

esac

shift

done


set -- `getopt -q ab:c "$@"`详解:-q表示忽略一些用户输入的不存在的选项,ab:c表示有三个选项,其中b选项带参数,$@表示原始的输入参数,它会被getopt命令格式化


另外一个更牛的处理选项的方式(注意case判断中选项没有破折号)

while getopts :ab:c opt

do

case "$opt" in

a) echo "option a";;

b) echo "option b" " value = $OPTARG";;

c) echo "option c";;

*) ;;

esac

done


getopts处理每一个选项的时候,会将环境变量OPTIND的值加1getopts处理完成之后,可以使用shift命令和OPTIND一起来移动参数

shift $[ $OPTIND – 1 ]



read命令从标准输入或者另一个文件描述符读取输入:

read name read -p “please enter your name:” name


可以用-t选项指定read的等待超时的时间


读取密码的时候可以指定-s选项(隐藏方式读取)


使用read读取文本(cat之后的输出用管道传给带whileread)

count=1

cat 1.sh | while read line

do

echo "line$count:$line"

count=$[ $count+1 ]

done


呈现数据


标准输入:STDID = 0

标准输出:STDOUT = 1

标准错误:STDERR = 2


在脚本中如果需要重定向输出到某一个文件而不是输出,可以使用exec

例如:

exec 1>text

echo "mobinsheng"

echo "hello world"


同理重回定向错误输出也可以使用exec 2>filename


重定向输入:exec 0<filename

例如:

exec 0<text

count=1

while read line

do

echo "line$count:$line"

count=$[ $count+1 ]

done


shell脚本中最多可以打开9个文件描述符,除了02分配给了标准输入、输出、错误之外,38都没有被分配。所以可以将这些文件描述符分配给任意一个文件(使用exec,例如:

exec 3>text2

echo "sssssssssssss" >&3


也可以将exec命令来将输出追加到现有文件中,而不是创建一个新文件:exec 3>>text2


把重定向的文件描述符恢复:

exec 3>&1 #3指向标准输出流,即3保存了标准输出

exec 1>text # 将标准输出重定向到text

。。。

exec 1>&3 # 恢复


对于已经重定向的输入,同理:

exec 0<&4 #4保存标准输入

exec text<0 #把标准输入重定向到text

….

exec 4<&1 #恢复



关闭文件描述符:

exec 3>text #3重定向到text

exec 3>&- #关闭文件描述符3



有时你不想显示脚本的输出,这在将脚本作为后台进程执行时很常见。此时可以把输入或者错误重定向到/dev/null

还可以将/dev/null作为输入,这样可以快速删除一个文件的内容,但是文件仍然存在,只是变空了


创建临时文件:

mktemp text.XXXXXX 可以创建出类似于text.326541这样的文件,常见用法:

filename=`mktemp text.XXXXXX`


mktemp-t选项会强制命令在系统的临时目录中创建临时文件


mktemp-d选项会创建临时文件夹


分流器tee,可以一边将输出显示在屏幕上一边将输出导入日志中,常见用法:date|tee filename

tee-a选项是追加到文件末尾,这样就不会覆盖原文件的内容了



控制脚本


信号


常用的信号:

SIGHUP 挂起进程

SIGINT 终止进程

SIGQUIT 停止进程

SIGKILL 无条件终止进程

SIGTERM 可能的话终止进程

SIGSTOP 无条件停止进程,但是不会终止进程

SIGTSTP 停止或暂停进程,但不终止进程

SIGCONT 继续运行停止的进程


ctrl+c 会终止jincheng

ctrl+z 会暂停进程



在脚本中可以使用trap来捕捉信号,格式是:trap commad signals,例如:

trap "echo ;echo get ctrl+c" SIGINT

sleep 1000


或者:

function handle()

{

echo ;

echo "get ctrl+c"

}

trap handle SIGINT

sleep 1000



在脚本退出时执行某个命令(例如清理等工作),可以用trap实现,例如:

trap "echo bybe" EXIT



可以用单破折线作为命令,后跟要移除的信号来移除一组信号捕捉,将其恢复到正常状态,例如:

trap – EXIT

脚本名& 这样可以实现脚本在后台运行。但是此时如果你退出控制台的话,进程还是会被终止,如果想要在退出终端后进程继续运行,那么需要nohup命令:

nohup ./test.sh &



定时运行脚本


at命令允许指定系统何时运行脚本:

at -f filename time

at -f ./test.sh 14:39



atq 命令可以列出等待的作业(定时作业)


atrm 作业号 ——命令可以删除等待的作业



计划定期执行脚本(周期性执行脚本):

使用cron时间表


cron表的格式:

min hour dayofmonth month dayofweek command


cron表运行使用特定值、值范围(例如15)或者是通配符(星号)来指定条目。加入你要在每天的10:15运行一个命令,可以这样指定:

15 10 * * * command



首先使用crontab -l查看用户是否有crontab


使用crontab -ecron表添加一条记录


如果你的脚本不需要有精确的执行时间,用预配置的cron脚本目录会更方便,有四个基本的目录:

/etc/cron.daily

/etc/cron.hourly

/etc/cron.monthly

/etc/cron.weekly

把自己的脚本复制到相应目录下


设置启动时运行(有几个方法):

1、在本地开机文件位置添加自己的需要开机运行的脚本:

debian /etc/init.d/rc.local

Fedora /etc/rc.d/rc.local

Ubuntu /etc/rc.local

2、在主目录的.bash_profile中添加命令或者脚本

3、在主目录的.bashrc中添加命令或脚本



创建函数


函数的典型样子:

function printName()

{

echo "mbs"

}

printName()

{

echo "mbs"

}


调用函数:

printName; #注意调用函数的时候不能加括号



注意不要覆盖原先定义的函数名


默认情况下,函数的退出码是函数中最后一条命令返回的退出状态码。在函数执行之后,可以使用#?来查看函数的退出码是什么


可以使用return来退出函数并返回退出码


注意!!!!!

函数中return返回的是函数的退出码,并不是函数的返回值(例如计算两个相加的函数,不能通过return返回计算值),相反,在函数中要返回一个值,需要用echo语句,例如:

function calX()

{

i=10;

echo $[ $i * 2 ];

}

var=`calX`;

echo $var



shell将函数当作小型脚本来处理。因此可以向函数传递参数,例子:

function calX()

{

echo $1

i=10;

echo $[ $i * 2 ];

}

var=`calX 1010 122`; #调用函数,并传递函数


注意函数中的参数$1等和脚本主体的参数是不一样的,所以不哟个担心会混淆,在函数中使用脚本主体的参数需要传递给函数


函数会用到两种类型的变量:全局变量、局部变量。


默认情况下,脚本中的任何变量都是全局变量


如果不想造成混乱可以使用局部变量,局部变量:local var=123


数组变量:

定义数组:array=(1 2 3 4 5)



向函数中传递数组的方法:

function pt()

{

local var=(`echo "$@"`)

echo ${var[*]}

}

array=(1 2 3 4 5)

pt ${array[*]}



从函数中返回数组:

function retA()

{

local a=(1 5 6)

echo ${a[*]}

}

var=`retA`

echo ${var[*]}


由于函数局部变量的出现,因此函数可以自成体系。由此,函数可以递归调用,也可以做成模块.


模块的做法:

2.sh(模块或者库)中定义如下函数:

#!/bin/bash

function TestBlock()

{

echo "hello"

}

在其他脚本中调用

. ./2.sh #前面一个点是资源加载符号,后面一个点和/连起来表示2.sh在当前路径下,即lib_path

var=`TestBlock` #调用库的函数

echo $var


可以在.bashrc文件中定义函数,这样就可以方便使用


在脚本中,默认的变量值都是字符串,如果你想定义非字符串的变量,可以使用declare,如下

declare [-afirx] var=xxxx

参数说明:

-a 定义为数组

-f 定义为函数

-i 定义为整数

-r 定义为只读

-x 定义为通过环境输出变量


调试脚本:

sh [-nvx] scripts

-n 不执行脚本,查询脚本内的语法,若有错误则列出

-v 在执行脚本之前,先将脚本的内容输出到屏幕

-x -v类似,但是稍微有点不同


配置用户之前建议先安装Linuxconf














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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值