Shell编程:文本处理器(awk)(二)



getline用法

getlineawk 中用于读取下一行或从文件、管道获取输入的命令。

它的使用会改变 awk 的一些内置变量,如 NF(字段数)、NR(当前记录数)、$0(当前记录内容)、FNR(当前文件的记录数)等。

以下是 getline 的几种常用形式:

  1. 基本用法(无重定向和管道符):

    • getline 没有重定向符号(<>)或管道符号(|)时,awk 首先读取的是当前行(第一行),而 getline 获取的是光标跳转至下一行(也就是第二行)的内容。例如:

      awk '{getline; print $0}' test1.txt
      

      上述命令相当于打印偶数行,因为 getline 使得光标跳转到下一行。

    • 如果把 getline 放在 print 后:

      awk '{print $0; getline}' test1.txt
      

      相当于打印奇数行,awk 首先打印当前行的内容,然后 getline 跳到下一行,awk 不再处理这行。

  2. 使用重定向或管道符号:

    • getline 两侧有管道符号或重定向符号时,getline 会对指定文件或命令的输出进行操作。因为文件刚被打开时,还没有被 awk 读入任何内容,所以 getline 读取的是文件的第一行,而不是跳转到下一行。

      原因:getline运行之后awk会改变NF,NR,$0,FNR等内部变量,所以此时读取$0的行号不再为1,而是2。

      例如:
      使用重定向将 test1.txt 的内容输出到 test2.txt

      awk '{getline < "test1.txt"; print $0 > "test2.txt";}' test1.txt
      
  3. 变量读取示例:

    • ls 命令的输出传递给 getline 函数:line 是一个变量,将 ls 的内容读入到变量 line,然后打印当前行和 line 的内容(如果无内容,不做任何操作)。

      ls | awk '{getline line; print $0, line;}'
      

文本内容匹配过滤打印

  • 匹配行以 root 开头并打印:

    awk '/^root/{print}' /etc/passwd
    
  • 匹配行以 bash 结尾并打印:

    awk '/bash$/{print}' /etc/passwd
    

BEGIN 和 END 模式

awk 支持在处理数据之前和之后执行一些命令操作。BEGIN 块在数据处理开始前执行,END 块在数据处理结束后执行。

  • 基本格式:

    awk 'BEGIN{...};{...};END{...}' 文件
    
    • BEGIN 块:在处理指定的文本之前,先执行 BEGIN 块中的命令操作。
    • 主处理块 {...}:用于处理文件内容的主命令操作。
    • END 块:在处理完所有文件内容后,执行 END 块中的命令操作。
  • 示例:

    awk 'BEGIN{x=1}; {x++}; END{print x}' test1.txt
    

    计算文件 test1.txt 的行数(包括初始值 x=1),最后输出结果 10

对字段进行处理打印

  • 指定分隔符并打印特定列:

    head -n5 /etc/passwd | awk -F: '{print $1}'  # 使用冒号 `:` 分隔并打印第一列
    head -n5 /etc/passwd | awk -F: '{print $2}'  # 使用冒号 `:` 分隔并打印第二列
    

-v 选项的用法:变量赋值

