shell-awk

文章目录

一、com

https://coolshell.cn/articles/9070.html
https://taoshu.in/awk-in-20-minutes.html

1.作用

awk是个文本处理的程序,是一种独立的编程语言,有自己独特的语法,它将一个文件分为行和列,以类似二维表的方式进行处理。
它不需要声明文件流的打开和关闭,不像java和python,它自动按行遍历,去操作每一行和列。也就是说,省去了文件流和循环的代码,直接写循环体。
它提供了内置变量和函数,并且可以通过正则筛选行

2.工作模式:按行进行匹配+处理

它的工作模式就是简单的匹配、处理。awk从标准输入逐行读取内容,然后使用不同的模式(Pattern)进行匹配,如果匹配成功,则会执行对应大括号内的动作(Action)。这就是awk的执行模型。读取、匹配、执行,就这么简单,所用复杂的功能都是在这个模型之上衍生出来的。
awk逐行扫描文件,寻找匹配特定模式的行,并在这些行上进行指定的操作,然后格式化输出。
它包含2部分:
模式匹配(用于找到要处理的行) pattern
处理 {action}

awk有两个特殊的模式:BEGIN和END,在没有读取任何数据之前以及在所有数据读取完成以后执行。一般用来声明变量和设置输出格式

工作模式总结如下:

第一步执行BEGIN 语句
第二步从文件或标准输入读取一行,然后再执行pattern+action,然后下一行
第三步执行END语句
在这里插入图片描述

3.awk同其他文本处理程序的对比

都是文本处理程序,并列关系,可以相互替换,awk功能最多。
awk同sed相比,awk偏重于格式化展示(其实就是把源文件给变个样子然后展示出来),所以awk要一行一行的把整个文件都给读了,有报表展示的影子。所以awk处理完,如果想把变化同步到源文件,需要重新写回。
sed偏重于文本的匹配 + 处理,即:找到想要的行,进行想要的处理,比如替换一个单词、下面添加一行。虽然这些awk也能,但sed方便一点,不需要循环读,也修改也不需要回写,因为sed的使命就是修改文件

4.awk的正则

awk的正则跟grep一样,很迷,很玄。而且不同版本之间也不一样
使用awk -V可以查看版本
在git bash中,版本为GNU Awk 5.0.0, API: 2.0 (GNU MPFR 4.1.0, GNU MP 6.2.1)
在mobaxterm自带的环境中,版本为BusyBox v1.22.1 (2015-11-10 11:07:12 ) multi-call binary.

前者,行范围表达指定行后面所有时,用/xxx/,/*/ 后者得用/xxx/,/\*/要多加个转义。。。迷!!!

二、syntax

1.basic format

awk options program file
awk [可选的命令行选项] 'BEGIN{命令 } pattern{ 命令 }pattern{ 命令 }…… END{ 命令 }' 文件名

2.options:

-F -fs 指定行中划分数据字段的字段分隔符。awk中默认的字段分隔符是任意的空白字符(例如n个空格或n个制表符tab),不是只有空格和制表。

由于/etc/passwd文件是使用冒号来分离字段的,因此要在awk选项中将冒号指定为字段分隔符。

$ awk -F: '{print $1}' /etc/passwd
nobody
root
daemon
_uucp
_taskgated
_networkd
_installassistant
_lp
...
行首有空白,会自动忽略

在这里插入图片描述
打印出第一列不受影响

FIELDWIDTHS,根据宽度分割,空格算长度

$ cat file 
1234567890
$ awk -vFIELDWIDTHS="1 2 3 4 5" -vOFS="|" 'NF=NF' file
1|23|456|7890|

-f programFile 从指定的文件中读取代码数据

-v var=value 定义awk程序中的一个变量及其默认值,设置OFS的时候,必须用-v

一个var对应一个-v

awk -v reportdate=$reportdate -v pt=$pt '{print $1"\t"$2"\t"$3"\t"$4"\t"reportdate"\t"pt}' ./转办.txt

设置OFS时,可以放在option中,也可以放在BEGIN中

awk -F: 'BEGIN{OFS="-"};/tyson/{ print ("username:"$1,"protocol:"$4)}' /etc/passwd
username:tyson-protocol:1001
username:tyson1-protocol:1003

ORS是输出的行之间的分隔符,默认是一个回车\n

