awk、sed

一、sed相关
1、在sed中引入shell变量的方法

eval sed 's/$a/$b/' filename
sed "s/$a/$b/" filename
sed 's/'$a'/'$b'/' filename
sed s/$a/$b/ filename

2、sed中使用正则表达式进行替换的时候,一定要注意有些特殊字符在使用时要转义
需要转义的特殊字符:
用于分组的小括号:( ),在使用时要用\进行转义,但是匹配字符串中真正的小括号时,无需转义
表示前面的表达式出现次数的{},也要转义
表示前面的表达式出现1次或多次的+,也要转义,在使用时,要用\+
表示前面的表达式至多出现1次?,也要转义,在使用时,要用\?
不需要转义的特殊字符:
用于表示字符集的[ ]
表示前面的表达式出现0次或多次的*
有些特殊字符在sed的正则表达式中不能用,比如要表示匹配一个数字不能用\d,而要用[0-9]
例:

sed -i 's#grafana:[0-9]\+.[0-9]\+.[0-9]\+-[0-9]\+$#grafana:latest#g' grafana.yaml
sed -ri 's#grafana:[0-9]+.[0-9]+.[0-9]+-[0-9]+$#grafana:latest#g' grafana.yaml
sed -ri 's#([a-z]+|[a-z]+-[a-z])(.grafana.net/grafana:)[0-9]+.[0-9]+.[0-9]+$#\1\2latest#g' grafana.yaml
sed -i '/image:/s#docker.*#grafana:latest#g' grafana.yaml
# 示例文本
containers:
    - name: grafana
      image: docker.grafana.net/grafana:7.5.2
      image: docker-host.grafana.net/grafana:7.5.2
      imagePullPolicy: IfNotPresent
      ports:
          - containerPort: 3000
            name: http-grafana
            protocol: TCP
命令 含义
1s/old/new/ 替换第1行内容old为new
1,10s/old/new/ 替换1行到10行的内容old为new
1,+5s/old/new/ 替换1行到6行的内容old为new
/pattern1/s/old/new/ 替换匹配pattern1的内容old为new
/pattern1/,/pattern2/s/old/new/ 替换匹配到pattern1的行直到匹配到pattern2的所有行内容old为new
10,/pattern1/s/old/new/ 替换第10行直到匹配到pattern1的所有行内容old为new

二、awk相关
1、awk中引入shell变量方法

var="this a test"
awk 'BEGIN{print "'"$var"'"}'
var1="hello"
var2="world"
awk -v a=$var1 -v b=$var2 'BEGIN{print a,b}'
处理规则顺序:
BEGIN{}//{}END{}
BEGIN{}     : BEGIN是在awk处理文本之前运行
//          : 使用的匹配规则
{}          :循环(每次只处理一行数据)
END{}       :当所有的处理全部执行完毕之后,执行END中的相关操作
示例:
netstat -lntup | awk 'BEGIN{printf "%s\t%s\t%s\n","行号","协议","进程"} NR==15 && /sshd/ {printf "%s\t%s\t%s\n",NR,$1,$NF}END{print "结束"}'

2、awk内置字符串函数
gsub(r,s)                               在整个$0中,用s代替r
gsub(r,s,t)                          在整个t中,用s代替r #替换字符串
index(s,t)                              返回s中字符串t的第一位置 #未用过
length(s)                                 返回s长度 #c语言strlen
match(s,r)                            测试s是否包含匹配r的字符串 #c语言strcmp
split(s,a,fs)                         使用分隔符fs将字符串s划分为指定序列a
sprint(fmt,exp)                      输出经过fmt格式化后的exp
substr(s,0)                            返回字符串s中从第0个字符开始到s字符串的结尾
substr(s,0,n)                       返回字符串s中从第0个字符开始到长度为n的部分

3、printf格式化输出
.prec                                  最大字符串长度,或小数点右面的位数
%c                                     ASCII字符
%d                                     整型
%e                                     科学计数法
%f                                     浮点型 #使用小数点后2位%.2f (用于除法后)
%g                                     awk决定哪种浮点数转换e或者f
%o                                     八进制
%s                                     字符串
%x                                     十六进制

# 表示是从第3列的第6个字符开始,一直到设定的分隔符","结束.
awk -F ',' '{print substr($3,6)}'

# 查看/api_jsonrpc.php被访问的次数
awk -F '[ ]' '/\/api_jsonrpc.php/{ipaddr[$1$7]++}END{for(i in ipaddr){print i,ipaddr[i]}}' access_log

# 状态码为200的html页面($5:$status;$7:$request;$9:$http_referer;substr($9,0,30):表示是从$9列的第0个字符开始,截取30个字符结束)
find /var/log/nginx -type f -name "access_*_*.log" -exec awk -F "[||]" '($5 ~ /\.html\s/ && $7 ~ 200){print $7""$5""substr($9,0,30)}' {} \;