-v 选项用于给 awk 脚本中的变量赋值。

  • 通过 -v 传递变量:

    FS(字段分隔符)被设置为 :OFS(输出字段分隔符)被设置为 +。然后打印第一列和第三列。

    fs=":"; awk -v FS=$fs -v OFS="+" '{print $1, $3}' /etc/passwd
    
    # 输出:
    root+0
    bin+1
    daemon+2
    adm+3
    lp+4
    
  • 从 Shell 传递变量到 awk

    将 Shell 变量 ab 传递给 awk,并计算它们的和。

    a=3
    b=4
    num=$(awk -v a="$a" -v b="$b" 'BEGIN{print a + b}')
    
  • 普通赋值变量:

    使用冒号 : 作为输入字段分隔符,== 作为输出字段分隔符,打印第一列和第三列。

    awk -v FS=':' -v OFS='==' '{print $1, $3}' /etc/passwd
    
    # 输出:
    root==0
    bin==1
    daemon==2
    adm==3
    lp==4
    sync==5
    
  • 按行输出 $PATH 环境变量中的各个路径:

    $PATH 环境变量包含一系列用冒号分隔的目录路径,系统会在这些目录中查找可执行命令。

    echo $PATH | awk -v RS=':' '{print $1}'
    # 默认就是换行输出,不需要更改
    # 输出示例:
    /usr/local/sbin
    /usr/local/bin
    /usr/sbin
    /usr/bin
    /root/bin
    

    解释:

    1. echo $PATH:打印出当前环境变量 $PATH 的内容。$PATH 包含多个目录路径,这些路径用冒号(:)分隔。
    2. awk -v RS=':' '{print $1}'
      • awk:一种强大的文本处理工具。
      • v RS=':':设置记录分隔符(Record Separator, RS)为冒号(:)。默认情况下,awk 以换行符作为分隔符;此处将其更改为冒号,以便逐个读取 $PATH 中的每个目录。
      • {print $1}:表示输出每条记录的第一个字段(这里的每条记录即为一个目录路径)。

BEGIN 模式指定

  • 改变分隔符并打印:
    awk 执行前使用 BEGIN 块改变分隔符为 :,然后在执行过程中以冒号分隔字段并打印第五列。

    head -n5 /etc/passwd | awk 'BEGIN{FS=":"}; {print $5}'
    

条件判断打印

  • 条件判断打印特定行:
    • 打印 uid 大于 500 的行:

      awk -F: '$3>500{print $0}' /etc/passwd
      
    • 取反打印大于 10 ,为 uid 小于 10 的行:

      awk -F: '!($3>10){print $0}' /etc/passwd
      
    • 使用 if 语句进行判断:
      使用 if 语句,() 内部是条件,{} 外部是执行语句块,相当于嵌套语法。

      awk -F: '{if ($3>500){print $0}}' /etc/passwd
      

awk 的三元表达式

awk 的三元表达式继承了 Java 的用法,格式与 Java 相似。

格式:

awk '(条件表达式) ? (A 表达式或者值) : (B 表达式或者值)'

示例:

awk -F: '{max=($3 >= $4) ? $3 : $4; {print max, $0}}' /etc/passwd | sed -n '1,6p'

这个命令的作用是比较 /etc/passwd 文件中以 : 为分隔符的第三个和第四个字段的大小,并输出结果。

  • max=($3 >= $4) ? $3 : $4:这行代码使用了三元运算符来设置变量 max,如果字段 3 大于等于字段 4,则 max 为字段 3 的值,否则为字段 4 的值。
  • ? : 运算符是 if-else 语句的简写,因此此行等效于 if ($3 >= $4) { max = $3 } else { max = $4 }
  • print max, $0:打印出 max 和当前行的内容。

最终结果是输出比较后的最大值 max,并打印文件的前 6 行。

awk 的精准筛选

awk 允许通过特定条件表达式筛选文本。

  • $n(> < ==):用于对比数值。
  • $n~"字符串":表示第 n 个字段包含某个字符串。
  • $n!~"字符串":表示第 n 个字段不包含某个字符串。
  • $n=="字符串":表示第 n 个字段为某个字符串。
  • $n!="字符串":表示第 n 个字段不为某个字符串。
  • $NF:表示最后一个字段。

实例:

  1. 输出第七个字段包含“bash”所在行的第一个字段和最后一个字段:

    awk -F: '$7~"bash" {print $1, $NF}' /etc/passwd
    
  2. 输出第七个字段不包含“nologin”所在行的第一个字段和最后一个字段:

    awk -F: '$7!~"nologin" {print $1, $NF}' /etc/passwd
    
  3. 指定第六个字段为 /home/dn,第七个字段为 /bin/bash,输出满足这些条件所在行的第一个和最后一个字段:

    awk -F: '($6=="/home/dn") && ($7=="/bin/bash") {print $1, $NF}' /etc/passwd
    

