如何在 Linux / Unix 上使用 awk 打印文件名

Q:我想在 Linux / 类Unix 系统上使用 awk 打印文件名。 如何使用 awk 的 BEGIN 特殊模式打印文件名? 我可以使用 gawk/awk 打印当前输入文件的名称吗?(LCTT 译注:读者最好能有一些 awk 的背景知识,否则阅读本文的时候会有一些困惑)

在 FILENAME 变量中存放着当前输入文件的名称。 您可以使用 FILENAME 显示或打印当前输入文件名,如果在命令行中未指定文件,则 FILENAME 的值为 – (标准输入)(LCTT 译注:多次按下回车键即可看到效果)。 但是,除非由 getline 设置,否则 FILENAME 在 BEGIN 特殊模式中未定义。

使用 awk 打印文件名

使用语法如下:

awk '{ print FILENAME }' fileNameHere 
awk '{ print FILENAME }' /etc/hosts

因 awk 逐行读取文件,因此,你可能看到多个文件名,为了避免这个情况,你可以使用如下的命令:(LCTT 译注:FNR 表示当前记录数,只在文件中有效)

awk 'FNR == 1{ print FILENAME } ' /etc/passwd 
awk 'FNR == 1{ print FILENAME } ' /etc/hosts

未分类

使用 awk 的 BEGIN 特殊规则打印文件名

使用下面的语法:(LCTT 译注:ARGV[I] 表示输入的第 i 个参数)

awk 'BEGIN{print ARGV[1]}' fileNameHere 
awk 'BEGIN{print ARGV[1]}{ print "someting or do something on data" }END{}' fileNameHere 
awk 'BEGIN{print ARGV[1]}' /etc/hosts

示例输出:

/etc/hosts

然而,ARGV[1] 并不是每一次都能奏效,例如:

ls -l /etc/hosts | awk 'BEGIN{print ARGV[1]} { print }'

你需要将它修改如下(假设 ls -l 只产生一行输出):

ls -l /etc/hosts | awk '{ print "File: " $9 ", Owner:" $3 ", Group: " $4 }'

示例输出:

File: /etc/hosts, Owner:root, Group: root

处理由通配符指定的多个文件名
使用如下的示例语法:

awk '{ print FILENAME; nextfile } ' *.c 
awk 'BEGIN{ print "Starting..."} { print FILENAME; nextfile }END{ print "....DONE"} ' *.conf

示例输出:

Starting...
blkid.conf
cryptconfig.conf
dhclient6.conf
dhclient.conf
dracut.conf
gai.conf
gnome_defaults.conf
host.conf
idmapd.conf
idnalias.conf
idn.conf
insserv.conf
iscsid.conf
krb5.conf
ld.so.conf
logrotate.conf
mke2fs.conf
mtools.conf
netscsid.conf
nfsmount.conf
nscd.conf
nsswitch.conf
openct.conf
opensc.conf
request-key.conf
resolv.conf
rsyncd.conf
sensors3.conf
slp.conf
smartd.conf
sysctl.conf
vconsole.conf
warnquota.conf
wodim.conf
xattr.conf
xinetd.conf
yp.conf
....DONE

nextfile 告诉 awk 停止处理当前的输入文件。 下一个输入记录读取来自下一个输入文件。 更多信息,请参见 awk/gawk[1] 命令手册页https://www.gnu.org/software/gawk/manual/

man awk 
man gawk

awk的二维数组

awk二维数组练习

  • 现有f1,f2两个文档
$cat f2
    5   6   7   8   9   10