awk -F: 'BEGIN{ORS="\n\n"};/tyson/{ print ("username:"$1,"protocol:"$4)}' /etc/passwd    
username:tyson protocol:1001
 
username:tyson1 protocol:1003


3.program=模式+action

‘模式 {命令}模式 {命令}模式 {命令}…’

整体用单引号包裹
可以连续多个,多个program全都包裹在’'中,相互独立,每个program都是针对所有行

命令用{}包裹
模式中可以用正则,也可以是if这种判断,当然if也可以放在命令中
命令中可以定义变量,也有循环等语法,别忘了,awk是种编程语言

模式是用来匹配行的,如果想匹配列,那要在{}中用列变量或者循环

awk的程序脚本是单个文本字符串,因为中间有空格,所以要用单引号包裹。下面的例子在命令行上指定了一个简单的Hello World例子:
$ awk ‘{print “Hello World!”}’
没指定动作,默认为打印到控制台

{1}模式

模式是为了筛选数据,自然而然,可以:
按行:行号、行正则匹配
按列:列号、列正则匹配
混合使用:逻辑运算符

正则模式、布尔模式、特殊模式、行模式、列模式。

[1]正则模式,要用//包裹
/admin/ { ... }     # 匹配包含 admin 的内容
/^admin/ { ... }    # 匹配 admin 开头的内容
/admin$/ { ... }    # 匹配 admin 结尾的内容
/^[0-9.]+ / { ... } # 匹配数字开头的内容
/(POST|PUT|DELETE)/ # 匹配包含部分 http 请求的内容

在这里插入图片描述

[2]行模式,这里的正则很迷,需要死记硬背。
syntax

A条件,B条件
注意A和B条件不一定是正则
在这里插入图片描述
上图中第一种语法是正则模式的语法,表示被正则表达式匹配到的行,将会执行对应的动作。

上图中第二种语法是行范围模式的语法,它表示,从被正则1匹配到的行开始,到被正则2匹配到的行结束,之间的所有行都会执行对应的动作,所以,这种模式被称为行范围模式,因为它对应的是一个范围以内的所有行,但是需要注意的是,在行范围模式中,不管是正则1,还是正则2,都以第一次匹配到的行为准

在这里插入图片描述

就像上述示例中,即使Lee在第2行与第3行中都出现了,但是由于正则1先匹配到第2行中的lee,所以,最终打印出的内容从第2行开始,即使Kevin在第5行与第7行中都出现了,但是由于Kevin第一次出现在第5行,所以最终打印出的内容到第5行结束,也就是说,最终打印出了第2行到第5行以内的所有行。

但是,你可能会有这样的需求,你不想依靠正则表达式去匹配行的特征,你只是想单纯的打印出从X行到Y行之间的所有行。

比如,我们有一个文本文件,这个文件中一共有7行文本,你想要打印出从第3行到第6行之间的所有行,该怎么做呢?

其实,使用之前学习到的”关系运算符模式”,即可满足我们的需求,示例如下。
在这里插入图片描述

行模式但不包含首尾行,见下面case
行模式中取筛选后数据的行号
  • 使用行模式筛选数据后,输出NR,输出的还是未筛选前的NR
    在这里插入图片描述
  • 如果想将筛选后的数据从1开始编号,可以使用i++
    在这里插入图片描述
    在这里插入图片描述
  • 筛掉前args行
    在这里插入图片描述
  • 筛掉后args行
行模式,指定某行上面或下面所有行
指定某行下面所有行,2种方法

####### 下面所有用/*/表示,为啥不是.*?,不明白,可能这里不是完全正则,是通配符

$ awk '/^CR/,/*/' hive_create_table1.sh
CREATE
        CREATE
 CREATE
CREATE TABLE
`col1` string,
`col2` string,
`col3` string
COMMENT 'comment text'
ROW
        ROW
COMMENT

####### 使用action内的变量

  • 筛选某行后的所有行
    在这里插入图片描述
指定某行上面所有行,跟上面不一样,这里用*就成所有的了,奇怪。可以用NR==1。这里的第二个条件指的是第一个匹配的
$ awk 'NR==1,/CREATE/' hive_create_table1.sh
SLF4j:123
SLF4j:456
22/06/17
22/06/17
22/06/17
CREATE
[3]布尔模式|列模式
$1 >= 500 { ... } # 匹配 5xx 请求
/admin/ || $1 >= 500 # 匹配 admin 接口的 5xx 错误

