【Linux技术】总结·linux shell脚本攻略

来源:互联网 发布:三明治面膜的危害知乎 编辑:程序博客网 时间:2024/05/17 15:58

第一章:小试牛刀

#变量赋值

var = value不同于var=value

把var=value写成var = value是一个常见的错误

前者是赋值操作,后者是相等操作

#let命令可以直接执行基本的算数操作

#bc

bc是一个用于数学运算的高级工具,这个精密计算器包含了大量的选项

借助bc可以执行浮点数运算并应用一些高级函数

#重定向

>和>>并不相同,尽管这两个操作都可以将文本重定向到文件

前者先清空文件,后者会将内容追加到现有文件的末尾

#位桶(黑洞)

/dev/null是一个特殊的设备文件,这个文件接收到任何数据都会被丢弃

因此,null设备通常也被称为位桶(bit bucket)或黑洞

#tee

有一个巧妙的方法可以将数据重定向到文件

同时提供一份重定向数据的副本作为后续命令的stdin

这一切都可以使用tee来实现

tee在默认情况下会将文件覆盖,但它提供一个-a选项,可以用于追加内容

#alias

alias命令的作用只是暂时的,一旦关闭当前终端,所有设置过的别名就失效了

如果想要一直起作用,可以写入到~/.bashrc中

例:$echo 'alias cmd="commad seq"'>>~/.bashrc

#shebang的妙用

把shebang从#!/bin/bash改成#!/bin/bash -xv,这样一来,不用任何其他选项就可以用调试功能了

#读取命令序列输出

shell脚本最棒的特性之一就是可以轻松地将多个命令或工具组合下来生成输出

一个命令的输出可以作为另一个命令的输入,而这个命令的输出又会传递到另一个命令,依次类推

#read

read命令提供一种不需要按回车键就可以完成输入的办法

-s不回显方式读取

-p显示提示信息

-t在特定时限内读取输入

#IFS

内部字段分隔符(Internal Field Separator)

#CSV

逗号分隔型数值(Comma Separated Value)

#for

for var in list;

do

commands;

done

#while

while condition

do

commands;

done

#算术比较

条件通常被放置在封闭的中括号内

一定要注意在[或]与操作数之间有一个空格。

如果忘记这个空格,脚本会报错

[ $var -eq 0 ] #当$var等于0时,返回真

eq 等于 | ne 不等于 | gt 大于 | lt 小于 | ge 大于或等于 | le 小于或等于

#字符串比较

使用字符串比较时,最好用双中括号,因为有时候采用单个括号会产生错误

所以最好避开它们

[ [ $str1 > $str2 ] ]; 如果str1的字母序比str2大,则返回真

[ [ -n $str1 ] ]; 如果str1包含的是空字符串,则返回真

[ [ -n $str1 ] ]; 如果str1包含的是非空字符串,则返回真

#test

test命令可以用来执行条件检测

用test有助于避免使用过多的括号(可以直接代替前面的测试条件)



第二章:命令之乐

#xargs

只要我们把find的输出作为xargs的输入,就必须将-print0与find结合使用,以字符null来分隔输出

find . -type f -name "*.txt" -print0 | xargs -0 rm -f

#tr

tr可以对来自标准输入的字符进行替换/删除/以及压缩。

它可以将一组字符变成另一组字符,因而通常也被称为转换命令(translate)

#md5sum

验和对于编写备份脚本或系统维护脚本来说非常重要

md5sum filename > file_sum.md5

md5sum -c file_sum.md5 #这个命令会输出校验和是否匹配的消息

检验和是从文件中计算出来的。对目录计算校验和意味着我们需要对目录中的所有文件以递归的方式进行计算

#sort

-n 按数字进行排序

-r 按逆序进行排序

-M 按月份进行排序 sort -M months.txt

为了使sort的输出与以\0作为参数的终止符的xargs命令相兼容,采用下面的命令

sort -z data.txt | xargs -0 #终止符\0使得xargs命令的使用更加安全

-b 选项用于忽略文件中前导空白字符

-d 选项用于指明以字典序进行排序

sort命令只能用于由换行符分隔的记录

#uniq

-c 选项统计各行在文件中出现的次数

sort unsorted.txt | uniq -c

#.$$

使用tempfile命令,我们也可以使用自己的临时文件名

temp_file="/tmp/var.$$"