awk 的分隔符用法

RS 指定行分隔符

awk 从文件中读取资料时,将根据 RS 的定义把资料切割成多条记录,awk 一次仅读入一条记录进行处理。内置变量 RS 的默认值是 "\n"(换行符)。可以使用 BEGIN 模式在操作前改变行分隔符。

示例:

: 作为行分隔符,分割后逐行打印内容和行号。

echo $PATH | awk 'BEGIN{RS=":"};{print NR, $0}'

指定输出的分隔符

OFS(输出字段分隔符)用于指定输出内容的列分隔符。注意:$n=$n 用于激活分隔符,否则不生效,n 必须存在。

示例:

  1. 使用 tr 改变分隔符输出:

    echo a b c d | tr " " ":"
    
  2. 使用 awk 改变输出分隔符:

    echo a b c d | awk '{OFS=":"; $1=$1; print $0}'
    echo a b c d | awk 'BEGIN{OFS=":"}; {$2=$2; print $0}'
    

awk 结合数组运用

awk 中定义数组打印

示例:

输出数组 a 中索引为 1 的值,即 20

awk 'BEGIN{a[0]=10; a[1]=20; a[2]=30; print a[1]}'

awk 中的数组遍历

示例:

遍历数组 a,打印索引和值。(在awk中打印变量不需要加 $

awk 'BEGIN{a[0]=10; a[1]=20; a[2]=30; for(i in a) print i, a[i]}'

处理文件去重统计

通过 awk 可以方便地统计文件中每个单词的出现次数。(在awk中打印变量不需要加 $

示例:

cat test3.txt
aaa
aaa
bbb
ccc
aaa
bbb
aaa

awk '{a[$1]++}; END{for(i in a){print i, a[i]}}' test3.txt

解释说明:

  • a[$1]++:将当前行的第一个字段作为索引,数组 a 对应的值加一。
  • END{...}:在文本读取完毕后,执行 END 块中的命令。
  • 最终输出每个唯一字段及其在文件中出现的次数。

调试示例:

awk -v debug=1 '{print "Processing line", NR, "with value", $1; a[$1]++}; END{for(i in a){print "Result for value", i, "is", a[i]}}' test2.txt

用于检测 awk 的运行机制。

这种逐行读取和处理的方式,能够高效地对数据进行去重统计。

原理:

a[$1]初始为0,a[$1]++后即为1,
而这里awk中的a[$1]++最终的值是由test2.txt文本内容有多少行决定的,awk本身就是编程语言,不要按照shell来
文本逐行读取完毕后再执行END中的命令
awk会按行读取文件test2.txt的内容,然后逐个单词存储到数组a中。
每个单词都作为数组a的索引,所以每个单词都对应一个值,初始值为0。
读取文件的第一行,单词为 aaa,此时a[aaa]为0,执行 a[$1]++ 后,a[aaa]变成1。
读取文件的第二行,单词为 aaa,此时a[aaa]为1,执行 a[$1]++ 后,a[aaa]变成2。
读取文件的第三行,单词为 bbb,此时a[bbb]为0,执行 a[$1]++ 后,a[bbb]变成1。
读取文件的第四行,单词为 ccc,此时a[ccc]为0,执行 a[$1]++ 后,a[ccc]变成1。
读取文件的第五行,单词为 aaa,此时a[aaa]为2,执行 a[$1]++ 后,a[aaa]变成3。
读取文件的第六行,单词为 bbb,此时a[bbb]为1,执行 a[$1]++ 后,a[bbb]变成2。
读取文件的第七行,单词为 aaa,此时a[aaa]为3,执行 a[$1]++ 后,a[aaa]变成4。
最终,数组a中的元素值变成:a[aaa]=4,a[bbb]=2,a[ccc]=1


  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值