vieyahn2017 / shellv

shell command test and study
4 stars 1 forks source link

6.25 awk结合正则,for,if 过滤查询 #48

Open vieyahn2017 opened 5 years ago

vieyahn2017 commented 5 years ago

awk

vieyahn2017 commented 5 years ago

匹配操作符(~) 用于对记录或字段的表达式进行匹配。 例如,找出当前目录下文件所有者为root的文件:

ls -l | awk '$3 ~/root/ {print $9}'
vieyahn2017 commented 5 years ago

awk-for循环简单用法

文本:

cat sshd.txt
1 2 3
4 5 6
7 8 9

循环打印上述文本

for 循环的固定格式 i=1设置i的初始变量 i<=NF i变量小于等于 NF变量的值(每行的字段数) i++ 表示i递增+1,

cat sshd.txt |awk '{for(i=1;i<=NF;i++){print $i}}'

1 2 3 4 5 6 7 8 9

vieyahn2017 commented 5 years ago

linux shell awk 流程控制语句(if,for,while,do)详细介绍

https://www.cnblogs.com/chengmo/archive/2010/10/04/1842073.html

vieyahn2017 commented 5 years ago

条件判断语句(if)

awk 'BEGIN{ 
test=100;
if(test>90)
{
    print "very good";
}
else if(test>60)
{
    print "good";
}
else
{
    print "no pass";
}
}'

very good

vieyahn2017 commented 5 years ago

while

awk 'BEGIN{ 
test=100;
total=0;
while(i<=test)
{
    total+=i;
    i++;
}
print total;
}'

5050

vieyahn2017 commented 5 years ago

for 循环

for循环有两种格式:

格式1:

for(变量 in 数组)

{语句}

例子:

awk 'BEGIN{ 
for(k in ENVIRON)
{
    print k"="ENVIRON[k];
}
}'

输出

AWKPATH=.:/usr/share/awk
OLDPWD=/home/web97
SSH_ASKPASS=/usr/libexec/openssh/gnome-ssh-askpass
SELINUX_LEVEL_REQUESTED=
SELINUX_ROLE_REQUESTED=
LANG=zh_CN.GB2312

。。。。。。

说明:ENVIRON 是awk常量,是子典型数组。

vieyahn2017 commented 5 years ago

for循环 格式2

for(变量;条件;表达式)

{语句}

例子:

awk 'BEGIN{ 
total=0;
for(i=0;i<=100;i++)
{
    total+=i;
}
print total;
}'

5050

vieyahn2017 commented 5 years ago

以上为awk流程控制语句,从语法上面大家可以看到,与c语言是一样的。有了这些语句,其实很多shell程序都可以交给awk,而且性能是非常快的。

break 当 break 语句用于 while 或 for 语句时,导致退出程序循环。
continue 当 continue 语句用于 while 或 for 语句时,使程序循环移动到下一个迭代。
next 能能够导致读入下一个输入行,并返回到脚本的顶部。这可以避免对当前输入行执行其他的操作过程。
exit 语句使主输入循环退出并将控制转移到END,如果END存在的话。如果没有定义END规则,或在END中应用exit语句,则终止脚本的执行。
vieyahn2017 commented 5 years ago

性能比较

 time (awk 'BEGIN{ total=0;for(i=0;i<=10000;i++){total+=i;}print total;}')
50005000

real    0m0.003s
user    0m0.003s
sys     0m0.000s

time (total=0;for i in $(seq 10000);do total=$(($total+i));done;echo $total;)
50005000

real    0m0.141s
user    0m0.125s
sys     0m0.008s

实现相同功能,可以看到awk实现的性能是shell的50倍!

vieyahn2017 commented 5 years ago

awk 用法(使用入门)

大全

https://www.cnblogs.com/emanlee/p/3327576.html

vieyahn2017 commented 5 years ago

Table 1. awk的环境变量