# 查看nginx服务客户端连接   
netstat -ntp | awk -F '[ :]+' '/[0-9]+\/nginx/{if($6 != "127.0.0.1"){print $6}}'

4、awk中使用的shell命令的3种方法:
1)、使用system()
awk程序中我们可以使用system() 函数去调用shell命令

root@ubuntu:~# awk 'BEGIN{system("echo abc")}'
abc
root@ubuntu:~#
root@ubuntu:~# awk 'BEGIN{v1="echo";v2="abc";system(v1" "v2)}'
abc
root@ubuntu:~#
root@ubuntu:~# awk 'BEGIN{v1="echo";v2="abc";system(v1 v2)}'
/bin/sh: echoabc: command not found
root@ubuntu:~#
root@ubuntu:~# awk 'BEGIN{v1=echo;v2=abc;system(v1" "v2)}'
root@ubuntu:~#

从上面的例子,我们简单的分析一下awk是怎样调用system的:
如果system()括号里面的参数没有加上双引号的话,awk认为它是一个变量,它会从awk的变量里面把它们先置换为常量,然后再回传给shell
如果system()括号里面的参数有加上双引号的话,那么awk就直接把引号里面的内容回传给shell,作为shell的“命令行”
2)、使用print cmd | "/bin/bash"

root@ubuntu:~# awk 'BEGIN{print "echo","abc"| "/bin/bash"}'
abc
root@ubuntu:~#
root@ubuntu:~# awk 'BEGIN{print "echo","abc",";","echo","123"| "/bin/bash"}'
abc
123
root@ubuntu:~#

3)、使用cmd | getline var

root@ubuntu:~# awk 'BEGIN{"echo hello"" ""world" | getline var;print var}'
hello world
root@ubuntu:~# awk 'BEGIN{ "seq 1 5" | getline var;close("seq 1 5");print var}'  # 只打印第一行
1
root@ubuntu:~# awk 'BEGIN{ while(("seq 1 5" | getline var)){print var};close("seq 1 5")}'  # 打印所有结果
1
2
3
4
5
root@ubuntu:~#

说明:
getline为awk所提供的输入指令。getline一次读取一行数据,若读取成功则return 1,若读取失败则return -1,若遇到文件结束(EOF),则return 0;
getline的用法和例子:
当getline左右没有重定向符|或<时,getline读去当前文件的第一行并将数据保存到变量中,若是没有变量,则数据保存到$0中;因为awk在处理getline以前已经读入了一行,因此getline获得的返回结果是隔行的。
当getline左右有重定向符|或<时,getline做用于定向输入文件,因为该文件是刚打开,awk并无读入一行数据,而getline读入了一行数据,那么getline返回的是该文件的第一行,而不是隔行。
4)、总结
无论使用system()还是print cmd | "/bin/bash",awk都是新开一个shell,在相应的cmdline参数送回给shell,所以要注意当前shell变量与新开shell变量问题

root@ubuntu:~# abc=12345567890
root@ubuntu:~# awk 'BEGIN{system("echo $abc")}'
root@ubuntu:~#
root@ubuntu:~# export abc=12345567890
root@ubuntu:~# awk 'BEGIN{system("echo $abc")}'
12345567890
root@ubuntu:~#
root@ubuntu:~# abc=1234567890
root@ubuntu:~# awk 'BEGIN{print "echo","$abc"| "/bin/bash"}'
root@ubuntu:~#
root@ubuntu:~# export abc=1234567890
root@ubuntu:~# awk 'BEGIN{print "echo","$abc"| "/bin/bash"}'
1234567890
root@ubuntu:~#

说明:
以上例子,没有export的话,那些变量都是只存在于当前shell变量中,所以都是echo不出来的 ,
而使用了 export的都是环境变量,所以awk调用新的shell时候,可以echo出来。

4、awk获取shell命令返回值

root@ubuntu:~# awk '{cmd="ls " $1 ">/dev/null 2>&1";rc=system(cmd);print "RC:", rc}' test.txt
RC: 0
RC: 2

命令说明:
1) 首先我们新建一个cmd变量,把需要执行的shell命令拼接起来赋给cmd
2) 通过system来执行shell命令,把返回值付给rc变量
3) 把rc变量打印到屏幕输出
如果想把出错信息也显示到屏幕的话,把2>&1去掉即可。
通过输出的信息我们看到,第一个ls命令的返回值是0,第二个ls命令的返回值是2

参考链接:
       https://www.cnblogs.com/DengGao/p/5935719.html
       https://www.cnblogs.com/emanlee/p/3327576.html
       http://www.zsythink.net/archives/tag/awk/
       https://www.cnblogs.com/liwei0526vip/p/5644163.html     # sed
       https://www.gnu.org/software/sed/manual/sed.html     # sed官方文档
       https://www.gnu.org/software/gawk/manual/gawk.html     # awk官方文档

posted @ 2019-02-15 14:39  風£飛  阅读(176)  评论(0编辑  收藏  举报