布尔模式自然要用到关系运算符,因为它的结果是布尔值
在这里插入图片描述

notice
  • 选择语句时,如果写在模式中,在{}前面。如果是用if,那if必须写在{}中
  • 要用==而不是=
[4]特殊模式:在数据处理前后使用BEGIN和END模式

有的时候可能需要在处理数据前运行脚本,如为报告创建标题;或是在处理完数据后运行脚本,在awk中分别使用BEGIN和END关键字实现。
这里print命令的结果会直接显示出来,因为BEGIN标示的脚本区域会在读取数据前执行。如果想使用正常的程序脚本处理数据,必须使用另一个脚本区域来定义程序(如果脚本区域过长,可以分成多行输入):

#在结束的时候输出含有admin日志的行数
awk 'BEGIN{c=0}/admin/{c+=1}END{print c}'
[5]空模式:默认所有行,所有列

{2}action行为,也翻译成命令,放在{}中

[1]syntax
  • {}也可以嵌套。判断语句可以写在{},也可以写在{}前面作为模式。一个模式对对应一个{}程序脚本,没有模式的{}针对所有行。
  • {}内有多条语句时,用;分割
awk '!/^$/{if(NR%3!=0){printf$0"|"}else{print$0}}' test.txt

{3}notice

[1]一个模式只能跟一个行为组合

比如

top | awk 'NR==8{printf $2"$2"}{print "test"}'

NR==8只会对{print $2"$2"}生效,{print “test”}对所有行生效,所以最终结果会有很多行,而不仅仅只有NR==8的行,除非写在一个大括号{}中。

4.变量

awk支持两种不同类型的变量:内建变量和自定义变量。

属性说明
$0当前记录(作为单个变量)
$1~$n 当前记录的第n个字段,字段间由FS分隔
FS输入字段分隔符 默认是空格
NF当前记录中的字段个数,就是有多少列,如果是0,则是空行
NR已经读出的记录数,就是行号,从1开始,如果有多个文件话,这个值也是不断累加中。
RS输入的记录他隔符默 认为换行符
OFS输出字段分隔符 默认也是空格
ORS输出的记录分隔符,默认为换行符
ARGC命令行参数个数
ARGV命令行参数数组
FILENAME当前输入文件的名字。注意:必须要大写
IGNORECASE如果为真,则进行忽略大小写的匹配
ARGIND当前被处理文件的ARGV标志符
CONVFMT数字转换格式 %.6g
ENVIRONUNIX环境变量
ERRNOUNIX系统错误消息
FIELDWIDTHS输入字段宽度的空白分隔字符串
FNR当前记录数
OFMT数字的输出格式 %.6g
RSTART被匹配函数匹配的字符串首
RLENGTH被匹配函数匹配的字符串长度
SUBSEP\034

{1}变量赋值

{ a=$1; b=$0 } # 变量赋值
{ c[$1] = $2 } # 字典赋值

{2}指定行

NR就是行号,从1开始
对 test-awk 文件内容按行做计算sum

name 1th 2th 3th 4th
zhang 123 12 13 14
wang 12 23 34 45
li 11 12 14 17
zhao 12 89 2 3
cat test-awk | awk 'NR==1{printf "%10s %10s %10s %10s %10s %10s\n",$1,$2,$3,$4,$5,"total"}
NR>=2{total = $2+$3+$4+$5;printf "%10s %10s %10s %10s %10s %10s\n",$1,$2,$3,$4,$5,total}'

在这里插入图片描述

{3}指定列

awk中默认的字段分隔符是任意的空白字符(例如n个空格或n个制表符tab),不是只有空格和制表。
awk会自动给每一行中的每个元素分配一个变量,在默认情况下,会按照下面的方式分配:

$0代表整行
$1代表文本行中的第一个数据字段,$2代表第二个字段,以此类推
NF列数

5.控制语句

{1}选择

{ if ($3 >= 500) { ACTION }
  else if ($3 >= 400) { ACTION }
  else { ACTION }
}
{ for (i=1; i<=NF; i++) { ACTION } }
{ for (item in c) { ACTION } }
{ exit; }      # 结束程序,很少使用
{ next; }      # 跳过当前行

{2}循环

统计 user 接口单ip的请求数量超过10次的ip

/user/ { ip=$1; ip_count[ip]++ }
END { for (ip in ip_count) {
  if (ip_count[ip] > 10) { print ip, ip_count[ip] }
} }

