Shell 总结
变量替换和测试
变量替换
变量测试
字符串处理
计算字符串长度
方法一
1 | ${#string} |
方法二
1 | expr length "$string" |
- string 有空格,则必须加双引号
子串
获取子串在字符串中的索引位置
1 | expr index $string $substring |
抽取子串
总结
计算字符串长度
1 | var1="This is a app" |
子串索引
1 | var1="quicstart is a app" |
子串长度
1 | var1="quicstart is a app" |
抽取字符串中的子串
1 | var1="quicstart is a app" |
- expr 索引1开始,${string:position}从0开始
实例
1 | #! /bin/bash |
命令替换
语法格式
例题
获取系统所有用户并输出
1 | cat /etc/passwd | cut -d “:” -^C #分割的第一个 |
1 | #! /bin/bash |
总结
\
`和
$()`
- ``和$()是等价的,但初学推荐$()
- $(())用于进行整数运算,包括加减乘除
- $(((100 + 30) / 12))
有类型变量
declare 和 typeset 命令
两者关系:两者等价,都是用来定义变量类型
declare参数表
1 | # 声明整数型变量 |
declare -x
声明为环境变量,可在脚本中直接使用
取消声明的变量
1 | declare +r |
数学运算
语法格式
expr操作符对照表
例子
Bash运算之bc
bc 操作
1 | scale=2 #精确到小数点后两位 |
函数
函数定义和使用
Linux Shell中的函数和大多数编程语言中的函数一样,将相似的任务或代码封装到函数中,供其他地方调用
语法一
1 | name() |
语法二
1 | function name |
如何调用
- 直接使用函数名调用,可以将其想象成 Shell 中的一条命令
- 函数内部可以直接使用函数 $1,$2…$n
- 调用函数:function_name $1 $2
例题
1 | #!/bin/bash |
判断进程
1 | netstat -tnlp | grep :80 |
向函数传递参数
shell传参
1 | function name |
函数返回值
- return
- echo
reutrn 返回值
- 只能返回1-255的整数
- 通常智能用来供其他地方调用获取状态,因此仅返回0(成功),1(失败)
echo 返回值
- 可以返回任何字符串结果
- 通常用于返回数据,如一个字符串值或列表值
局部变量全局变量
全局变量
- 不做特殊处理,shell 中变量为全局变量
- 大型脚本程序慎用
局部变量
- 定义时,使用local关键字
- 函数内外如果存在同名变量,册函数内部变量覆盖外部变量
函数库
- 为什么要定义函数库
- 经常使用的重复代码封装成函数文件
- 一般不直接执行,而是由其他脚本调用
实例
1 | function add |
1 | #!/bin/bash |
注意事项
- 库文件名的后缀是任意的,但一般用
.lib
- 库文件通常没有可执行权限
- 库文件无需和脚本在同级目录,只需在脚本中引时指定
- 第一行一般使用
#!/bin/bash/echo
,输出警告信息,避免用户执行
文件查找之find命令
find [路径][选项][操作]
实例
查找 /etc 目录下 conf 结尾的文件
1
fing /etc -name '.conf'
查找当前目录下文件名为 aa 的文件,不区分大小写
1
find . -name aa
查找文件属主为 hdfs 的所有文件
1
find . -user hdfs
查找文件属组为 yarn 的所有文件
1
find . -group yarm
选项
-type
- f 文件 find . -type f
- d 目录 dind . -type d
- c 字符设备文件 dind . -type c
- b 块设备文件 dind . -type b
- l 链接文件 dind . -type l
- p 管道文件 dind . -type p
-size
- -n 大小小于n的文件
- +n 大小大于n的文件
- n 大小等于n的文件
1
2
3
4# 小于10000字节的文件
find /etc -size -10000c
# 大于1M的文件
find /etc -size +1M-mtime
- -n n天以内修改的文件
- +n n天以外修改的文件
- n 正好n天修改的文件
1
2
3
4#查找/etc下5天内修改的conf结尾的文件
find /etc -mtime -5 -name '*.conf'
# 查找10天之前修改且属主为root的文件
find /etc -mtime +10 -user root-mmin
- -n n分种内修改的文件
- +n n分钟外修改的文件
1
2
3
4# 30分钟前修改的文件
find /etc -mmin +30
# 30分钟内修改的目录
find /etc -mmin -3o -type d-mindepth n
- 表示从n级子目录开始搜索
1
find /etc -mindepth 3
-maxdepth n
- 表示最多搜索n-1级子目录
1
2
3find /etc -maxdepth 3 -name '*.conf'
find ./etc -type f -name '.*conf' -size +10k -maxdepth 2
find . -type f -nogroup-perm
- find .perm 644
-prune
- 通常和-path一起用,用于将特定目录排除在搜索条件之外
1
2
3
4
5
6
7
8# 查找当前目录下所有普通文件,排除test目录
find . -path ./etc -prune -o -type f
# 查找当前目录下所有普通文件,但排除etc和opt目录
find . -path ./etc -prune -o -path ./opt -prune -o -type f
# 当前目录所有普通文件,排除etc和opt目录,但属主为hdfs
find . -path ./etc -prune -o -path ./opt -prune -o -type -f -a -user hdfs
# 当前目录所有普通文件,排除etc和opt目录,但属主为hdfs,文件大小大于
find . -path ./etc -prune -o -path ./opt -prune -o -type -f -a -user hdfs -a -size +2M-newer file1
1
find /etc -newer a
操作
1
2
3
4
5# 搜索/etc下的文件非目录,以conf结尾,大于19k,然后删除
find ./etc -type -f -name '*.conf' -size +10k -exec rm -rf {} \;
find /var/log/ -name '*.log' -mtime +7 -exec rm -rf {} \;
find /etc -size +10k -type -f -name '*.conf' -exec cp {} /root/conf/ \;- -print 打印输出
- -exec 对搜索的文件执行特定的操作
- -ok 和exec功能一样,但每次操作都会给用户提示
逻辑运算符
- -a 与
- -o 或
- -not|! 非
find locate whereis和 which 总结及使用场景分析
locate
- 文件查找命令,所属软件包mlocate
- 不同于find命令是在整块磁盘中搜索,locate在数据库文件中查找
- find默认全部匹配,locate默认部分匹配
- updatedb命令
- 用于更新/var/lib/mlocate/mlocate.db
- 所使用配置文件/etc/update.conf
- 该命令在后台cron计划任务定期执行
whereis选项和含义
- -b 只返回二进制文件
- -m 只返回帮助文档文件
- -s 只返回源码文件
which
- 仅查找二进制程序文件
- 选项
- -b 只返回二进制文件
各命令使用场景推荐
grep和egrep
grep
语法
- grep [option] [pattern] [file1,file2…]
- command | grep [option] [pattern]
grep参数
- grep -E “python | PYTHON” file
egrep
egrep语法
1 | egrep(选项)(查找模式)(文件名1,文件名2,……) |
sed
sed(Stream Editor),流编辑器,对标准输出或文件逐行进行处理
语法
- stdot | sed [option] “pattern command”
- sed [option] “pattern command” file
选项
1 | sed ‘p’ sed.tet # p打印出来 |
sed 的pattern详解
1 | # 打印file的17行 |
sed 中的编辑命令
1 | sed -i‘1d/ sed.txt # 删除第一行 |
反向引用
- 是什么
- &和\1 引用模式匹配到的整个串
1 | # file中寻找1开头的后跟两任意字符以e结尾的字符 |
- 上面两种方法实现一样的功能,分别使用&和\1代表搜寻到的整个字符串
- 区别在于&只能表示匹配到的完整字符串,只能引用整个字符串,而\1可以使用()对匹配到的
- 要替换匹配的字符串的一部分,name必须使用\1,不能使用&
sed 引用变量
- 注意
- 匹配模式中存在变量,则建议使用双引号
- sed中需要引入自定义变量时,如外面使用单引号,则自定义变量必须使用单引号
用 sed 查询特定内容
查询命令
实例
1 | # 打印/etc/passwd中的第20行内容 |
1 | #!/bin/bash |
sed 删除特定内容
1 | sed -i '15d' passwd |
- 删除配置文件中所有的注释行和空行
- 在配置文件中所有不以#开头的行前面加×符合,主要以#开头的行不添加
1 | sed -i ‘/^#/d;/^$/d’ nginx.conf #删除注释 |
sed 修改文件内容
实例
1 | #修改第一行的root为ROOT |
sed 追加文本内容
- 语法
a
1
2
3
4
5
6# 第十行后追加"Add lind behind"
sed -i '10a Add lind behind' passwd
# 第10到20行,每一行后面都追加"Test line behind"
sed -i '10,20a Test line behind' passws
# 匹配到/bin/bash的行后面追加"insert line for /bin/bash behind"
sed -i '/\bin\/bash/a insert line for /bin/bash behind' passwsi
1
2
3
4# 匹配yarn开头的行,在匹配航后面追加"Add lind behind"
sed -i 'yarn/i Add lind behind' passwd
# 每一行前面都追加“insert line before every line"
sed -i 'i insert line before every line' passwdr
1
2
3
4
5
6#将/etc/fstab文件的内容追加到passwd的第20行后面
sed -i '20r /etc/fstab' passwd
#将/etc/inittab文件内容追加到passwd文件匹配/bin/bash行后面
sed -i '/\bin\/bash/r /etc/inittab' passwd
#将/etc/vconsol.conf文件内容追加到passwd文件中特定行的后面,匹配以ftp开头的行后面
sed -i /^ftp/,18r /etc.vconsole.conf’ pssswdw
1
2
3
4# 将passwd文件匹配到/bin/bash的行追加到/tmp/sed.txt文件中
sed -i '/\bin\/bash/w /tmp/sed.txt' passwd
# 将passwd文件从10行还是到匹配到hsfs开头的所有行内容追加到/tmp/sed-1.txt
sed -i '10,/^hsfs/w /tmp/sed-1.txt' passwd
awk
awk 的工作模式
- awk 为一个文本处理工具,通常用于处理数据并产生结果报告。
- 命名是由三个创始人姓氏首字母组成
语法
- awk ‘BEGIN{}pattern{commands}END{}’ file_name
- standard outpu | awk ‘BEGIN{}pattern{commands}END{}’ file_name
- 语法格式说明
awk 内置变量
内置变量对照表
1 | awk '{print $0}END{}' /etc/passwd |
awk 格式化输出
printf(默认不带分隔符) 语法
修饰符
实例
1 | # 不加任何修饰输出 |
1 | # 以字符串格式打印/etc/passwd中的第七个字段,以":"为分隔符 |
awk模式匹配的两种用法
- 语法
- RegExp 含义:按正则表达式匹配
- 关系运算 含义:按关系运算匹配
实例
- RegExp
1 | # 匹配/etc/passwd文件中含有root字符串的所有行 |
(运算符)关系运算
- 关系运算符:<,><=,>=,==,!=,~(匹配正则表达式),!~
1
2
3
4
5
6
7
8
9
10
11# 以:为分隔符,匹配第3个字段小于50的所有行信息
awk 'BEGIN{FS=":"}$3<50{print $0}' /etc/pssswd
awk 'BEGIN{FS=":"}$7=="/bin/bash"{print $0}' /etc/pssswd
awk 'BEGIN{FS=":"}$7!="/bin/bash"{print $0}' /etc/pssswd
# 第三个字符包含3个以上数字的所有行信息
awk 'BEGIN{FS=":"}$3~/[0-9]{3,}/{print $0}' /etc/passwd # {3,}重复3次
awk 'BEGIN{FS=":"}$0~/\sbin\/nologin/{print $0}' /etc/passwd- 布尔运算:||(或),&&(与),!(非)
1
2
3
4
5
6
7# 以:为分隔符,匹配文件中包含hdfs或者yarn的所有行信息
awk 'BEGIN{FS=":"}$1=="hdfs" || $1=="yarn" {print $0}' /etc/passwd
# 第三字段小于50且第四字段大于50的所有行信息
awk 'BEGIN{FS=":"}$3<50 && $4>50 {print $0}' /etc/passwd
awk 'BEGIN{FS=":"}$3<50 && $7~/\bin\/bash/ {print $0}' /etc/passwd
awk 动作中的表达式用法
算术运算符
运算符 | 含义 |
---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
% | 模 |
^或** | 乘方 |
++X | 再返回X变量之前,X变量加1 |
X++ | 再返回X变量之后,X变量加1 |
- 实例
1 | awk 'BEGIN{var=20;var1="hello";print var,var1}' |
1 | # 计算文件中空白行数量 |
awk 动作中的条件及循环语句
- 条件语句
1 | if(条件表达式) |
- 实例
1 | # 以:为分隔符只打印第3个字段的数值在50-100范围内的行信息 |
vim scripts.awk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17BEGIN{
FS=":"
}
{
f($3<50)
{
printf "%-30s%-20s%-5d\n","小于50的UID",$1,$3
}
else if($3>50 && $3<100)
{
printf "%-30s%-20s%-5d\n","大于50且小于100的UID",$1,$3
}
else
{
printf "%-30s%-20s%-5d\n","大于100的UID",$1,$3
}
}使用
1 | awk -f scripts.awk /etc/passed |
- 循环语句
do while 循环
1
2
3
4do while
do
动作
while(条件表达式)for 循环
1 | for (初始化计数器;测试计数器;计数器变更) |
实例
- 1+2+…100的和
while
1
2
3
4
5
6
7BEGIN{
while(i<=100)
{
sum+=1
}
print sum
}- awk -f while.wak
for
1
2
3
4
5
6
7BEGIN{
for(i0;i<=100;i++)
{
sum+=1
}
print sum
}- awk -f for.awk
do while
1
2
3
4
5
6
7
8BEGIN{
do
{
sum+=1
i++
}while(i<=100)
print sum
}- awk -f do_while.awk
- 计算每个同学平均分,仅显示大于90
1 | BEGIN{ |
- 计算平均分大于90的各科总分
1 | BEGIN{ |
awk 中的字符串函数
字符串函数对照表
例子
- 以:为分隔符,返回文件中每行中的字段长度
1 | # NF 字段个数 |
- 搜索字符串”I have a dream”中出现”ea”子串的位置
1 | # 方法1 |
- 将字符串”Hadoop is a bigdata Framework”全部转为小写
1 | awk 'BEGIN{str="Hadoop is a bigdata Framework";print tolower(str)}' |
- 上一题转为大写
1 | awk 'BEGIN{str="Hadoop is a bigdata Framework";print toupper(str)}' |
- 将字符串”Hadoop Kafka Spark Storm”按空格为分隔符,分割每一部分保存到数组arr中
1 | awk 'BEGIN{str="Hadoop Kafka Spark Storm";split(str,arr," ");print arr[0]}' |
- 搜索字符串”Tranction 2345 start:select * from master”第一个数字出现的位置
1 | awk 'BEGIN{str="Tranction 2345 start:select * from master";location=match(str,/[0-9]/);print location}' |
- 正则表达式要用
//
引起来
- 截图字符串”transaction start”的子串,条件从第4个字符开始,截取5为
1 | awk 'BEGIN{str="transaction start";print substr(str,4,5)}' |
- 替换”Tranction 243 start,Event ID:9002”中第一个匹配到的数字为$符号
1 | awk 'BEGIN{str="Tranction 243 start,Event ID:9002";count=sub(/[0-9]+/,"$",str);print count,str}' |
awk中的常用选项
选项 | 解释 |
---|---|
-v | 参数传递 |
-f | 指定脚本文件 |
-F | 指定分隔符 |
-V | 查看awk的版本号 |
实例
- -v 把外部变量引入
1 | num1=20 |
- -f 引入文件
1 | awk -f student.awk /etc/passws |
- -F
1 | awk -F ":" '{print $7}' /etc/passwd |
Shell 数组的用法
- array=(“Mike”,”Bell”,”Hellen”)
- 下面的
井
为#
- 下面的
解释 | 代码 |
---|---|
打印元素 | echo ${井array[2]} |
打印元素个数 | echo ${井array[@]} / echo ${井array[*]} |
打印元素长度 | echo ${井array[3]} |
给元素赋值 | array[3]=”LI” |
删除元素 | unset array[2];unset array |
分片访问 | echo ${井array[@]:1:3} |
元素内容替换 | ${array[@]/e/E} #只替换第一个e;${array[@]//e/E}替换全部e |
- 数组遍历
1 | for a in array |
awk 数组用法
- awk中使用数组时,不仅可以使用数字作为数组下标,也可以使用字符串作为数组下标
- 统计主机上所有TCP连接状态,按照每个TCP状态分类
1 | netstat -an | grep tcp | awk '{arrary[$6]++}END{for(a in arrary) print a,arrary[a]}' |
- 计算横向数总和,计算纵向数据总和
1 | Allen 80 90 96 98 |
1 | BEGIN{ |
awk 处理数据例子
生成随机数据
1 | #!/bin/bash |
- 统计每个用户分别插入多少record
1 | BEGIN{ |
- 统计每个用户分别插入成功和失败各多少record
1 | BEGIN{ |
- 将例子1,2结合,一起输出每个用户分别插入多少条数据,成功失败各多少条
1 | BEGIN{ |
- 在例子3的基础上,加上结尾,统计全部插入记录数,成功记录数,失败记录数
- 方法一
1 | BEGIN{ |
- 方法二
1 | BEGIN{ |
- 查找丢失数据的现象,也就是成功+失败的记录数不等于一共插入的记录数,找出这些数据并显示行号和对应行的日志信息
1 | awk '{if($8!=$14+$17) print NR,$0}' db.log.20190722 |