.$$作为添加的后缀会被扩展成当前运行脚本的进程ID

有时候也可以借助随机数

temp_file="/tmp/file_$RANDOM"

#名称.扩展名

${VAR%.*}

从$VAR中删除位于%右侧的通配符,通配符是从右向左进行匹配

%属于非贪婪操作,它从右到左打出匹配通配符的最短结果

%属于贪婪的(greedy),这意味着它会匹配符合条件的最长的字符串

${VAR#*.}

从$VAR中删除位于#右侧的通配符,通配符从左向右进行匹配

#也有一个相对应的贪婪操作符##

VAR=hack.fun.book.txt

echo ${VAR%.*} #hack.fun.book

echo ${VAR%%.*} #hack

echo ${VAR#*.} #fun.book.txt

echo ${VAR##*.} #txt

#grep

在grep中,^标记着单词的开始,$标记着单词的结束

-q禁止产生任何输出

列出文件中以特定单词起头的所有单词

grep "^word" filepath

#echo

-e 忽略转义字符,用于交互输入自动化



第三章:以文件之名

#comm

用于两个文件之间的比较,它有很多不错的选项可用来调整输出,以便我们执行交集/求差以及差集操作

#setuid

setuid权限允许用户以其拥有者的权限来执行可执行文件,即使这个可执行文件是由其他用户运行的

#setgid

setgid允许以同该目录拥有者所在组相同的有效权限来允许可执行文件,但是这个组和实际发起命令的用户组未必相同

#粘滞位

录有一个特殊的权限,叫做粘滞位(sticky bit),当一个目录设置了粘滞位,只有创建该目录的用户才能删除目录中的文件,即使用户组和其他用户也有写权限,用t或T来表示,如果没有设置执行权限,但设置了粘滞位,那么用t,同时设置了执行权限和粘滞位,就用T

chmod a+t directory_name

#chattr

以用chattr将文件设置为不可修改

一旦文件被设置为不可修改,任何用户包括超级用户都不能删除该文件,除非其不可修改的属性被移除

sudo chattr +i file

#iso

可引导光盘自身具备引导能力,也可以运行操作系统或其他软件

不可引导光盘则做不到这些

想保留光盘的可引导性,应该将它以磁盘镜像或是ISO文件的形式进行复制

#tail

tail命令的一个重要用法是从一个内容不断增加的文件中读取数据,新增加的内容总是被添加到文件的尾部,因此当新内容被写入文件时候,可以用tail将其显示出来

tail有一个特殊的选项-f或--follow,它们会使tail密切关注文件中新添加的内容,并随着数据的增加时时保持更新

tail具有一个很有意思的特性,当某个给定进程结束之后,tail也会随之终结

#pushd

pushd和popd是基于命令行接口(CLI)的定位技术

使用pushd和popd的时候,就可以无视cd命令了

pushd /var/www

dirs

pushd +3

当涉及3个以上的目录时,可以使用pushd和popd,但是当你只涉及两个位置的时候,另一个更简便的方法:cd -

#wc

wc是一个用于统计的工具,它是Word Count的缩写,统计文件的行数/行数/字符数

统计行数-l/单词数-w/字符数-c/-L打印最长行的长度

#LOC

line of code,代码行数

#tree

tree命令是以图形化的树状结构打印文件和目录的主角

只重点标记出匹配某种样式的文件

tree path -P PATTERN | tree PATH -P "*.sh"

只重点标记出除符合某种样式之外的那些文件

tree path -I PATTERN

同时打印出文件和目录的大小

tree -h

以HTML形式输出目录树

tree PATH -H http://localhost -o out.html

http://localhost替换为适合存放输出文件的URL,将PATH替换为主目录的真正路径



第四章:让文本飞

#grep

grep命令是UNIX中用于文本搜索的大师级工具

--color 选项可以在输出行中重点票房出匹配到的单词

如果要使用正则表达式,需要添加-E选项,或者使用egrep

-o 只输出文件中匹配到的文本部分

-v 将匹配结果进行反转

-C 只是统计匹配行的数量,并不是匹配的次数

-n 匹配字符串的行数

-b 匹配所位于的字符或字节偏移 选项-b 总是和-o 配合使用

-l 搜索多个文件并找出匹配文本位于哪一个文件中,相反的选项是-L

-R 递归搜索文件

-i 忽略大小写

-e 匹配多个样

-f 提供一个样式文件用于读取样式

--include/exclude/exclude-dir 包括或排除文件

-Z 使用0字节后缀,-Z通常和-l结合使用

-q 静默输出

-A/-B/-C 打印出匹配文本之前或之后的行

#cut

cut是一个帮我们将文本按列进行切分的小工具。它可以指定分隔每列的定界符

在cut的术语中,每列都是一个字段

-f n 提取第n个字段

-s 避免打印出不包含定界符的行

-complement 对提取的字段进行补集运算

-d 指定字段的定界符

指定字段的字符或字节范围 -b表示字节/-c表示字符/-f表示定义字段

当用-b或-c提取多个字段时,必须使用--output-delimiter,否则,你就没法区分不同的字段了

cut range_filelds.txt -cl -3,6-9 --output-delimiter "."

#sed

sed是stream editor(流编辑器)的缩写

sed命令众所周知的一个用法是进行文本替换

-i 可以将替换结果应用于原文件

/在sed中作为定界符使用,当定界符出现在样式内部时,我们必须用前缀\对其进行转义

/pattern/d 移除匹配样式的行

&对应于之前所匹配到的单词

\(pattern\)用于匹配子串,对于匹配到的第一个子串,其对应的标记是\1

在sed表达式中使用一些变量字符串时,双绰号就有用武之地了

#awk

awk被设计用于数据流,可以对列和行进行操作,有很多内建的功能,比如数组/函数

基本结构:awk 'BEGIN{ print "start" } pattern { commands } END{ print "end" }'

当使用不带参数的print时,它会打印出当前行

关于print,需要记住两件事:

一:当print的参数是以逗号进行分隔时,参数打印时则以空格作为定界符

二:在awk的print语句中,双引号是被当做拼接操作符使用的

特殊变量:

NR:number of records,记录数量,对应于当前行号

NF:number of fields,字段数量,对应于当前字段数

$0:这个变量包含执行过程中当前行的文本内容

$1:这个变量包含第一个字段的文本内容

$2:这个变量包含第二个字段的文本内容

print $NF 打印一行中最后一个字段

-v 可以将外部值传递给awk

如果只想读取某一行,可以使用getline函数

-F "delimiter" 明确指定一个定界符

将command的输出读入变量output

"command" | getline output;

#paste

cat命令所实现的拼接通常是按照行来进行的

paste命令实现同按列来拼接

-d 明确指定定界符

#rev

查一个字符串是否是回文的最简单的方法就是使用rev命令

rev命令接受一个文件或stdin作为输入,并逆序打印出每一行内容

tac命令将所有的行进行反转

#context line



第五章:一团乱麻,没这回事

#wget

-O 指定输出文件名

-o 指定日志文件,从而不必将日志信息打印到stdout

-t 指定重试次数

--limit-rate 对wget限速

--quote或-Q 指定下载配额(quota)

-c 从断点开始继续下载

--mirror或者-r 复制或镜像整个网站,-l指定页面层级DEPTH

--use和--password提供认证信息

#cURL

cURL通常将下载文件输出到stdout,将进度信息输出到stderr

--silent 避免显示进度信息

-O 将下载数据写入文件,而非写入标准输出

--progress代替--silent 显示形如###的进度条

-C offset 指定偏移量进行断点续传

-C - 希望以curl推断出正确的续传位置

#shift

shift用来移动参数,当使用shift后,$2的值就被赋给$1



第六章:B计划

#tar

-c 代表创建文件

-f 代表指定文件名,文件名必须紧跟在-f之后

-r 向归档文件中添加文件

-v或-vv(verbose) 在归档或列出归档内容的过程中获知更多细节

-x(exact) 从归档文件中提取文件或文件夹

-C 指定需要将文件提取到哪个目录

-A 拼接多个tar文件

-u 通过检查时间戳来更新归档文件中的内容

-d 可以打印出两者之间的差别

--delete 从给定的归档文件中删除文件

-j 指定bunzip2格式压缩

-z 指定gzip格式压缩

--lzma 指定lzma格式

-a或--auto-compress 支持根据扩展名进行压缩

--exclude[PATTERN]排除匹配通配符样式的文件"样式应该使用双引号来引用"

-X 将需要排除的文件列表放入文件中

-totals 在归档完成后打印出总归档字节数

#cpio

来将多个文件和文件夹存储为单个文件,同时还能保留所有的文件属性,如权限/文件所有权等

ccio通过stdin获取输入文件名,并将归档文件写入stdout,我们必须将stdout重定向到一个文件,以接收cpio的输出

-o 指定了输出

-v 用来打印归档文件列表

-i 用于指定输入

-t 表示列出归档文件中的内容

-d 提取内容

#gzip

gzip只能够压缩单个文件,而无法对目录和多个文件进行归档

gzip filename 压缩文件

gunzip filename.gz 解压文件

-l 列出压缩文件的属性信息

-c 用来将输出指定到stdout

--fafst或--best 分别提供最低或最高的压缩比

归档文件和gzip联用,可以用tar命令的-Z项来压缩归档文件

zcat 无需解压缩,直接读取gzip格式文件

压缩率 我们可以指定压缩率,压缩率分为1到9级

#bzip2

通常能生成比gzip更小的文件

bzip2 filename

bzip2会删除原文件并生成名为filename.bzip2的压缩文件

bunzip2 filename.bz2

-C 用于将输出指定到stdout

用tar命令的-j选项来压缩归档文件

-k 避免删除输入文件

压缩率 1到9

#lzma

提供了比gzip或bzip2更好的压缩率

lzma filename

lzma会删除原文件并生成名为filename.lzma的压缩文件

unlzma filename

-c 用以将输出指定到stdout

tar命令的-lzma选项来压缩归档文件

-k 避免删除输入文件

压缩率 1到9

#zip

在internet上的文件通常都是采用这种格式

zip file.zip file 压缩文件

-r 指定递归操作

unzip file.zip

在完成操作后,unzip并不会删除file.zip

-u 更新归档文件中的内容

-d 从压缩归档文件删除内容

-l 列出归档文件中的内容

#squashfs

squashfs是一种只读型的超高压缩率文件系统

它将根文件系统保存在一个压缩过的文件系统文件中,这个文件可以使用环回的形式来挂载并对其中的文件进行访问,因此当进程需要某些文件,可以将它们解压,然后载入内存中使用.

如果需要构建一个定制的Live OS,或是需要使用超高压缩率的文件并且无需解压就可以访问文件,那么squashfs的相关知识就能派上用场

需要安装squshfs-tools软件包

要挂载squashfs文件,利用环回形式进行挂载

-e 在创建squashfs文件时排除部分文件

-ef 将需要排除的文件名写入文件

-wildcard 在排除文件列表中使用通配符

#ctypt

简单的加密工具

ctypt < input_file > output_file

-d 解密文件

ctypt PASSPHRASE -d < encrypted_file > output_file

#gpg

GNU privacy guard,GNU隐私保护,是一种广泛的加密方案,采用密钥签名技术

gpg -c filename 加密文件

gpg filename.gpg 解密文件

#md5sum与sha1sum

都是单向散列算法,均无法逆推出原始数据,常用于验证数据完整性或为特定数据生成唯一的密钥

#rsync

可以对位于不同位置的文件和目录进行备份,它可以借助差异计算以及压缩技术来最小化数据传输量

rsync会比较源端和目的端的文件,只有当文件有更新时才进行复制,rsync也支持压缩/加密等多种特性

rsync -av source_path destination_path

-a 表示要进行归档

-v(verbose) 表示在stdout上打印出细节信息或进度

rsync命令用SSH连接远程主机,用user@host这种形式设定远程主机的地址

-z 指定在网络传输时使用数据压缩

--exclude 在归档时排除部分文件

--exluce-from 通过一个列表文件指定需要排除的文件

--delete 在更新rsync备份时,删除不存在的文件

#git

将整个源目录复制到目的端的备份目录中,并使用日期或时间作为版本号,得用差异备份(differential backup)



第七章:无网不利

#ifconfig

用于显示网络接口/子网掩码等信息

在每个系统中,默认都有一个称之为环回接口的io,这个接口指向当前主机本身

#网关

能够向外部网络转发分组的特殊节点主机被称为网关

操作系统维护着一个被称为路由表(routing table)的表格,它包含了关于分组如何转发以及通过网络中的哪些节点转发的信息

#route 显示路由表

route -n 指定以数字形式显示地址

#tarceroute

可以显示分组途径的所有网关的地址

#ping

一个验证网络上两台主机连通性的诊断工具

ping命令使用互联网控制消息协议(internet control message protocal,ICMP)的echo分组

用ctrl+C来停止ping命令

RTT Round Trip Time,往返时间,是分组从源主机到目的主机的往返时间

-c 限制所发送的echo分组的数量

#wait

要想等所有子进程结束之一再终止脚本,得用wait命令

将wait放在脚本最后,它就会一直等到所有的子进程全部结束

#fping

-a 指定打印出所有活动主机的IP地址

-u 指定打印出所有无法到达的主机

-g 指定走上IP地址记法中生成IP地址范围

-d 通过对每一个echo回应进行DNS查询来返回主机名,使用该选项可以在ping的回应信息中打印出主机名而非IP地址

#lftp

通过FTP传输文件可以使用lftp命令,通过SSH传输文件可以使用sftp,RSYNC使用SSH与rsync命令

lftp username@ftphost 连接FTP服务器传输文件

cd directory改变目录

lcd改变本机的目录

mkdir创建目录

get filename 下载文件

put filename 上伟文件

quit

lftp提示符支持命令自动补全

-i 关闭用户的交互会话

#iwconfig

iwconfig命令用来为无线网卡配置适合的无线网络,WEP密钥以及频率

iwlist工具扫描并列出可用的无线网络

iwlist scan

#zenity

可以用zenity来实现一个GUI弹出窗口.zenity是一个脚本化的GUI工具,用来创建包括文本框/输入框等在内的窗口

zenity --info --text "This is a message"

zenity依赖于Xserver.Xserver是一个守护进程,它负责在屏幕上绘制GUI图开元素

Xserver用特殊的环境变量DISPLAY跟踪运动在系统中的Xserver实例

#&

在语句末尾加上一个&符号,这个符号的作用是将语句放置到后台运行.这样做有利于多条语句并行执行

#lsof

开放端口列表不仅能够协助检测恶意软件,而且还能够收集开放端口的相关信息对网络应用程序进行调试,它可以用来分析某些端口无法连接及端口侦听功能是否正常

lsof -i 列出系统中的开放端口以及运行在端口上的服务详细信息

netstat -tnp 列出开放端口与服务



第八章:当个好管家

#du

df是disk free的缩写,du是disk usage的缩写

du file.txt

统计结果默认以字节作为计算单位

-a 递归地输出指定目录或多个目录中所有文件统计结果

-h 采用更友好的格式进行打印

-c 输出作为命令参数的所有文件和目录的磁盘使用情况总计,它会在输出结果的末尾加上一行总计

-s(summarize) 只输出合计数据,可以配合-h打印出人们易读的格式

-b 以字节为单位输出

-k 以KB为单位

-m 以MB为单位

-B 以指定块为单位的大小

--exclude 排除部分文件

--exclude-from 排除列表文件

--max-depth 指定du应该遍历的目录层次的最大深度

#time

time用来计算命令执行时间,将time放在需要计算执行时间的命令之前

real 挂钟时间,也就是命令从开始执行到结束的时间

user 指进程花费在用户模式中的CPU时间,这是唯一真正用于执行进程所花费的时间

sys 指进程花费在内核模式中的CPU时间

-o filename 将命令执行时间写入文件

-ao 要将命令执行时间添加到文件而不影响其原有内容,使用选项-a以及-o

-f 利用格式字符串格式化时间输出 real -%e|user -%U|sys -%S

#history

之前用户输入过的命令,并将其存储在文件~/.bash_history中,不过,它只保留特定数量的最近执行过的命令

可以用命令histroy或cat ~/.bash_history查看命令历史记录

#$0

在shell和awk中,都有一个变量$0,尽管变量名相同,但是含义却不同

对于shell来说,$0中包含的是命令行的第一个单词

对于awk来说,$0来说,$0中包含当前记录

#ps

ps命令用于收集系统中进程的详细信息.

包括CPU使用情况,正在执行的命令,内存使用,进程状态等

comm表示命令名(command name)

pcpu表示CPU使用率(CPU usage in percent)

#watch

watch命令不断地查看输出

可以用来在终端中以固定的时间间隔监视命令输出

-n SECOND 命令默认每2秒更新一次,指定需要更新输出的时间间隔

-d 可以将时间间隔前后的命令输出差异以不同颜色突出标示出来



第九章:管理重任

#ps

-f 显示包含更多信息的更多列

-e(every)或-a(all) 获取运行在系统中的每一个进程的信息

-o 指定想要显示的列,以逗号操作符作为定界符

可以用--sort对ps命令的输出根据特定的列进行排序,在参数前面加上+(升序)或-(降序)来指定排序方式

-t 指定进程所属的TTY

-L 在ps输出中显示线程的相关信息

NLWP 进程的线程数量 | NLP 每个条目的线程ID

ps -eo cmd e 显示进程的环境变量,这种环境跟踪方法的用途之一就是解决apt-get软件包管理器的故障

#top

默认会输出一个占用CPU最多的进程列表

#pgrep

可以获得一个特定命令的进程ID列表

-d 指定输出定界符,而不以换行符作为定界符

-u 指定进程的用户列表

-c 返回所匹配的进程数量

#kill

每一种信号都同一个整数值相关联,当进程接收到一个信号时,它会通过执行对就的信号处理程序来进行响应

像Ctrl+C/Ctrl+Z这种作业都属于信号

kill命令可用来向进程发送信号,而trap命令用来处理所接收的信号

kill -l 列出所有可用的信号

kill PROCESS_ID_LIST 终止一个进程

kill -s SIGNAL PID 通过kill命令向进程发送指定的信号

#killall

-u 通过名称以及所属用户名指定进程

-i 在杀死进程前进行确认

#trap

在脚本中用来为信号分配信号处理程序

一旦使用trap将某个函数分配给一个信号,那么当脚本运行时收到这个信号,对应于信号的函数就会开始执行

通过trap命令,我们能够为任意可用的信号(kill -l)定义处理程序,也可以为多个信号指定单个信号处理程序

#which

用来找出某个命令的位置

#whereis

它不仅返回命令的路径,还能够打印出其对应的命令手册的位置以及命令源代码的路径(如果有的话)

#file

用来确定文件的类型

#whatis

输出作为参数的命令的简短描述信息

#wall

wall命令用来向所有当前登录用户的终端写入消息

终端是作为设备存在的,因此那些打开的终端在/dev/pts/中都会有对应的设备节点文件.向特定的设备写入数据将会在对应的终端中显示出消息

mesg y 允许写入消息

mesg n 禁止写入消息

#uname

uname -n 打印当前系统的主机名 | hostname

uname -a 打印linux内核版本/硬件架构等详细信息

uname -r 打印内核发行版本

uname -m 打印主机类型

cat /proc/cpuinfo 打印CPU的相关信息

cat /proc/meminfo 打印内存在的详细信息

cat /proc/partitions 列出系统的分区信息 | fdisk -l

lshw 获取系统的详细信息

#/proc

/proc是一个位于内存中的伪文件系统(in-memory pseudo filesystem),它的引入是为了提供一个可以从用户空间(user space)读取系统参数的接口

每一个运行的进程在/proc中都有一个对应的目录.进程的目录名和进程ID相同

environ 包含与进程相关联的环境变量

ext 是一个到进程工作目录的符号链接

fd 包含了由进程所使用的文件描述符

#cron

安排脚本在某个时间或每隔一段时间来运行

cron通过守护进程cron使得任务能够以固定的时间间隔在系统后台自动运行

cron利用一个被称为"cron表(cron table)"的文件,这个文件中存储了需要执行的脚本或命令的调度列表以及执行时间

命令crontab用来添加高度条目

每一位用户都有自己的cron调度,通常这也被称为一个cron作业

分钟(0~59)|小时(0~23)|天(1~31)|月份(1~12)|工作日(0~6)|命令

星号(*)指定了命令应该在每一个时间阶段执行

如果希望在某个特定时段执行命令,那么就在对应的时间字段中指定时段,并用逗号分隔,如"5,10"

*/5 可以每5分钟执行一次命令

-l 列出现有的cron表中的内容

-u 指定用户名来查看其他用户的cron表

-r 移除当前用户的cron表

#mysql

要处理mysql数据库,系统中必须安装mysql-server和mysql-client软件包

SET @i:=0是一个SQL构件(SQL construct),用来设置变量i=0


本文出自 “成鹏致远” 博客,请务必保留此出处http://infohacker.blog.51cto.com/6751239/1155196

原创粉丝点击