6.在命令行跨行写awk命令

有3种方法:可以在命令之间加个分号,也可以连续设置多个{},也可以分成多行

$ echo "My name is oldmanw" | awk '{$4="handsomeBoy"; print $0}'
My name is handsomeBoy
$ echo "My name is oldmanw" | awk '{$4="handsomeBoy"}{print $0}'
My name is handsomeBoy
$ echo "My name is oldmanw" | awk '{$4="handsomeBoy"
> print $0}'
My name is handsomeBoy

在使用了表示起始的单引号之后,bash shell会使用次提示符(>)提示你输入更多的数据。你可以在每一行输入一条命令,直到输入了结尾的单引号。注意这种方法只有在控制台输入的时候才有效,> 是自动出现的,如果写在脚本,直接换行即可,不用手动输入>

7.使用脚本文件

$ cat awkScript
{print "The meaning of user " $1 " is " $5}
$ awk -F: -f awkScript /etc/passwd
The meaning of user nobody is Unprivileged User
The meaning of user root is System Administrator
The meaning of user daemon is System Services
The meaning of user _uucp is Unix to Unix Copy Protocol
The meaning of user _taskgated is Task Gate Daemon
The meaning of user _networkd is Network Services
The meaning of user _installassistant is Install Assistant
The meaning of user _lp is Printing Services
The meaning of user _postfix is Postfix Mail Server
以下省略...

9.转义特殊字符

无论在模式中还是在命令中,都可以用八进制的编码来替代单引号和双引号。因为awk的程序用单引号包裹,直接打会误认为是program。

使用引号拼接

使用八进制编码

八进制ASCII码特殊字符列表:

041 !   042 "   043 #   044 $   045 %   046 &   047 '
050 (   051 )   052 *   053 +   054 ,   055 -   056 .   057 /
072 :   073 ;   074 <   075 =   076 >   077 ?
case
打印输出一个单引号
echo -e '\047'

当需要同时打印单引号、双引号和$符等等的时候,该方法比较方便

存在问题,如果特殊字符后面跟数字,则会造成歧义:
#想要输出:单引号 空格 单引号后面跟个1
Administrator@WIN-DFIIOPTMTL8 MINGW64 /e/develop/shell_test
$ echo -e '\047 \0471'
' 9
#第一个正常打印单引号,第二个打印9
#先使用空格分离,然后再使用\b(退格)表示取消前面的空格
Administrator@WIN-DFIIOPTMTL8 MINGW64 /e/develop/shell_test
$ echo -e '\047 \047 \b1'
' '1

简单应用举例:

echo -en 'mysq.host: \047 ';echo -e '192.168.77.10\047'|cat -A
使用\047打印一个单引号,cat -A 最终拼出来一个$符号
echo -en 'mysq.host: \047 \b';echo -e '192.168.77.10\047'|cat -A
单引号后面有一个空格,可以使用\b退格消除掉
su - oracle -c "echo -ne '\047';echo -e '192.168.1.1\047'"
su命令使用双引号引起来,echo 使用单引号引起来,又要打印单引号,则使用\047拼出
su - oracle -c "echo -ne '\042';echo -e '192.168.1.1\042'"
同样道理,使用\042拼双引号

10.函数

https://www.runoob.com/w3cnote/awk-built-in-functions.html#b2
split,gsub,substr

{1}字符串

[1]substr(str, start, l)

start从1开始,l计数时包含start
从第 start 个字符开始长度为 l(包含start) 的子串。如果没有指定 l 的值,返回 str 从第 start 个字符开始的后缀子串。

$ awk 'BEGIN {
    str = "Hello, World !!!"
    subs = substr(str, 1, 5)
    print "Substring = " subs
}'

输出结果为:
Substring = Hello

printf、print print后面默认带换行符,printf没有

格式化字符描述
s字符串
c单个字符
d数值
e指数
f浮点数
g根据值决定使用 e 或 f 中较短的输出
o八进制
x十六进制
%百分号
$ cat printf-format.awk
BEGIN {
 printf "s--> %s\n", "String"
 printf "c--> %c\n", "String"
 printf "s--> %s\n", 101.23
 printf "d--> %d\n", 101,23
 printf "e--> %e\n", 101,23
 printf "f--> %f\n", 101,23
 printf "g--> %g\n", 101,23
 printf "o--> %o\n", 0x8
 printf "x--> %x\n", 16
 printf "percentage--> %%\n", 17
}
$ awk -f printf-format.awk 
s--> String
c--> S
s--> 101.23
d--> 101
e--> 1.010000e+02
f--> 101.000000
g--> 101
o--> 10
x--> 10
percentage--> %
print自动换行,printf不换行
printf不受 OFS,ORS 影响