A   0.7 0.8 0.9 1   1.1 1.2
C   0.22    0.34    0.46    0.58    0.7 0.82
D   -0.26   -0.12   0.02    0.16    0.3 0.44
E   -0.74   -0.58   -0.42   -0.26   -0.1    0.06
F   -1.22   -1.04   -0.86   -0.68   -0.5    -0.32
G   -1.7    -1.5    -1.3    -1.1    -0.9    -0.7
H   -2.18   -1.96   -1.74   -1.52   -1.3    -1.08
I   -2.66   -2.42   -2.18   -1.94   -1.7    -1.46
K   -3.14   -2.88   -2.62   -2.36   -2.1    -1.84
L   -3.62   -3.34   -3.06   -2.78   -2.5    -2.22
M   -4.1    -3.8    -3.5    -3.2    -2.9    -2.6
$cat f1
5   A
8   C
10  G
11  D
12  F
13  H

需求:

根据f2文件的第一行第一列为序号,求出f1中各对应的结果,打印在第三列

[root@Web awk]# awk 'FNR==NR{for(i=0;i++<NF;){if(NR==1)a[i+1]=$i;if(NR>1)b[a[i],$1]=$i};next}{print $0,b[$1,$2]}'  f2  f1
5   A 0.7
8   C 0.58
10  G -0.7
11  D 
12  F 
13  H 

思路

awk 'FNR==NR{for(i=0;i++<NF;){if(NR==1)a[i+1]=$i;if(NR>1)b[a[i],$1]=$i};next}{print $0,b[$1,$2]}'  f2  f1
对每一行进行循环,对第一行进行对应数组处理
a[1]=$0,a[2]=5,a[3]=6,a[4]=7,a[5]=8,a[6]=9,a[7]=10
然后下次循环跳过第一列

b[a[1],$1]=$1,b[a[2],$1]=$2,b[a[3],$1]=$3,b[a[4],$1]=$4,b[a[5],$1]=$5,b[a[6],$1]=$6,b[a[7],$1]=$7

这样就形成了我们需要的二维数组
然后循环整个f2文件
最后对f1文件获取,将结果打印在f1文件的第三列

正则表达式之awk

  • awk兼具sed所有的功能,并且更加强大。它也是流式编辑器,针对文档中的行来操作。一行一行的执行。

(1)截取文档中的某个字段

head -n2   test.txt  |awk  -F  ': '   '{print  $1}'  //-F用来指定分隔符。不加-F选项,默认使用空格或者tab为分隔符,print为打印的意思。 $1表示打印第1字段   $0表示整行  

未分类

如果需要截取多个字段,可以在{ }中用“,”来分隔

(2)指定分隔符号

awk -F ':' '{print $1"#"$5"#"$6}' test.txt //将之前的:分隔符号替换为#,必须使用双引号引起来。

未分类

(3)匹配功能

awk '/oo/' test.txt //匹配出现oo的行

未分类

(4)匹配某段中出现的字符

awk -F ':' '$1 ~ /oo/' test.txt

未分类

awk命令可以直接使用特殊符号而不用使用转义字符

(5)支持多个条件匹配

awk -F ':' '/oo/ {print $1,$4} /user1/ {print $1,$6}' test.txt

未分类

(6)条件操作符

awk -F ':' '$3==0'  test.txt  //这里表示打印第3段等于0的行,要想等于必须使用2个=,不然就是赋值了。

未分类

也可以这样来打印

awk -F ':' '$3>=500 {print $0}' test.txt

未分类

这里写图片描述在和数字进行比较时,若把比较的数字用双引号括起来,那么awk不会认为是数字,而会认为是字符,那么就会按ASCII码表来排序,得不到想要的结果

(7)打印出某段不等于xx的行

awk -F ':' '$7!="/sbin/nologin"  {print $0} ' test.txt //字符作为判断条件则是要使用双引号括起来的   !=表示不等于

未分类

(8)2字段之间比较

awk -F ':'  '$3<$4' test.txt  //打印第3段小于第4段的行,比较的是数字

未分类

awk -F ':'  '$3==$4' test.txt //打印第3段与第4段相同的行。

未分类

(9)在2个字符之间查找