变量 描述
$n 当前记录的第n个字段,字段间由FS分隔。
$0 完整的输入记录。
ARGC 命令行参数的数目。
ARGIND 命令行中当前文件的位置(从0开始算)。
ARGV 包含命令行参数的数组。
CONVFMT 数字转换格式(默认值为%.6g)
ENVIRON 环境变量关联数组。
ERRNO 最后一个系统错误的描述。
FIELDWIDTHS 字段宽度列表(用空格键分隔)。
FILENAME 当前文件名。
FNR 同NR,但相对于当前文件。
FS 字段分隔符(默认是任何空格)。
IGNORECASE 如果为真,则进行忽略大小写的匹配。
NF 当前记录中的字段数。
NR 当前记录数。
OFMT 数字的输出格式(默认值是%.6g)。
OFS 输出字段分隔符(默认值是一个空格)。
ORS 输出记录分隔符(默认值是一个换行符)。
RLENGTH 由match函数所匹配的字符串的长度。
RS 记录分隔符(默认是一个换行符)。
RSTART 由match函数所匹配的字符串的第一个位置。
SUBSEP 数组下标分隔符(默认值是\034)。
vieyahn2017 commented 5 years ago

awk的内建函数

14.8.1. 字符串函数 sub 函数匹配记录中最大、最靠左边的子字符串的正则表达式,并用替换字符串替换这些字符串。如果没有指定目标字符串就默认使用整个记录。替换只发生在第一次匹配的时候。格式如下:

        sub (regular expression, substitution string):
        sub (regular expression, substitution string, target string)

实例:

        $ awk '{ sub(/test/, "mytest"); print }' testfile
        $ awk '{ sub(/test/, "mytest"); $1}; print }' testfile

第一个例子在整个记录中匹配,替换只发生在第一次匹配发生的时候。如要在整个文件中进行匹配需要用到gsub

第二个例子在整个记录的第一个域中进行匹配,替换只发生在第一次匹配发生的时候。

gsub 函数作用如sub,但它在整个文档中进行匹配。格式如下:

        gsub (regular expression, substitution string)
        gsub (regular expression, substitution string, target string)

实例:

        $ awk '{ gsub(/test/, "mytest"); print }' testfile
        $ awk '{ gsub(/test/, "mytest" , $1) }; print }' testfile

第一个例子在整个文档中匹配test,匹配的都被替换成mytest。

第二个例子在整个文档的第一个域中匹配,所有匹配的都被替换成mytest。

index 函数返回子字符串第一次被匹配的位置,偏移量从位置1开始。格式如下:

      index(string, substring)

实例:

        $ awk '{ print index("mytest", "test") }' testfile

实例返回test在mytest的位置,结果应该是3。

length 函数返回记录的字符数。格式如下:

        length( string )
        length

实例:

        $ awk '{ print length( "test" ) }' 
        $ awk '{ print length }' testfile

第一个实例返回test字符串的长度。

第二个实例返回testfile文件中第条记录的字符数。

substr 函数返回从位置1开始的子字符串,如果指定长度超过实际长度,就返回整个字符串。格式如下:

        substr( string, starting position )
        substr( string, starting position, length of string )

实例:

        $ awk '{ print substr( "hello world", 7,11 ) }' 

上例截取了world子字符串。

match 函数返回在字符串中正则表达式位置的索引,如果找不到指定的正则表达式则返回0。match函数会设置内建变量RSTART为字符串中子字符串的开始位置,RLENGTH为到子字符串末尾的字符个数。substr可利于这些变量来截取字符串。函数格式如下:

        match( string, regular expression )

实例:

        $ awk '{start=match("this is a test",/[a-z]+$/); print start}'
        $ awk '{start=match("this is a test",/[a-z]+$/); print start, RSTART, RLENGTH }'

第一个实例打印以连续小写字符结尾的开始位置,这里是11。

第二个实例还打印RSTART和RLENGTH变量,这里是11(start),11(RSTART),4(RLENGTH)。

toupper 和tolower 函数可用于字符串大小间的转换,该功能只在gawk中有效。格式如下:

        toupper( string )
        tolower( string )

实例:

        $ awk '{ print toupper("test"), tolower("TEST") }'

split 函数可按给定的分隔符把字符串分割为一个数组。如果分隔符没提供,则按当前FS值进行分割。格式如下:

        split( string, array, field separator )
        split( string, array )

实例:

        $ awk '{ split( "20:18:00", time, ":" ); print time[2] }'

上例把时间按冒号分割到time数组内,并显示第二个数组元素18。

vieyahn2017 commented 5 years ago

14.8.2. 时间函数