printf 不会使用 OFS 和 ORS,它只根据”format”里面的格式打印数据

字符串长度不足时补 0

默认情况下,右对齐时左边会补空格
$ awk ‘BEGIN { printf “|%5s|\n”, “100” }’
| 100|
为了在右对齐是,左边补 0 (而不是空格),在指定宽度的数字前面加一个 0,即使用”%05s”代 替”%5s”

系统函数

时间函数

11.数组

awk的数组是关联数组,索引不一定是数字。
awk没有数据类型,数字的字符串会根据上下文自动判断为数字还是字符串。数组的索引同样适用,意味着,数字索引不用加引号
awk的数组不需要定义,直接用。名称命名规范和变量相同。

判断数组是否存在某索引

if ( index in array-name )

$ cat array-refer.awk
BEGIN {
#引用了item[55]但没有赋值,缺省为null
 x = item[55];
 if ( 55 in item )
 print "Array index 55 contains",item[55];
 item[101]="HD Camcorder";
 if ( 101 in item )
 print "Array index 101 contains",item[101];
 if ( 1010 in item )
 print "Array index 1010 contains",item[1010];
}
$ awk -f array-refer.awk
Array index 55 contains
Array index 101 contains HD Camcorder

该例中:

  • Item[55] 在引用前没有赋任何值,所以在引用是 awk 自动创建该元素并赋 null 值
  • Item[101]是一个已赋值的缘分,所以在检查索引值时返回 true,打印该元素
  • Item[1010]不存在,因此检查索引值时,返回 false,不会被打印

循环

for ( var in arrayname )
actions

删除数组元素

如果要删除特定的数组元素,使用 delete 语句。一旦删除了某个元素,就再也获取不到它
的值了。
语法:

delete arrayname[index];

删除数组内所有元素

for (var in array)
delete array[var]

在 GAWK 中,可以使用单个 delete 命令来删除数组的所有元素: Delete array
此外,下面例子中,item[103]=””并没有删除整个元素,仅仅是给它赋了 null 值。

$ cat array-delete.awk
BEGIN {
 item[101]="HD Camcorder";
 item[102]="Refrigerator";
 item[103]="MP3 Player";
 item[104]="Tennis Racket";
 item[105]="Laser Printer";
 item[1001]="Tennis Ball";
 item[55]="Laptop";
 item["no"]="Not Available";
 delete item[102]
 item[103]=""
 delete item[104]
 delete item[1001]
 delete item["na"]
 for(x in item) 
 print "Index",x,"contains",item[x]
}
$ awk -f array-delete.awk 
Index no contains Not Available
Index 55 contains Laptop
Index 101 contains HD Camcorder
Index 103 contains 
Index 105 contains Laser Printer

为数组排序

asort 函数重新为元素值排序,并且把索引重置为从 1 到 n 的值,此处 n 代表数组元素个数。
total = asort(item,itemnew);

用 asorti 为索引排序

12.内置指令

{1}getline

awk是一行一行循环读取的,每读取一行,就会进行一些操作,比如更新NF,NR,FNR和$0等这些内部变量。正常情况下这个过程无法介入。
而getline可以将这些操作给替换成自己想要的操作。

getline顾名思义,获取一行数据,这里的一行指的是当前行的下一行,所以就是获取下一行,然后交给后续处理。这里的下一行,不一定从参数文件中获取,也可以从指定的其他文件、标准输入、shell命令。
==》

[1]作用:可以实现更加灵活的操作

getline可以自由的组合要处理的数据源,让数据源更灵活,比如读一行文件1,再读一行文件2。
getline可以在循环中添加、替换其中的某一次循环、删除某次循环

[2]usage
for(i=1;i++;i<=总行数){$0、NR、NF、FNR等变量赋值;
	getline
}

getline可以带一个参数,根据有无参数,操作会不同
在这里插入图片描述

getline var一个参数时,在读取记录时,会将下一条记录放在参数变量var中,不划分字段不设置变量
  • getline var用来处理参数文件,参与循环

