Linux系统——Shell编程免交互及小命令详解

目录

一、免交互

1.Here Document 多行重定向

1.1定义

1.2语法格式

1.3tee命令

1.4cat与tee

 1.5Here Document 变量设定

2.Expect

2.1定义

2.2格式

2.3命令详解

2.4示例

2.4.1交互式

2.4.2远程拷贝文件

2.4.3远程登录

交互式登录

免交互式登录

2.4.4免交互远程批量创建用户

二、字符串处理

1.字符串切片

1.1基于偏移量取字符

1.1.1取字符长度

1.1.2跳过指定位置取指定位置数值

1.1.3跳过左边字符

1.1.4取字符串右边的字符

1.1.5掐头去尾

 1.1.6取倒数的范围

1.2基于模式取字符串

1.2.1删左留右

1.2.2删右留左

2.查找替换删除

2.1查找替换

2.2查找并删除

2.3示例扩展

2.3.1懒惰模式

2.3.2贪婪模式

2.3.3开头更换

2.3.4结尾更换

3.大小写转换

3.1转换大写

3.2转换小写

三、高级变量

1.高级变量赋值

2.变量的间接引用——eval命令引用

四、其他脚本工具

1.创建临时文件mktemp

1.1格式

1.2选项

1.3示例

1.3.1直接使用代表创建临时文件

1.3.2创建随机文件

1.3.3创建随机目录

1.4扩展——垃圾箱

2.安装复制文件install

2.1格式

2.2选项

 

3.扩展——生成随机六位数密码


一、免交互

1.Here Document 多行重定向

1.1定义

使用I/O重定向的方式将命令列表提供给交互式程序

Here Document是标准输入的一种替代品,可以帮助脚本开发人员不必使用临时文件来构建输入信息,而是直接就地生产处一个文件并用作命令的标准输入,Here Document可以与非交互式程序和命令一起使用。

1.2语法格式

命令  <<  标记

内容

标记

注意事项:

  • 标记可以使用任意的合法字符(通用的字符是EOF[End of file])
  • 结尾的标记一定要顶格写,前面不能有任何字符(包括空格)
  • 结尾的标记后面也不能有任何字符(包括空格)
  • 开头标记前后空格会被省略掉

1.3tee命令

1.4cat与tee

cat与tee相差不大

 1.5Here Document 变量设定

Here Document也支持使用变量,如果标记之间有变量被使用,会先替换成变量值。如果想要将一些内容写入文件,除了常规的方法外,也可以使用Here Document。如果写入的内容中包含变量,在写入文件时要先将变量替换成实际值,再结合cat命令完成写入。

[root@localhost opt]#vim eof.sh

#!/bin/bash
var="today i will study well"
myvar=$(cat <<EOF
TOday is Therday
$var
this is a good day
EOF
)

echo $myvar

[root@localhost opt]#bash eof.sh 
TOday is Therday today i will study well this is a good day
[root@localhost opt]#vim eof.sh

#!/bin/bash
var="today I will study"
myvar=$(cat <<EOF
Today is Friday
$var
this is good day
EOF
)

echo "$myvar"


[root@localhost opt]#bash eof.sh 
Today is Friday
today I will study
this is good day

2.Expect

2.1定义

建立在TCL(Tool Command Language)语言基础上的一个工具,常被用于进行自动化控制和测试,解决Shell脚本中交互的相关问题

  • 建立在tcl之上的一个工具(需要安装,例yum install expect)
  • 用于进行自动化控制和测试
  • 解决Shell脚本中交互相关的问题
[root@localhost ~]#yum install expect -y
已加载插件:fastestmirror, langpacks
base                                                   | 3.6 kB     00:00     
epel/x86_64/metalink                                   | 7.5 kB     00:00     
epel                                                   | 4.7 kB     00:00     
extras                                                 | 2.9 kB     00:00     
updates                                                | 2.9 kB     00:00     
(1/2): epel/x86_64/updateinfo                            | 1.0 MB   00:01     
(2/2): epel/x86_64/primary_db                            | 7.0 MB   00:01     
Loading mirror speeds from cached hostfile
 * base: mirrors.ustc.edu.cn
 * epel: mirrors.tuna.tsinghua.edu.cn
 * extras: mirrors.ustc.edu.cn
 * updates: mirrors.ustc.edu.cn