awk -F ':' '$3>"4" && $3<"8"' test.txt //第3段大于某个字符并且小于某个字符 。这里数字使用了双引号,所以表示字符 

未分类

2个条件满足一个的也打印出来

未分类

  • awk的内置变量

(1)OFS和-F选项有类似的功能,也是用来定义分隔符的,但是它是在输出的时候定义的

未分类

未分类

(2)变量NR的用法 表示行号

未分类

(3变量NF表示用分隔符分隔后一共有多少段

未分类

(4)使用NR打印前多少行

未分类

(5)打印2个条件同时满足的行

未分类

(6)2个变量的应用

未分类

(7)赋值

未分类

赋值后没有了分隔符号了,使用OFS重新定义

(8)求和

未分类

使用awk分析nginx访问日志access.log的ip

access.log为nginx的访问日志,默认路径在

/var/log/nginx/access.log

分析access.log的ip命令如下:

awk '{print $1}' access.log |sort|uniq -c|sort -n
  1. 命令里使用awk过滤出访问的ip

  2. 使用sort对ip排序

  3. 对排序后的ip进行统计,统计每一个ip访问数

  4. 最后使用sort 对访问数进行排序,排序为升序。

awk 赋值多个Shell变量

需求

将文件夹中的两个目录分别赋值给两个变量
文件夹名:test_dev、test_release

实现

#! /bin/bash
FOLDERS=$(ls | grep -v '.sh')

echo $FOLDERS

if [[ $FOLDERS =~ "dev" && $FOLDERS =~ "release" ]]; then
        eval $(echo $FOLDERS | awk -F' ' '{printf("DEV_PATH=%s;RELEASE_PATH=%s",$1,$2);}')
fi
echo "DEV_PATH: " $DEV_PATH
echo "RELEASE_PATH: " $RELEASE_PATH

结果

test_dev test_release
DEV_PATH:  test_dev
RELEASE_PATH:  test_release

awk中使用shell变量

其实在awk里,是不能直接使用shell变量的
方法是:awk -v 选项让awk 里使用shell变量

TIME=60 
awk -v time="$TIME" 'BEGIN{FS="|"} {if ($7>time) print $2 }' 

这样要注意:在awk里,time不能加$符号。

网上说如下方法都可行:

一:”‘$var’”

这种写法大家无需改变用’括起awk程序的习惯,是老外常用的写法.如:

var="test" 
awk 'BEGIN{print "'$var'"}'

这种写法其实际是双括号变为单括号的常量,传递给了awk.

如果var中含空格,为了shell不把空格作为分格符,便应该如下使用:

var="this is a test" 
awk 'BEGIN{print "'"$var"'"}' 

二:’”$var”‘

这种写法与上一种类似.如果变量含空格,则变为’””$var””‘较为可靠.

三.把括起awk程序的”变为””,使用”$var”

如:

$var="this is a test" 
awk 'BEGIN{print "$var"}" 

这是因为在””里$是特殊字符,而在”里$是普通字符.

四:export 变量,使用ENVIRON[“var”]形式,

如:

$var="this is a test";export $var 
awk 'BEGIN{print ENVIRON["var"]}' 

五:当然也可以使用-v选项

如:

$var="this is a test" 
awk -v nvar="$var" '{print nvar}'

这样便把系统变量定义成了awk变量.

awk获取文本的某一行,某一列

打印文件的第一列(域)  awk '{print $1}' filename
打印文件的前两列(域)  awk '{print $1,$2}' filename
打印完第一列,然后打印第二列  awk '{print $1 $2}' filename
打印文本文件的总行数  awk 'END{print NR}' filename
打印文本第一行       awk 'NR==1{print}' filename
打印文本第二行第一列  sed -n "2, 1p" filename | awk 'print $1'

Bash里面的赋值方法有两种,格式为

1) arg=`(命令)`
2) arg=$(命令)

想要把某一文件的总行数赋值给变量nlines,可以表达为:

1) nlines=`(awk 'END{print NR}' filename)`
或
2) nlines=$(awk 'END{print NR}' filename)

——EOF——

linux三大利器–grep|sed|awk

  • grep 文本查找
  • sed 行编辑器
  • awk 文本处理工具

grep

grep 比较简单 查找文本离不开正则 具体用法如从简单到复杂如

grep '[1-9]' 文件名 //匹配含有1到9数字
grep '[^1-9]' 文件名 //匹配除了1-9数字的其他字符
grep '^root' 文件名 //^变成头字符 以root开头
grep '^$' 文件名 //头和尾加起来 匹配空行
grep '.' 文件名 //匹配.
grep 'w' 文件名 //等同([a-zA-Z1-9_])
grep 'W' 文件名 //等同([^a-zA-Z1-9_])
grep 'b' 文件名 //表示单词分隔 如 b[a]b 就是a
grep 'sb+' 文件名 //匹配至少出现一次的sb
grep 'sb*' 文件名 //有s或者b都可以
grep '.' 文件名 //匹配任意字符

sed

  • 可用自动处理文件
  • 分析日志文件
  • 修改配置文件

  • sed的处理原则是行处理,而且不改变源文件

sed的格式

sed [options] ‘command’ file(s) //命令行格式
sed 'p' passwd #会打印出两行,因为sed的原理是读入一行,输出一行,此处再加上p命令打印出来的一行,所以最后会打印出两行
sed -n 'p'passwd #加了-n选项之后,只会打印出相关的行,那些不相关的行则不会打印出来

awk 获取指定列的内容进行判断输出

现在有一些日志,要获取倒数第二行的内容,并且判断其值是否为 0,如果为0则输出对应的行。

[root@centos ~]# awk -F ',' '{if($7==0) print $0}' test.txt
上海, 上海, 中国移动, 0.000000 0.000000, 2017-10-10 16:45:14, http://upod-dl.fm/m4a/57c555ed7cb8912ec41015b0_5820095_24.m4a?sign=376ed5bda7ca56c990aed71e107498c0&t=59dd3192, 0, 404

或者

awk -F ',' '{ if($(NF-1)==0) print $0}' test.txt

其中 NF 表示最后一行,NF-1 表示倒数第二行。

awk使用

awk操作两个文件

  • NR,表示awk开始执行程序后所读取的数据行数.

  • FNR,与NR功用类似,不同的是awk每打开一个新文件,FNR便从0重新累计.

下面看两个例子:

1、对于单个文件NR 和FNR 的 输出结果一样的 :

awk ‘{print NR,$0}’ file1

1 a b c d 2 a b d c 3 a c b d
awk ‘{print FNR,$0}’ file1

1 a b c d 2 a b d c 3 a c b d

2、但是对于多个文件 :

awk ‘{print NR,$0}’ file1 file2

1 a b c d 2 a b d c 3 a c b d 4 aa bb cc dd 5 aa bb dd cc 6 aa cc bb dd
awk ‘{print FNR,$0}’ file1 file2

1 a b c d 2 a b d c 3 a c b d 1 aa bb cc dd 2 aa bb dd cc 3 aa cc bb dd

在看一个例子关于NR和FNR的典型应用:

现在有两个文件格式如下:

cat account

张三|000001 李四|000002
cat cdr

000001|10 000001|20 000002|30 000002|15

想要得到的结果是将用户名,帐号和金额在同一行打印出来,如下:

张三  000001   10

张三  000001   20

李四  000002   30

李四  000002   15

执行如下代码

awk -F | ‘NR==FNR{a[$2]=$0;next}{print a[$1]”   “$2}’ account cdr

注释:

由NR=FNR为真时,判断当前读入的是第一个文件account,然后使用{a[$2]=$0;next}循环将account文件的每行记录都存入数组a,并使用$2第2个字段作为下标引用.

未分类