在这里插入图片描述

  • getline var < filename 从其他文件读取数据保存至变量var中,不划分字段不更新内置变量
  • commend | getline var #一个参数的getline,将读取的数据存放在变量var1中,不更新变量
#下面例子通过getline得到系统的当前时间。
~]$ awk '{if(("date +\"%F %T\"" | getline var) >0 );print var;close(get_date)}' a.txt  ##输出时间
getline无参数时,表示读取下一条记录并保存到$0中,并且会继续执行后续的代码逻辑,并且进行字段分割,然后继续执行后续的代码逻辑 此时的getline会更新NR,FNR,$0和$N等内置变量
  • getline无参处理参数文件
    在这里插入图片描述

  • getline < filename 从其他文件读取数据,并且读取的数据会保存至$0中,会更新$0,NF等内置变量

root@ubuntu:~/bash/awk$ awk '{printf "%s ", $0; getline < "b.txt"; print $0}' a.txt 
1 6
2 7
3 8
4 9
5 20
#a.txt文件的内容为上面打印出来的第一列,b.txt文件的内容为上面打印出来的第二列。
  • 无参数读取shell命令结果,commend | getline,读取的数据将会被放入$0,会进行变量划分$0,NF等变量
[3]notice
getline是处于awk的main代码块中,如果放在BEGIN或END代码块中则无效

三、notice

1.设置OFS时,要保证有列的操作,比如NF=NF

2.awk中的if判断,用==,shell中的if,可以只用=

3.判断变量同字符串是否相等

经测试,==无效,要用

 '{if($1~"文本")}' 
 awk -F,  '{if($NF~"2024-06-11"){print $0}}' 增量客户问题率2024-06-11.csv > zengliangmoban.txt

4.awk的代码用’‘包裹,可以用多个’‘拼接,’'之间和’之间的内容最后会汇总成完整的代码。

5.拼接变量时,跟代码中一样,变量需要""包裹的就加,比如函数参数,但//里的不需要。

6.awk的正则包裹在//中,不支持i和g

7.传入外部变量

一个v一对变量

awk -F, -v get_val=$get_val -v get_point=$get_point -v reportdate=$reportdate -v pt=$pt '{print $1"\t"$2"\t"get_val"\t"get_point"\t"$5"\t"$6"\t"reportdate"\t"pt}' zengliangmoban.txt  >> zengliang.txt

8.输出\t

9.回写到文件 > >>

10.awk结果赋给变量 $() ``不行

get_val=$(awk -v min=0.0015 -v max=0.0096 'BEGIN {srand(); print min + rand() * (max - min)}')

四、实例

1.过滤,类似于grep

df -h | grep sd
df -h | awk ‘/sd/’
在这里插入图片描述
在这里插入图片描述

2.去除重复行列,类似于uniq

uniq -u
awk ‘!a[$0]++’
在这里插入图片描述

行count,去除重复行并统计每组数量

$ find -name "*.sh" | xargs awk '/T/{print FILENAME}'|uniq -c|sort -r
      3 ./sort_uniq_test.sh
      2 ./awk_test.sh
      1 ./awk_sub_test.sh

3.计数统计,类似于wc

在这里插入图片描述

awk xxx | wc -l

NF

统计某单词出现次数

# 搜索统计单词“law”的个数
$ awk -F : '/law/{count++} END{print "the count is ",count}' /etc/legal
the count is 1
# 统计单词“the”的个数
$ awk -F : '/the/{count++} END{print "the count is ",count}' /etc/legal 
the count is 3

4.取前几行,≈head

在这里插入图片描述

5.切分列,≈split

在这里插入图片描述

设置输出的分隔符,并且统计分隔符的数量

OFS

Administrator@WIN-DFIIOPTMTL8 MINGW64 /e/develop/shell_test
$ awk -F \t -v OFS='|' '{NF=NF;print $0}' split_test.txt
92000004545_1   1233455444_1    1231-NF-|y      2020-04-20 00:00:00     D       U       Z       2021-04-20 23:59:59     ANT

#输出的分隔符,可以方便的查看字段的分割情况
Administrator@WIN-DFIIOPTMTL8 MINGW64 /e/develop/shell_test
$ awk -F '\t' -v OFS='|' '{NF=NF;print $0}' split_test.txt
92000004545_1|1233455444_1|1231-NF-ty|2020-04-20 00:00:00|D|U|Z|2021-04-20 23:59:59|A|N|T
#统计下上面分隔符的数量,是一种查看列数的方式,比较直观,当然没有直接输出NF方便
Administrator@WIN-DFIIOPTMTL8 MINGW64 /e/develop/shell_test
$ awk -F '\t' -v OFS='|' '{NF=NF;print $0}' split_test.txt | awk '{s+=gsub(/\|/,"&")}END{print s}'
10

