导论:awk的世界
- awk options 'pattern { action }' input-files
- #或
- awk options -f awk-scripting-file input-files
- pattern部分用来定位具体需要action的行:几乎是任何表达式,一般情况下,是一个ERE正则。pattern省略后,即action对每一行都会生效。
- actions部分用来执行具体操作:为任何awk语句,一般情况下,是一个print $n语句。action省略后,action即{ print },即打印整行。
- 注释为#符号开头,类比shell;
- 任何地方允许空白,单条语句不能多行,除非结尾有'\',类比shell;
- awk字串最短为空,长度为0,最长无限制,视内存而定。
- 字串的比较用"==,!=,<,>,>="比较,非零为真,短的字符串总<长字符串,如"a"<"aa"返回1;
- awk没有特殊的字符串接续操作符,类比shell,两个字串按照字面的排列合在一起。
- awk用一律在内部用双精度浮点数表示任何数值;
- #对于第三点,字符串相连
- $ awk 'BEGIN{ s="ab" "cd" ; q="xy" "z" ; s=s q ; print s }'
- abcdxyz #这里s q空格必须
- $ awk -v external_var=ext 'BEGIN { for (k=0;k<ARGC;k++)
- print "ARGV["k"] is "ARGV[k] }' s*
- ARGV[0] is awk
- ARGV[1] is sed.sh
- ARGV[2] is standardio.sh
- ARGV[3] is subprocess.sh
- #可见第一个参数ARGV[0]始终是awk程序,选项-v没有计入参数列表中。
- $ awk 'BEGIN{ print ENVIRON["LANG"] ; print ENVIRON["SHELL"] }'
- en_US
- /bin/bash
- #在BEGIN块里实现FS和OFS的全局设置;
- #取用户名和用户登录shell并用*号隔开打印
- #!/bin/bash
- awk ' BEGIN{#这里的BEGIN是模式,{}是action
- FS=":"
- OFS="*"
- }
- { print $NF , $1 } //逆序
- END{
- #comment here
- }' /etc/passwd
- #把单词列表逐行输出
- $ echo who is my love | awk '{ OFS = "\n" ; $1 = $1 ; print }' #$1=$1必须,至少指定一个字段的值,否则$0不会变
- who
- is
- my
- love
- -------------------------
- #打印全文,同cat file
- awk '{ print $0 }' file
- awk '{ print }' file
- 记录record:由RS分隔的字串称为一条记录,RS默认为\n,即一条记录为一行,$0代表整条记录;NR为当前行的行数(总文件范围下的);
- 域field:一条记录的每个字段叫“域”,NF为当前域(字段)的个数;输入域分隔符是FS,默认情况下是\t和空格的组合,可通过-F命令行或FS赋值指定。域的引用:$1,$2,...,$NF。
- #产出PID和进程命令(ps -ef的第2列和最后一列)
- ps -ef | awk '{ print $2 , $NF }'
- #取用户名和用户登录shell
- awk -F':' '{ print $1,$NF }'
- 进阶
- awk -v FS=":" -v OFS="<->" '{print $1 ,$2,$NF}' /etc/passwd
- root<->x<->/bin/bash
- bin<->x<->/sbin/nologin
- daemon<->x<->/sbin/nologin
- -----------------------------------
- $ echo | awk -v 'a=yes' '{print a }'
- yes
- if ( expression1 ){
- statement1
- }else if ( expression2 ){
- statement2
- }else{
- statement3
- }
- while ( expression ){
- statement
- break #可选
- }
- for( i=1 ; i<=5 ; i++ ){
- statement
- break|continue #不支持多层break
- }
- $ awk 'BEGIN{
- hash["a"]=1
- hash["b"]=2
- hash["c"]=3
- for ( name in hash ){ #name相当于perl里的for $_ (keys %hash)
- print name "=>" hash[name]
- }
- }'
- a=>1
- b=>2
- c=>3
- awk 'BEGIN{
- hash["a"]=1
- hash["b"]=2
- hash["c"]=3
- if( "d" in hash ){
- print "in"
- }else{
- print "out"
- }
- }'
- --------------------------------
- #而非
- if ( hash["d"] != "" ) ... #这样会引用hash["d"],使之立即初始化为空值数组元素。
- $ awk 'BEGIN{
- cmd = "uptime"
- if ( system( cmd ) != 0 ){ #推荐加调用判断
- print "system call bad"
- }}'
- 23:08:21 up 151 days, 3:02, 5 users, load average: 0.00, 0.00, 0.00
- $ awk '{ print $0 > "youtube.txt" END{ close("youtube.txt") }' plain.txt
- $ cat youtube.txt
- send someone to love me
- i need to rest in arms
- ...
- $ awk -v awk_var=$you 'BEGIN { print awk_var }'
- me
- $ awk 'BEGIN { print "'$you'" }'
- me
- $ echo "Legend" | awk '{ print length($0) }'
- 6
- echo "Legend" | awk '{ print substr($0,4,3) }' #好吧,index是从1开始的
- end
- $ echo "Legend" | awk '{ print toupper($0) }'
- LEGEND
- $ echo "Legend" | awk '{ print tolower($0) }'
- legend
- $ echo "Legend" | awk '{ print index($0,"ge") }'
- 3 #返回ge的起始index位置
- $ echo "I'm the Legend" | awk --re-interval '{ #巨坑:awk不支持{n,m}的ere,必须制定--posix或--re-interval才能使用{n,m}的正则
- print match($0,/[a-z]{3}/)
- print substr($0,RSTART,RLENGTH )
- }'
- 5
- the
- $ awk 'BEGIN{
- string = "champion12sh3ip"
- print string
- sub( /[0-9]/ , "" , string)
- print string
- }'
- champion12sh3ip
- champion2sh3ip
- --------------------
- $ #将sub换成gsub
- champion12sh3ip
- championship
- --------------------
- $ gsub( /[0-9]/ , "&&" , string) #特殊用法,&为regex匹配的文本段,&&为重复两遍
- champion1122sh33ip
- $ awk 'BEGIN{
- string = "yes I am what i am " #最后的空白省略了,和FS处理时一样
- len = split( string , array )
- print len
- for( i=1 ; i<=len ; i++){
- print ">" array[i]
- }
- }'
- 6
- >yes
- >I
- >am
- >what
- >i
- >am
- ---------------------------------------------
- $ awk 'BEGIN{
- string = "money:is:very:bad:" #不会省略最后一个":"
- len = split( string , array , ":")
- print len
- for( i=1 ; i<=len ; i++){
- print ">" array[i]
- }
- }'
- 5
- >money
- >is
- >very
- >bad
- >
- -----------------------------------------------
- $ awk 'BEGIN{ #把字串每一个字符提出的好方法
- string = "I am what i am"
- len = split( string, chars, "" )
- for ( idx = 1 ; idx <= len ; idx++ ) {
- print chars[idx]
- }
- }'
- $
- i$
- ^I$
- a$
- m$
- ----------------------------------------
- $ split("", array) #清空数组
- $ awk 'BEGIN{
- a="i am the"
- b="99"
- c="C"
- printf("%s : %d,%c\n", a, b, c)
- d=sprintf("%s-%d-%c",a,b,c)
- print d
- x=-7.58
- printf "%010.5f\n", x
- }'
- i am the : 99,C
- i am the-99-C
- -007.58000
- #这个例子实现了给定n,m,输出这个区间的随机整数。
- awk '
- BEGIN{
- myrand = gen_random(10,20)
- print myrand
- }
- function gen_random( low , high ){ #函数可以在任意成对的模式/操作组前后之间定义
- low = int( low )
- high = int( high )
- if( low >= high ){ #health check
- return low
- }
- srand()
- n = low + int( rand() * ( high + 1 - low ) ) #因为取整,+1必须,0<=rand()<1
- return n
- }
- '
- $ for((i=1;i<=100;i++)); do sh awk.sh; sleep 1; done
- 17
- 19
- 12
- 18
- 16
- 17
- ...
- $ awk 'NF > 0' file
- $ awk 'NF > 0 { print $0 }' file #等价形式
- -----------------------------------
- $ awk '{ print }' $file #同cat file
- echo $list | awk '{ OFS="\n" ; $1=$1 ; print $0 }'
- $ awk '{count+=length($0)+1;word+=NF } END{ print NR , word , count }' plain.txt
- #length($0)计算整个行的字符数,+1算上换行符;
- $ awk 'BEGIN{ col=N } { sum += $col } END{ print sum/NR }' file #N是用户定义的列索引值
- $ grep -E 'pattern' files #ERE
- $ awk '/pattern/ { print FILENAME ":" FNR ":" $0 }' files #注意要用FNR,不是NR
- $ awk 'BEGIN{ RS="\r\n" } { print }' windows_format.txt > unix_format.txt #指定RS值
- $ sed 's/\r$//' windows_format.txt > unix_format.txt #sed用法
- $ tr -d '\r' < windows_format.txt > unix_format.txt #tr用法
- $ dos2unix -n windows_format.txt unix_format.txt #dos2unix用法
- $ awk 'BEGIN{
- string = "I am what i am"
- len = split( string, chars, "" )
- for ( idx = 1 ; idx <= len ; idx++ ) {
- print chars[idx]
- }
- }'