9、函数
1.系统函数
1、basename 基本语法
basename [string / pathname] [suffix]
basename 命令会删掉所有的前缀包括最后一个(‘/’)字符,然后将字符串显示出来。
选项:
suffix 为后缀,如果 suffix 被指定确定的后缀值了,basename 会将 pathname 或 string 中的后缀去掉。
2、dirname 基本语法
dirname 文件绝对路径
从给定的包含绝对路径的文件名中去除文件名 (非目录的部分),然后返回剩下的路径(目录的部分)
10、自定义函数
1.基本语法
[ function ] funname[()] { Action; [return int;] } funname
2.经验技巧
(1)必须在调用函数地方之前,先声明函数,shell 脚本是逐行运行。不会像其它语言一 样先编译。
(2)函数返回值,只能通过$?(代表了上个命令的退出状态,或函数的返回值)系统变量获得,可以显示加 return 返回,如果不加,将以最后一条命令运行结果,作为返回值。
11、Shell 工具
1.cut
cut 的工作就是“剪”,具体的说就是在文件中负责剪切数据用的。cut 命令从文件的每一行剪切字节、字符和字段并将这些字节、字符和字段输出。
1、基本用法
cut [选项参数] filename
说明:默认分隔符是制表符
2、选项参数说明
- -f: 列号,提取第几列
- -d: 分隔符,按照指定分隔符分割列
- -c: 指定具体的字符
[admin@ datas]$ vim words hello world !! hadoop spark hive 张三 李四 王五 (1)根据" "切割 words 第一、三列 [admin@ datas]$ cut -d " " -f 1,3 words (2)根据" "切割,获取 words 第 2 行第 1 列 [admin@ datas]$ cat words | head -n 2 | tail -n 1 | cut -d " " -f 1 (3)选取系统 PATH 变量值 [admin@ datas]$ echo $PATH 选取系统 PATH 变量值,第 2 个“:”开始后的所有路径: [admin@ datas]$ echo $PATH | cut -d : -f 2- 选取系统 PATH 变量值,第 4 列(包括第 4 列)之前的所有路径: [admin@ datas]$ echo $PATH | cut -d : -f -4 选取系统 PATH 变量值,第 2 到 4 列(包括第 2 到 4 列)之间的所有路径: [admin@mc datas]$ echo $PATH | cut -d : -f 2-4 (4)切割 ifconfig 后打印的 IP 地址 [admin@ ~]$ ifconfig 获取 ip 地址 [admin@ ~]$ ifconfig | grep "inet" | tail -n 1 | cut -d " " -f 9- | cut -d " " -f 2
2.sed
sed 是一种流编辑器,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”,接着用 sed 命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变, 除非你使用重定向存储输出。
1、基本用法
sed [选项参数] ‘command’ filename
2、选项参数说明
-e: 直接在指令列模式上进行 sed 的动作编辑 -i: 直接编辑文件
3、命令功能描述
- a: 新增,a 的后面可以接字串,在下一行出现
- d: 删除
- s: 查找并替换
(0)数据准备 [admin@ datas]$ vim words hello world !! hadoop spark hive 张三 李四 王五 (1)将“lucy 1234 www”这个单词插入到 words 第二行下,打印。 [admin@ datas]$ sed "2a lucy 1234 www" words (2)删除 words 文件所有包含 h 字母的行 [admin@ datas]$ sed "/h/d" words (3)将 words 文件中 h 字母替换为 H 字母 [admin@ datas]$ sed "s/h/H/g" words 注意:‘g’表示 global,全部替换 (4)将 words 文件中的第二行删除并将 h 字母替换为 H 字母 [admin@ datas]$ sed -e "2d" -e "s/h/H/g" words
3.awk
一个强大的文本分析工具,把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行分析处理。
基本用法
awk [选项参数] ‘pattern1{action1} pattern2{action2}...’ filename
- pattern:表示 AWK 在数据中查找的内容,就是匹配模式
- action:在找到匹配内容时所执行的一系列命令
1、选项参数说明
- -F: 指定输入文件折分隔符
- -v(小写): 赋值一个用户定义变量
(1)awk+action 简单示例 数据准备 [admin@ datas]$ vim words hello world !! hadoop spark hive 张三 李四 王五 找到 words 文件的第 1 列 [admin@ datas]$ awk '{print $1}' words (2)awk+pattern+action 示例 awk 可以使用正则 搜索 passwd 文件,以:分隔,输出以 a 字母开头的所有行 [admin@ datas]$ awk -F ':' '/^a/{print $0}' passwd 搜索 passwd 文件,以:分隔,输出以 a 字母开头的所有行的第 1 列和第 6 列,两列之间加上--字符 [admin@ datas]$ awk -F ':' '/^a/{print $1"--"$6}' passwd 注意:有正则的时候,只有匹配了 pattern 的行才会执行 action 搜索 passwd 文件,输出以 a 字母开头的所有行的第 1 列和第 6 列,以--分割,且在开头第一行的上面添加一行列名“1 列”“6 列”,以--分隔,在最后一行的下面添加一行内容"这是所有的以 a 开头的行的 1、6 两列"。 [admin@ datas]$ awk -F ':' 'BEGIN{print "1 列--6 列"} /^a/{print $1"--"$6} END {print "这是所有的以 a 开头的行的 1、6 两列"}' passwd 注意:BEGIN 在所有数据读取行之前执行;END 在所有数据执行之后执行。 搜索 passwd 文件,输出以 a 字母开头的所有行的第 1 列和第 6 列,以空格分割,且在开头第一行的上面添加一行列名“行号”“1 列”“6 列”,以空格分隔,在最后一行的下面添加一行内容"这是所有的以 a 开头的行的 1、6 两列",并且给每一行一个行号(从 1 开始,以此类推,不包括开始和结束行) [admin@ datas]$ awk -v i=0 -F ':' \ 'BEGIN{print "行号","1 列","6 列"} \ /^a/{print ++i,$1,$6} \ END {print "这是所有的以 a 头的行的 1、6 两列"}' \ passwd
2、运算符
= += -= *= /= %= ^= **= 赋值 ?: C 条件表达式 || 逻辑或 && 逻辑与 ~ 和 !~ 匹配正则表达式和不匹配正则表达式 < <= > >= != == 关系运算符 空格 连接 +- 加,减 * / % 乘,除与求余 + - ! 一元加,减和逻辑非 ^ *** 求幂 ++ -- 增加或减少,作为前缀或后缀 $ 字段引用 in 数组成员 搜索 passwd 文件,输出以 a 字母开头的第 2、3 行的第 1 列和第 6 列,以空格分割,且在开头第一行的上面添加一行列名“行号”“1 列”“6 列”,以空格分隔,在最后一行的下面添加一行内容"这是所有的以 a 开头的行的 1、6 两列",并且给每一行一个行号 (从 1 开始,以此类推,不包括开始和结束行) [admin@ datas]$ awk -v i=0 -F ':' '/^a/{print ++i,$1,$6}' passwd | awk 'BEGIN{print "行号","1 列","6 列"} $1>=2&&$1<=3 {print $0} END {print "这是 所有的以a 头的行的1、6两列"}'
print 和 printf
[admin@ datas]$ vim person 张三 男 13 想要把上面 person 的内容输出为以下这样: 姓名:张三 性别:男 年龄:13 使用 print,print 输出字符串,直接拼接 [admin@ datas]$ awk '{print "姓名:"$1"\n 性别:"$2"\n 年龄:"$3}' person
使用 printf,printf 是格式化输出,语法格式为 printf 格式 参数
[admin@ datas]$ awk '{printf ("姓名:%s\n 性别:%s\n 年龄:%d\n",$1,$2,$3)}' person
注意:printf 可以直接在命令行使用,但是在命令行中使用和在 awk 中有些区别
直接使用是这样写
[admin@ datas]$ printf "姓名:%s\n 性别:%s\n 年龄:%d\n" "张三" "男" 11
常用转义字符
\" - 转义后的双引号 \\ - 转义后的反斜杠 \b - 退格符 \n - 换行符 \r - 回车符 \t - 水平制表符 \v - 垂直制表符 %% - 单个%符号
格式化常用的类型转换符
%d - 将参数打印为十进制整数 %f - 将参数打印为浮点数 %s - 将参数打印为字符串 %x - 将参数打印为十六进制整数 %o - 将参数打印为八进制整数
3、awk 的内置变量
$n 当前记录的第 n 个字段,字段间由 FS 分隔 $0 完整的输入记录 ARGC 命令行参数的数目 ARGIND 命令行中当前文件的位置(从 0 开始算) ARGV 包含命令行参数的数组 CONVFMT 数字转换格式(默认值为%.6g) ENVIRON 环境变量关联数组 ERRNO 最后一个系统错误的描述 FIELDWIDTHS 字段宽度列表(用空格键分隔) FILENAME 当前文件名 FNR 各文件分别计数的行号 FS 字段分隔符(默认是任何空格) IGNORECASE 如果为真,则进行忽略大小写的匹配 NF 一条记录的字段的数目 NR 已经读出的记录数,就是行号,从 1 开始 OFMT 数字的输出格式(默认值是%.6g) OFS 输出字段分隔符,默认值与输入字段分隔符一致。ORS 输出记录分隔符(默认值是一个换行符) RLENGTH 由 match 函数所匹配的字符串的长度 RS 记录分隔符(默认是一个换行符) RSTART 由 match 函数所匹配的字符串的第一个位置 SUBSEP 数组下标分隔符(默认值是/034) (1)统计 passwd 文件名,每行的行号,每行的列数 [admin@ datas]$ awk -F ':' '{printf ("文件名:%s,第%d 行有%d 列 \n",FILENAME,NR,NF)}' passwd (2)切割 IP(awk 有去空格) [admin@ datas]$ ifconfig | grep "inet" | head -n 1 | awk '{print $2}' (3)查询一个文件中所有空行所在的行号 数据准备 [admin@ datas]$ vim space.txt [admin@ datas]$ awk '/^$/ {print NR}' space.txt (4)统计单词数 [admin@mc datas]$ vim wordcount hello world hello world hello hadoop spark hive hadoop hive 李四 使用 awk 统计查询 wordcount [admin@ datas]$ awk '{for(i=1;i<=NF;i++){arr[$i]++}} END{for(i in arr){print i,arr[i]}}' wordcount
注意:
我们这里使用了 for 和 if,在命令行执行 awk 命令时,for 和 if 这些的写法基本上就与 java 很相似了。
awk 中不用特意声明变量,第一次使用变量的地方就相当于声明。
我们使用了数组,awk 中的数组除了数字做索引之外,也可以使用字符串做索引,我们这里就是使用的字符串做索引(这样就相当于 java 中的 map 集合了),数组元素如果是 null 也可以直接使用++计算。
4、awk脚本
awk 脚本中是要执行的 awk 逻辑,脚本以后缀为.awk,脚本开头是#!/bin/awk -f
。
awk 脚本执行是 awk -f 脚本 查询的目标文件
使用 awkdemo.awk 脚本查询 words 文件 [admin@ datas]$ awk -f awkdemo.awk words 之前使用 awk 命令行统计查询 wordcount [admin@ datas]$ awk '{for(i=1;i<=NF;i++){arr[$i]++}} END{for(i in arr){print i,arr[i]}}' wordcount 把这个改成 awk 脚本执行 [admin@ datas]$ vim count.awk #!/bin/awk -f #遍历文件前 #BEGIN{ # 如果遍历前有逻辑,就在这里写 #} #遍历文件每一行时的逻辑 { for(i=1;i<=NF;i++){ arr[$i]++ } } #遍历文件后 END{ #如果遍历后有逻辑,在这里写 #我这里是输出汇总结果 for(i in arr){ print i,arr[i] } } 执行这个脚本,统计 wordcount 文件的单词 [admin@ datas]$ awk -f count.awk wordcount
5、awk 内置函数
1、算数函数
atan2( y, x ) 返回 y/x 的反正切。 cos( x ) 返回 x 的余弦;x 是弧度。 exp( x ) 返回 x 幂函数。 log( x ) 返回 x 的自然对数。 sqrt( x ) 返回 x 平方根。 int( x ) 返回 x 的截断至整数的值。 rand( ) 返回任意数字 n,其中 0 <= n < 1。 srand( [Expr] ) 将 rand 函数的种子值设置为 Expr 参数的值,或如果省略 Expr 参数则使用某天的时间。返回先前的种子值。srand()括号内没有表达式的话,它会采用当前时间作为随机计数器的种子,这样以秒为间隔,rand()随机数就能滚动随机生成了
生成 3 次 10 以内的随机数
[admin@ datas]$ vim demo.awk #!/bin/awk -f BEGIN{ srand() for(i=0;i<3;i++){ print int(rand()*10) } }
2、字符串函数
sub( Ere, Repl, [ In ] )
字符串替换,只替换第一次匹配的
参数 1:被替换的字符串(具体的值或正则)
参数 2:替换的新的字符串
参数 3:被替换的字符串所属的变量(不填默认是 awk 的$0)
gsub( Ere, Repl, [ In ] )
替换所有匹配的字符串
参数含义与sub相同
index( String1, String2 )
返回参数 2 作为参数 1 子串出现在参数 1 的哪个索引位置,从 1 开始。如果参数 2 不是参数 1 的子串,则返回 0。
length [(String)]
返回 String 参数指定的字符串的长度(字符形式)。如果未给出 String 参数,则返回整个记录的长度($0 记录变量)。
blength [(String)]
返回 String 参数指定的字符串的长度(以字节为单位)。如果未给出 String 参数,则返回整个记录的长度($0 记录变 量)。
substr( String, M, [N])
字符串截取
参数 1:被截取的字符串
参数 2:截取的子串开始位置索引值,从 1 开始
参数 3:截取的子串的结束位置索引值,不填就是截取到结尾 返回值:截取的字符串,从 M 到 N 位置,不包括 N
match( String, Ere )
在 String 参数指定的字符串(Ere 参数指定的扩展正则表达式 出现在其中)中返回位置(字符形式),从 1 开始编号,如果 Ere 不存在,则返回 0(零)。
split( String, A, [Ere] )
分割为数组
参数 1:被分割的字符串
参数 2:接收分隔的字符串数组
参数 3:分隔的字符(固定值或者正则)
tolower( String )
返回 String 参数指定的字符串,字符串中每个大写字符将更改 为小写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义。
toupper( String )
返回 String 参数指定的字符串,字符串中每个小写字符将更改 为大写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义。
sprintf(Format, Expr, Expr, . . . )
根据 Format 参数指定的 printf 子例程格式字符串来格式化 Expr 参数指定的表达式并返回最后生成的字符串。
(1)substr 字符串截取 [admin@mc datas]$ vim words hello world !! hadoop spark hive 张三 李四 王五 截取输出 words 每一行的第 1 列的前 2 个字符 [admin@ datas]$ awk '{print substr($1,1,2)}' words
6、 awk 做大数据查询的简单应用
[admin@ datas]$ vim grade.txt #学号 科目 成绩 1 语文 90 1 数学 40 1 英语 59 2 语文 95 2 数学 80 2 英语 52 3 语文 89 4 数学 29 (1)使用 awk 命令行,根据学号汇总查询每个学生的总成绩 [admin@ datas]$ cat grade.txt | grep -v "#" | awk '{arr[$1]+=$3} END{for(i in arr){printf("学号为%d 的学生的成绩是:%d\n",i,arr[i])}}' (2)使用 awk 脚本,根据学号查询学生的总成绩,学号参数由外部输入,可以同时查询 多个学号 可以使用 awk -f xxx.awk -v 参数名=参数值 参数名=参数值 查询的目标文件这样的方式 给 awk 脚本传参 [admin@ datas]$ vim grade-no.awk #!/bin/awk -f { arr[$1]+=$3 } END{ #接收参数名为 no 的外部参数,参数 no 的值是学号,可以多个 #多个值时用英文逗号分隔 #使用 split 函数切割 no 的字符串,分隔符是英文逗号, #用变量 nos 接收切割后的数组 split(no,nos,",") for(i in nos){ cno=nos[i] printf("学号为%d 的学生的成绩是:%d\n",cno,arr[cno]) } } 执行 awk 脚本,查询学号为 2 和 4 的学生的总成绩 [admin@ datas]$ awk -f grade-no.awk -v no="2,4" grade.txt