Administrator@WIN-DFIIOPTMTL8 MINGW64 /e/develop/shell_test
$ awk -F '\t' -v OFS='|' '{NF=NF;print $0}' split_test.txt | grep -o '|' | wc -l
10

循环所有列,并且输出单引号'和双引号"的文本

#单引号:用双引号框住,在使用2个单引号框住需要输出的单引号,在使用反斜线对单引号转义。前面2个单引号和后面2个单引号是并列、前后串行的。
ls | awk '{print  " ' \' ' "}'
#双引号:使用双引号框住,在使用反斜线对双引号转义
ls | awk '{print  " \" "}'  
#单引号"\047"  使用单引号的八进制编码,需要用""包裹
awk '{for(i=1,i<=NF;i++){printf "\047"$i"\047";}}' 5.txt
#双引号"\042"  使用双引号的八进制编码,需要用""包裹
awk '{for(i=1,i<=NF;i++){printf "\047"$i"\042";}}' 5.txt

case

将hive命令行中查出的一行字段值转为select列表,如果使用默认分隔符(n个空白),有些时间字段值形如 2020-04-20 00:00:00,因为中间有个空格,会把这个值分为2个,但实际上列之间的分隔符是\t,字段里面的分隔符是空白。所以此时不需要用默认分隔符,要指明-F ‘\t’。

$	awk	'{print NF}'	5.txt
326
$	awk	-F	'\t'	'{print NF}'	5.txt
321
  • 也可以用循环
    在for循环体中进行判断,判断一个字符串中是否包含子串可以用index函数。
    写复杂awk时,可以先把架子搭好,然后从内往外分别写。
awk '{for(i = 1;i <= NF;i++){if(index($i,"2020-04-20")==0){print "\047"$i}else if(index($i,"00:00:00")==0){print " "$i"\047,"}else{print "\047"$i"\047,"}}}'
或者
awk '{for(i=1;i <= NF;i++){printf "\047$i\047"}}' | awk '{gsub(/0\047,\0420/,"0 0");printf $0}'
或者
awk '{for(i=1;i<=NF;i++){if($(i+1)~/[0-9][0-9]:[0-9][0-9]:[0-9][0-9]/){print $i" "$(i+1);i+=1}else {print $i}}}'
#测试文本
echo "92000004545_1 1233455444_1 1231-NF-ty 2020-04-20 00:00:00 D U Z 2021-04-20 23:59:59 A N K"|

用管道的话,awk接收的要是一行行的数据。如果是文件名,要用xargs
在这里插入图片描述

将2个指定关键字的行之间的内容输出,且不包括关键字行

http://blog.chinaunix.net/uid-29369535-id-5767318.html

将hive show create 的输出中过滤出字段列表

SLF4j:123
SLF4j:456
22/06/17
22/06/17
22/06/17
CREATE
	CREATE
 CREATE
CREATE TABLE
`col1` string,
`col2` string,
`col3` string
COMMENT 'comment text'
PARTITIONED BY (
 `col_par`)
ROW
	ROW
COMMENT 

因为分区那一行也有COMMENT,所以要用^COMMENT

cat hive_create_table.sh |xargs \
awk '/^CREATE TABLE/,/^ROW/{if(!sub(/CREATE|^COMMENT|PARTITIONED|ROW/,"")){gsub(/`|\)/,"",$1);print $1}}'

指定输出的列

不想要哪列,直接将那列设为"",然后输出$0即可

  • 输出$1之后的列
awk '{ $1=""; print $0 }' file_name

6.将ldap的add文件修改为modify文件

从ldapsearch中把dn那行过滤出来,然后在每行dn下插入修改的2个属性。

ldapsearch -x -D cn=admin,dc=xyyh,dc=com -w bigdata2019 -b ou=People,dc=xyyh,dc=com | grep "dn: uid=" | \
awk '{print $0 "\nchangetype: modify\nreplace: userPassword\nuserPassword: Q1w2e3r4\n"}' \
>/opt/ldap/user_modify_changeDefaultPassword.ldif