软件包 expect-5.45-14.el7_1.x86_64 已安装并且是最新版本
无须任何处理
[root@localhost ~]#rpm -q expect 
expect-5.45-14.el7_1.x86_64
[root@localhost ~]#which expect 
/usr/bin/expect

2.2格式

expect  [选项]  [ -c cmds ] [[ -[f|b] ]  cmdfile ]  [ args ]

2.3命令详解

1.脚本解释器

  • expect脚本中首先引入文件,表明使用的是哪一种shell(#! /usr/bin/expect)

2.spawn

  • 启动新的进程(监控、捕捉)
  • spawn后面通常跟一个Linux执行命令,表示开启一个会话、进程,并跟踪后续交互信息(spawn passwd root)

3.expect

  • 从进程接收字符串
  • 判断上次输出结果中是否包含指定的字符串,如果有则立即返回,否则就等待超时时间返回;只能捕捉有spawn启动的进程输出;
  • 用于接收命令执行后的输出,然后和期望的字符串匹配

4.send

  • 向进程发送字符串,用于模拟用户的输入;该命令不能自动回车换行,一般要加\r(\n)

5.结束符(expect eof)

  • 表示交互结束,等待执行结束,退回到原用户,与spawn对应(切换到root用户,expect脚本默认的等待时间是10s,当执行完命令之后,默认停留时间10s后,自动切回原用户)

6.interact

  • 允许用户交互expect eof
  • 执行完成后保持交互状态,把控制权交给控制台,会停留在目标终端而不是退回到原终端,这时候就可以手工操作了,interact后命令不再起作用,比如interact后添加exit,并不会退出root用户;而如果没有interact则登录完成后会退出,而不是留在远程终端上。

expect eof 与 interact 只能二选一

7.set

  • expect默认的超时时间是10秒,通过set命令可以设置会话超时时间,若不限制超时时间,则应设置为-1(set time out 30)

8.exp_continue

  • 匹配多个字符串在执行动作后加此命令
  • 表示允许expect继续向下执行指令;
  • exp_continue附加于某个expect判断选项之后,可以是该项被匹配后还能继续匹配expect判断语句内的其他项。exp_continue类似于控制语句的continue语句,表示允许expect继续向下执行命令
expect { "(yes/no)" {send "yes\n";exp_continue;}

            "*password" {set timeout 300;send "abc123\n"}
       }

注意:使用exp_continue时,如果跟踪像passwd这样输入密码后就结束进程的命令,expect{}外不要加上expect eof,因为spawn进程结束后会默认向expect发送eof,会导致后面的expect eof执行报错

9.send_user

表示回显命令    与echo相同

10.接收参数(位置变量)

expect脚本可以接收从bash命令行传递参数,使用[lindex $argv n]获得。其中从0开始,分别表示第一个,第二个,第三个......参数

set hostname  [lindex $argv 0]     相当于hostname=$1

set password [lindex $argv 1]        相当于passswd=$2

2.4示例

2.4.1交互式

2.4.2远程拷贝文件

2.4.3远程登录
交互式登录

免交互式登录

2.4.4免交互远程批量创建用户
[root@localhost ~]#vim cxk

#!/bin/bash
net=192.168.241
password=123
iplist="
22
23
"

for i  in $iplist
do
ip=$net.$i

/usr/bin/expect <<EOF

spawn ssh root@$ip

expect {
        "(yes/no)"
        {send "yes\r";exp_continue}
        "*password"
        {send "$password\r"}
}
expect "*]#" { send "useradd test\n" }
expect "*]#" { send "echo 123 |passwd --stdin test\r" }
expect "*]#" { send "exit\r"   }
expect eof
EOF
done


[root@localhost ~]#bash cxk
spawn ssh root@192.168.241.22
root@192.168.241.22's password: 
Last failed login: Wed Jan 31 03:48:51 EST 2024 from 192.168.241.11 on ssh:notty
There was 1 failed login attempt since the last successful login.
Last login: Wed Jan 31 03:41:56 2024 from 192.168.241.11
[root@node2 ~]#useradd test
useradd:警告:此主目录已经存在。
不从 skel 目录里向其中复制任何文件。
正在创建信箱文件: 文件已存在
[root@node2 ~]#echo 123 |passwd --stdin test
更改用户 test 的密码 。
passwd:所有的身份验证令牌已经成功更新。
[root@node2 ~]#exit
登出
Connection to 192.168.241.22 closed.
spawn ssh root@192.168.241.23
root@192.168.241.23's password: 
Last failed login: Thu Feb  1 00:48:57 CST 2024 from 192.168.241.11 on ssh:notty
There was 1 failed login attempt since the last successful login.
Last login: Thu Feb  1 00:48:28 2024 from 192.168.241.1
[root@node3 ~]#useradd test
[root@node3 ~]#echo 123 |passwd --stdin test
更改用户 test 的密码 。
passwd:所有的身份验证令牌已经成功更新。
[root@node3 ~]#exit
登出
Connection to 192.168.241.23 closed.



[root@node2 ~]#id test
uid=1001(test) gid=1001(test) groups=1001(test)



[root@node3 ~]#id test
uid=1001(test) gid=1001(test) 组=1001(test)

二、字符串处理

1.字符串切片

1.1基于偏移量取字符

1.1.1取字符长度
  • ${#var}  返回字符串变量var的字符的长度,一个汉字算一个字符

1.1.2跳过指定位置取指定位置数值

1.1.3跳过左边字符
  • ${var:offset} 返回字符串变量var中第offset个字符后(不包括第offset个字符)的字符开始,到最后的部分,offset的取值在0到${#var}-1 之间
  • ${var:offset:number}返回字符串变量var中从第offset个字符后(不包括第offset个字符)的字符开始,长度为number的部分

1.1.4取字符串右边的字符
  • ${var:-length}取字符串的最右侧几个字符,取字符串的最右侧几个字符;
  • 注意:冒号后必须有一空白字符

1.1.5掐头去尾
  • ${var:offset:-length}从最左侧跳过offset字符,一直向右取到距离最右侧length个字符之前的内容,即掐头去尾

 1.1.6取倒数的范围
  • ${var:-length:-offset}先从最右侧向左取到length个字符开始,再向右取到距离最右侧offset个字符之间的内容;
  • 注意:-length前空格,并且length必须大于offset
[root@localhost ~]#echo ${str}
abcdefghijklmnopqrstuvwxyz
[root@localhost ~]#echo ${str: -4:-3}
#从倒数第4个开始取 取1个
w
[root@localhost ~]#echo ${str: -4:-2}
#从倒数第4个开始取 取2个
wx
[root@localhost ~]#echo ${str: -5:-2}
#从倒数第5个开始取 取3个
vwx
[root@localhost ~]#echo ${str: -5:-3}
#从倒数第5个开始取 取2个
vw
[root@localhost ~]#echo ${str: -6:-3}
#从倒数第6个开始取 取3个
uvw

1.2基于模式取字符串

1.2.1删左留右
  • ${var#*word}其中word可以是指定的任意字符,自左向右,查找var变量所存储的字符串中,第一次出现的word,删除字符串开头至第一次出现word字符串(含)之间的所有字符,即懒惰模式

  • ${var#*word}其中word可以是指定的任意字符,自左向右,查找var变量所存储的字符串中,最后一个word为界,删除字符串开头的字符向右至最后一次出现word字符串(含)之间的所有字符,即贪婪模式

1.2.2删右留左
  • ${var%word*}其中word可以是指定的任意字符,自右向左,查找var变量所存储的字符串中,第一次出现的word,删除字符串最后一个字符向左至第一次出现word字符串(含)之间的所有字符,即为懒惰模式,以从右向左的第一个word为界删右留左

  • ${var%word*}其中word可以是指定的任意字符,自右向左,查找var变量所存储的字符串中,第一次出现的word,删除字符串最后一个字符向左至最后一次出现word字符串(含)之间的所有字符,即为懒惰模式,以从右向左的第一个word为界删右留左

2.查找替换删除

2.1查找替换

  • ${var/pattern/substr}               ${变量/搜索的字符串/修改的字符串}
  • #查找var所表示的字符串中,第一次被pattern所匹配到的字符串,以substr替换之
  • 懒惰模式

  • ${var//pattern/substr}
  • 查找var所表示的字符串中,所有能被pattern所匹配到的字符串,以substr替换之
  • 贪婪模式

  • ${var/#pattern/substr}
  • 查找var所表示的字符串中,行首被pattern所匹配到的字符串,以substr替换之(在此处“#”代表开头

  • ${var/%pattern/substr}
  • 查找var所表示的字符串中,行尾被pattern所匹配到的字符串,以substr替换之(在此处“%"代表结尾

2.2查找并删除

  • ${var/pattern}
  • 删除var表示的字符串中第一次被pattern匹配到的字符串
  • 懒惰模式

  • ${var//pattern}
  • 删除var表示的字符串中所有被pattern匹配到的字符串
  • 贪婪模式

  • ${var/#pattern}
  • 删除var表示的字符串中所有以pattern为行首匹配到的字符串

  • ${var/%pattern}
  • 删除var所表示的字符串中所有以pattern为行尾匹配到的字符串

2.3示例扩展

getent命令会读取文件/etc/hosts中的内容

2.3.1懒惰模式

2.3.2贪婪模式

2.3.3开头更换

2.3.4结尾更换

3.大小写转换

3.1转换大写

  • ${var^^}
  • 把var中的所有小写字母转换为大写

3.2转换小写

  • ${var,,}
  • 把var中的所有大写字母转换为小写

三、高级变量

1.高级变量赋值

高级变量赋值
变量配置方式str没有配置str为空字符串str已配置为费控字符串
var=${str-expr}var=exprvar=var=$str
var=${str:-expr}var=exprvar=exprvar=$str
var=${str+expr}var=var=exprvar=expr
var=${str:+expr}var=var=var=expr
var=${str=expr}

str=expr

var=expr

str不变

var=

str不变

var=$str

var=${str:=expr}

str=expr

var=expr

str=expr

var=expr

str不变

var=$str

var=${str?expr}expr 输出至 stderrvar=var=$str
var=${str:?expr}expr 输出至 stderrexpr 输出至 stderrvar=$str
[root@localhost ~]#unset str
#str没有配置
[root@localhost ~]#var=${str-lucky}
#定义var的变量为str-lucky
[root@localhost ~]#echo $var
#输出var的值为lucky
lucky
[root@localhost ~]#unset str;var=${str-lucky};echo $var
lucky
[root@localhost ~]#str=" ";var=${str-lucky};echo $var
#str变量为空
#var输出变量为空

[root@localhost ~]#str=1;var=${str-lucky};echo $var
#str变量定义为1
#var输出变量为非空字符串   输出str的变量
1
[root@localhost ~]#str=1;var=${str:-lucky};echo $var
#str变量定义为1
#var输出变量为非空字符串   输出str的变量
1
[root@localhost ~]#str=1;var=${str+lucky};echo $var
#str变量定义为1   "+"输出var的定义变量lucky
lucky
[root@localhost ~]#str=1;var=${str:+lucky};echo $var
#str变量定义为1   ":+"输出var的定义变量lucky
lucky
[root@localhost ~]#str=1;var=${str=lucky};echo $var
#str变量定义为1   "="输出str的定义变量1
1
[root@localhost ~]#str=1;var=${str:=lucky};echo $var
#str变量定义为1   ":="输出str的定义变量1
1
[root@localhost ~]#str=1;var=${str?lucky};echo $var
#str变量定义为1   "?"输出str的定义变量1
1
[root@localhost ~]#str=1;var=${str:?lucky};echo $var
1
#str变量定义为1   ":?"输出str的定义变量1

2.变量的间接引用——eval命令引用

eval将首先扫描命令行进行所有的置换,然后再执行该命令。该命令适用于一次扫描无法实现其功能的变量,该命令可以对变量进行两次扫描

四、其他脚本工具

1.创建临时文件mktemp

mktemp命令用于创建并显示临时文件,可以避免冲突

1.1格式

mktemp [option]... [template]

template:filenamexxx,x至少要出现是哪个

1.2选项

选项含义
-d创建临时目录
-p DIR或--tmpdir=DIR指明临时文件所存放目录位置

1.3示例

1.3.1直接使用代表创建临时文件

1.3.2创建随机文件

1.3.3创建随机目录

1.4扩展——垃圾箱

[root@localhost ~]#vim ljx.sh 

#!/bin/bash
DIR=`mktemp -d /tmp/trash-$(date +%F_%H-%M-%S)XXXXXX`
mv $* $DIR
echo $* is move to $DIR


function rm () { local trash=`mktemp -d /tmp/trashXXXX`;mv $*
$trash; }

[root@localhost ~]#alias rm=ljx.sh
[root@localhost ~]#cd /opt
[root@localhost opt]#ls
passwd  rh  test
[root@localhost opt]#rm passwd 
bash: ljx.sh: 未找到命令...
[root@localhost opt]#chmod +x ~/ljx.sh
[root@localhost opt]#rm passwd 
bash: ljx.sh: 未找到命令...
[root@localhost opt]#cd
[root@localhost ~]#rm /opt/passwd 
bash: ljx.sh: 未找到命令...
[root@localhost ~]#alias
alias cp='cp -i'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'
alias ls='ls --color=auto'
alias mv='mv -i'
alias rm='ljx.sh'
alias scan='echo "- - -" > /sys/class/scsi_host/host0/scan;echo "- - -" > /sys/class/scsi_host/host1/scan;echo "- - -" > /sys/class/scsi_host/host2/scan'
alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
[root@localhost ~]#alias rm="~/ljx.sh"
[root@localhost ~]#rm /opt/passwd 
/opt/passwd is move to /tmp/trash-2024-01-31_19-57-46h6jTVD
[root@localhost ~]#cd /tmp
[root@localhost tmp]#ls
systemd-private-a4556f18442a41648b3f9b5caa837012-chronyd.service-rGR5Ls
systemd-private-a4556f18442a41648b3f9b5caa837012-colord.service-h9U9Wb
systemd-private-a4556f18442a41648b3f9b5caa837012-cups.service-IeH202
systemd-private-a4556f18442a41648b3f9b5caa837012-rtkit-daemon.service-AjhOUA
systemd-private-a4556f18442a41648b3f9b5caa837012-vgauthd.service-anvCIk
systemd-private-a4556f18442a41648b3f9b5caa837012-vmtoolsd.service-9SsjJc
testdirBrl
trash-2024-01-31_19-57-46h6jTVD

定义别名一定要加绝对路径!!!

2.安装复制文件install

install功能相当于cp,chmod,chown,chgrp,mkdir等相关工具的集合(也就是说install可以替代他们执行他们的命令)

2.1格式

  • install [OPTION]...[-T] SOURCE DEST 单文件
  • install [OPTION]... SOURCE... DIRECTORY
  • install [OPTION]...-t DIRECTORY SOURCE...
  • install [OPTION]...-d DIRECTORY ...

2.2选项

选项含义
-mMODE 默认755
-oOWNER
-gGROUP
-dDIRNAME 目录

[root@localhost opt]#install /etc/passwd -m 700 -o cxk -g cxk .
[root@localhost opt]#ls
passwd  rh  test
[root@localhost opt]#cat passwd 
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
abrt:x:173:173::/etc/abrt:/sbin/nologin
libstoragemgmt:x:998:996:daemon account for libstoragemgmt:/var/run/lsm:/sbin/nologin
rpc:x:32:32:Rpcbind Daemon:/var/lib/rpcbind:/sbin/nologin
colord:x:997:995:User for colord:/var/lib/colord:/sbin/nologin
saslauth:x:996:76:Saslauthd user:/run/saslauthd:/sbin/nologin
rtkit:x:172:172:RealtimeKit:/proc:/sbin/nologin
pulse:x:171:171:PulseAudio System Daemon:/var/run/pulse:/sbin/nologin
chrony:x:995:991::/var/lib/chrony:/sbin/nologin
rpcuser:x:29:29:RPC Service User:/var/lib/nfs:/sbin/nologin
nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin
tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
usbmuxd:x:113:113:usbmuxd user:/:/sbin/nologin
geoclue:x:994:989:User for geoclue:/var/lib/geoclue:/sbin/nologin
qemu:x:107:107:qemu user:/:/sbin/nologin
radvd:x:75:75:radvd user:/:/sbin/nologin
setroubleshoot:x:993:988::/var/lib/setroubleshoot:/sbin/nologin
sssd:x:992:987:User for sssd:/:/sbin/nologin
gdm:x:42:42::/var/lib/gdm:/sbin/nologin
gnome-initial-setup:x:991:986::/run/gnome-initial-setup/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
avahi:x:70:70:Avahi mDNS/DNS-SD Stack:/var/run/avahi-daemon:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
ghd:x:1000:1000:ghd:/home/ghd:/bin/bash
cxk:x:1001:1001::/home/cxk:/bin/bash
[root@localhost opt]#ll passwd 
-rwx------ 1 cxk cxk 2138 1月  31 20:11 passwd
[root@localhost opt]#install -d wyb -m 666
[root@localhost opt]#ls
passwd  rh  test  wyb
[root@localhost opt]#ll wyb
总用量 0
[root@localhost opt]#ll
总用量 4
-rw-------  1 cxk  cxk  2138 1月  31 20:11 passwd
drwxr-xr-x. 2 root root    6 12月 24 11:43 rh
-rw-r--r--  1 root root    0 1月  31 19:55 test
drw-rw-rw-  2 root root    6 1月  31 20:14 wyb

3.扩展——生成随机六位数密码

[root@localhost opt]#cat /dev/random|tr -dc "[0-9a-zA-Z]"|tr -d "[ ]"|head -c6
SPdRQl
[root@localhost opt]#cat /dev/random|tr -dc "[0-9a-zA-Z]"|tr -d "[ ]"|h6ad -c6
zCy0ev
[root@localhost opt]#cat /dev/random|tr -dc "[0-9a-zA-Z]"|tr -d "[ ]"|h6ad -c6
f38QpE
[root@localhost opt]#cat /dev/random|tr -dc "[0-9a-zA-Z]"|tr -d "[ ]"|h6ad -c6
8yjVM3
[root@localhost opt]#cat /dev/random|tr -dc "[0-9a-zA-Z]"|tr -d "[ ]"|h6ad -c6
4GwN38
[root@localhost opt]#cat /dev/random|tr -dc "[0-9a-zA-Z]"|tr -d "[ ]"|h6ad -c6
a4ERRf
[root@localhost opt]#cat /dev/random|tr -dc "[0-9a-zA-Z]"|tr -d "[ ]"|h6ad -c6
eD3Mym
[root@localhost opt]#cat /dev/random|tr -dc "[0-9a-zA-Z]"|tr -d "[ ]"|h6ad -c6
3yJQdp
[root@localhost opt]#cat /dev/random|tr -dc "[0-9a-zA-Z]"|tr -d "[ ]"|h6ad -c6
OavoBw
  • 27
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值