systime函数返回从1970年1月1日开始到当前时间(不计闰年)的整秒数。格式如下:

        systime()

实例:

        $ awk '{ now = systime(); print now }'

strftime函数使用C库中的strftime函数格式化时间。格式如下:

        systime( [format specification][,timestamp] )

实例:

        $ awk '{ now=strftime( "%D", systime() ); print now }'
        $ awk '{ now=strftime("%m/%d/%y"); print now }'
Table 3. 日期和时间格式说明符 格式 描述
%a 星期几的缩写(Sun)
%A 星期几的完整写法(Sunday)
%b 月名的缩写(Oct)
%B 月名的完整写法(October)
%c 本地日期和时间
%d 十进制日期
%D 日期 08/20/99
%e 日期,如果只有一位会补上一个空格
%H 用十进制表示24小时格式的小时
%I 用十进制表示12小时格式的小时
%j 从1月1日起一年中的第几天
%m 十进制表示的月份
%M 十进制表示的分钟
%p 12小时表示法(AM/PM)
%S 十进制表示的秒
%U 十进制表示的一年中的第几个星期(星期天作为一个星期的开始)
%w 十进制表示的星期几(星期天是0)
%W 十进制表示的一年中的第几个星期(星期一作为一个星期的开始)
%x 重新设置本地日期(08/20/99)
%X 重新设置本地时间(12:00:00)
%y 两位数字表示的年(99)
%Y 当前月份
%Z 时区(PDT)
%% 百分号(%)
vieyahn2017 commented 5 years ago

awk字符串函数,包含使用示例:

https://www.tutorialspoint.com/awk/awk_string_functions.htm

https://www.gnu.org/software/gawk/manual/html_node/String-Functions.html

vieyahn2017 commented 5 years ago

几个实例

test是文件

$ awk '/^(no|so)/' test
-----打印所有以模式no或so开头的行。

$ awk '/^[ns]/{print $1}' test
-----如果记录以n或s开头,就打印这个记录。

$ awk '$1 ~/[0-9][0-9]$/(print $1}' test
-----如果第一个域以两个数字结束就打印这个记录。

$ awk '$1 == 100 || $2 < 50' test
-----如果第一个或等于100或者第二个域小于50,则打印该行。

$ awk '$1 != 10' test
-----如果第一个域不等于10就打印该行。

$ awk '/test/{print $1 + 10}' test
-----如果记录包含正则表达式test,则第一个域加10并打印出来。

$ awk '{print ($1 > 5 ? "ok "$1: "error"$1)}' test
-----如果第一个域大于5则打印问号后面的表达式值,否则打印冒号后面的表达式值。

$ awk '/^root/,/^mysql/' test
----打印以正则表达式root开头的记录到以正则表达式mysql开头的记录范围内的所有记录。如果找到一个新的正则表达式root开头的记录,则继续打印直到下一个以正则表达式mysql开头的记录为止,或到文件末尾。

vieyahn2017 commented 5 years ago

一个验证passwd文件有效性的例子

cat /etc/passwd | awk -F: 'NF != 7{printf("line %d,does not have 7 fields:%s\n",NR,$0)} $1 !~ /[A-Za-z0-9]/{printf("line %d,non alpha and numeric user id:%d: %s\n",NR,$1,$0)} 
 $2 == "*" {printf("line %d, no password: %s\n",NR,$0)}'

cp /etc/passwd 111.log 修改其中,增加:分隔符,把某些行的用户名,密码改为*。修改后的文件内容如下:

$$$:x:1:1:bin:/bin:/sbin/nologin
222:root:x:0:0:root:/root:/bin/bash
daemon:x:2:2:daemon:/sbin:/sbin/nologin
halt:*:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
ntp:x:38:38::/etc/ntp:/sbin/nologin
viid:x:1000:1000::/home/viid:/bin/bash

再执行上面的awk语句

cat 111.log  | awk -F: 'NF != 7{printf("line %d,does not have 7 fields:%s\n",NR,$0)} $1 !~ /[A-Za-z0-9]/{printf("line %d,non alpha and numeric user id:%d: %s\n",NR,$1,$0)}