7.将文件中的空白行提取出,一个空白行打印一句话

/^$/表示空行

awk '/^$/ {print "Blank line}' test.txt

8.将文件中空行过滤,然后每3行合并为一行

空行:!/^$/ 或者NF,NF表示当前行字段数,为0表示空行,NF==0
awk NF file.txt > new.txt
打印换行:printf表示不换行,print会自动换行

awk '!/^$/{if(NR%3!=0){printf$0"|"}else{print$0}}' test.txt

9. 查找

打印包含主机名的行,因为没有指定动作指令,默认动作为打印。

awk '/nologin/' /etc/passwd
或者
#注意:必须要用xargs,不然就是在文件路径名里找
find -name "*.sh" |xargs awk '/TTT/ {print $0}'

10. 替换

{1}sub/gsub/gensub

awk的sub/gsub函数用来替换字符串
sub匹配第一次出现的符合模式的字符串,相当于 sed ‘s//’
gsub匹配所有的符合模式的字符串,相当于 sed ‘s//g’

sub(/regexp/, replacement, target);简称 gsub(r,s,t)
注意第三个参数target,如果忽略则使用$0作为参数,即整行文本。

[1]case
(1)base
awk '{ sub(/AAAA/,"BBBB"); print $0 }' t.txt
awk '{ gsub(/AAAA/,"BBBB"); print $0 }' t.txt
(2)将目录下所有文件都替换且覆盖原文件(用FILENAME变量)
find . -name "*.sh" |xargs awk '{gsub(/AA/,"TT");print $0 > FILENAME}'

{2}if

awk -F. -v OFS='.' '{if($2==2020)$2=2021}{print$0}'

11. 某一列进行条件判断

cat /etc/hosts | awk ‘$3~/(z|d|m)[0-9]/ {print $1}’ | grep -v ‘161|159|163’
将hosts列表中的第三列是’z3’这种格式并且把161、159、163这3个排除。

注意:在模式部分中,如果要单独对某一列进行判断,用$num~,然后后面跟正则,正则一定要用/ xxx /包裹

12. 从某目录下过滤出某些文件,然后将这些文件复制后改名

#!/bin/bash
arr=`ll -lrt /sftp/data | grep 20201207 | awk '{print $9}'`
for i in $arr
do
	cp -f $i `echo $i | awk -F. -v OFS='.' '{if($2==20201207)$2=20201208}{print $0}'`
done

13.逆转字符串

用for循环
在这里插入图片描述

echo abc12345678 | awk '
{for(i=1;i<=length;i++){line=substr($0,i,1) line}} END {print line}
'
  • length即当前行长度
  • substr($0,i,1),表示取当前字符从索引i开始,取当前位
  • line=substr($0,i,1) line;后面的line表示将值分别保存在内存栈中,循环完毕后打印的就是所有行
    substr($3,6,2)—>表示从第三个字段里的第六个字符开始,截取2个字符结束
    substr($3,6)—>表示是从第3个字段里的第6个字符开始,一直到结尾

14.将文本以行为单位逆序输出

awk '{line[NR]=$0}END{i=NR;while(i>0){print line[i];i--}}'

15.打印指定行的前几行&按行逆序

case:打印匹配行的前2行

awk 'NR==1,/d/{print $0}' shell-awk-test.txt | awk '{line[NR]=$0}END{i=NR;while(i>0){print line[i];i--}}'|sed -n  '1,+2p' |awk '{line[NR]=$0}END{i=NR;while(i>0){print line[i];i--}}'

在这里插入图片描述

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Linux Shell中的awk -F命令用于指定字段的分隔符。在awk命令中,借用shell中类似于位置变量的方法,使用$1、$2、$3等顺序表示行(记录)中的不同字段。而awk命令默认的分隔符是空格。但是通过使用awk -F命令,我们可以指定其他字符作为分隔符来处理文本和数据。例如,如果我们想要以逗号作为分隔符,我们可以使用awk -F","来指定逗号为分隔符。这样,在awk命令中,我们就可以使用$1、$2、$3等来表示逗号分隔的不同字段了。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [linux awk命令详解](https://blog.csdn.net/qq_15245487/article/details/100144279)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [【linux】Shell 编程 awk 命令详解](https://blog.csdn.net/sirria1/article/details/126768903)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Shell脚本之awk篇](https://download.csdn.net/download/rio520/10572387)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值