>  
$2 == "*" {printf("line %d, no password: %s\n",NR,$0)}'
line 1,non alpha and numeric user id:0: $$$:x:1:1:bin:/bin:/sbin/nologin
line 2,does not have 7 fields:222:root:x:0:0:root:/root:/bin/bash
line 4, no password: halt:*:7:0:halt:/sbin:/sbin/halt
vieyahn2017 commented 5 years ago

awk命令选项

-F fs or --field-separator fs 指定输入文件折分隔符,fs是一个字符串或者是一个正则表达式,如-F:。

-v var=value or --asign var=value 赋值一个用户定义变量。

-f scripfile or --file scriptfile 从脚本文件中读取awk命令。

-mf nnn and -mr nnn 对nnn值设置内在限制,-mf选项限制分配给nnn的最大块数目;-mr选项限制记录的最大数目。这两个功能是Bell实验室版awk的扩展功能,在标准awk中不适用。

-W compact or --compat, -W traditional or --traditional 在兼容模式下运行awk。所以gawk的行为和标准的awk完全一样,所有的awk扩展都被忽略。

-W copyleft or --copyleft, -W copyright or --copyright 打印简短的版权信息。

-W help or --help, -W usage or --usage 打印全部awk选项和每个选项的简短说明。

-W lint or --lint 打印不能向传统unix平台移植的结构的警告。

-W lint-old or --lint-old 打印关于不能向传统unix平台移植的结构的警告。

-W posix 打开兼容模式。但有以下限制,不识别:\x、函数关键字、func、换码序列以及当fs是一个空格时,将新行作为一个域分隔符;操作符=不能代替^和^=;fflush无效。

-W re-interval or --re-inerval 允许间隔正则表达式的使用,参考(grep中的Posix字符类),如括号表达式[[:alpha:]]。

-W source program-text or --source program-text 使用program-text作为源代码,可与-f命令混用。

-W version or --version 打印bug报告信息的版本

vieyahn2017 commented 5 years ago

用for和if显示日期

awk  'BEGIN { 
for(j=1;j<=12;j++) 
{ flag=0; 
  printf "\n%d月份\n",j; 
        for(i=1;i<=31;i++) 
        { 
        if (j==2&&i>28) flag=1; 
        if ((j==4||j==6||j==9||j==11)&&i>30) flag=1; 
        if (flag==0) {printf "%02d%02d ",j,i} 
        } 
} 
}'
vieyahn2017 commented 5 years ago

在awk中调用系统变量必须用单引号,如果是双引号,则表示字符串 Flag=abcd awk '{print '$Flag'}' 结果为abcd awk '{print "$Flag"}' 结果为$Flag

vieyahn2017 commented 5 years ago

字符串函数例子

  awk 'gsub(/\$/,"");gsub(/,/,""); cost+=$4; 
  END {print "The total is $" cost>"filename"}'    file 

gsub函数用空串替换$和,再将结果输出到filename中。 1 2 3 $1,200.00 1 2 3 $2,300.00 1 2 3 $4,000.00

    awk '{gsub(/\$/,"");gsub(/,/,""); 
    if ($4>1000&&$4<2000) c1+=$4; 
    else if ($4>2000&&$4<3000) c2+=$4; 
    else if ($4>3000&&$4<4000) c3+=$4; 
    else c4+=$4; } 
    END {printf  "c1=[%d];c2=[%d];c3=[%d];c4=[%d]\n",c1,c2,c3,c4}"' file 

通过if和else if完成条件语句

    awk '{gsub(/\$/,"");gsub(/,/,""); 
    if ($4>3000&&$4<4000) exit; 
    else c4+=$4; } 
    END {printf  "c1=[%d];c2=[%d];c3=[%d];c4=[%d]\n",c1,c2,c3,c4}"' file 

通过exit在某条件时退出,但是仍执行END操作。

    awk '{gsub(/\$/,"");gsub(/,/,""); 
    if ($4>3000) next; 
    else c4+=$4; } 
    END {printf  "c4=[%d]\n",c4}"' file 
通过next在某条件时跳过该行,对下一行执行操作。 
tom-snow commented 2 years ago

这个博主的关于 sed / awk 的教程挺不错 Sed and awk 笔记之 awk 篇:快速了解 Awk-1 Sed and awk 笔记之 awk 篇:快速了解 Awk-2 Sed and awk 笔记之 awk 篇:快速了解 Awk-3

可以点 Sed and Awk 标签查看更多内容。