換一種視角理解 awk 命令

原文鏈接:https://www.barretlee.com/blog/2019/10/29/awk/
原文作者:Barret李靖

awk 是使用頻度非常高的一個超級有用的命令,如果你做過應用的線上運維,想必已經是十分熟悉了,但是對大多數人來說,它仍然是個陌生的東西,即便看過很多次文檔,依然記不住它的模樣,還是得翻文檔、查 Google。下面我就帶着你,換一種視角重新理解 awk。

在這裏插入圖片描述

它是什麼?我覺得它就是一個表單篩選工具,往下看。

一個例子

下面是我截取 cat /etc/passwd 的部分內容,

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin

保存爲 1.txt,然後執行如下命令:

cat 1.txt | awk -F : ' BEGIN { print "No. R1 R3 R4"; count = 0 } $3 > 5 && $3 < 50 && $1 !~ /root/ { count += 1; print NR, $1, $3, $4} END { print "total " count " lines" } '

輸出的內容是:

No. R1 R3 R4
7 man 6 12
8 lp 7 7
9 mail 8 8
10 news 9 9
total 4 lines

看起來好像很複雜的樣子,下面我們進行分解動作,一步一步帶你認識 awk 的世界。

獲取表單

例子裏的數據是結構化的,每一行有 7 條數據,使用 : 連接符合併成一行,總共 10 行,所以你看到的就是一個無表頭的 10 行 7 列數據,我們可以通過如下命令對錶單進行整理:

cat 1.txt | awk -F : ' { print $1, $2, $3, $4, $5, $6, $7 } '

-F 是分隔符標識,默認 awk 命令以空格作爲分隔符,在這個例子中,我們需要告訴程序 : 爲列分隔符。這條命令執行的結果是:

root x 0 0 root /root /bin/bash
daemon x 1 1 daemon /usr/sbin /usr/sbin/nologin
bin x 2 2 bin /bin /usr/sbin/nologin
sys x 3 3 sys /dev /usr/sbin/nologin
sync x 4 65534 sync /bin /bin/sync
games x 5 60 games /usr/games /usr/sbin/nologin
man x 6 12 man /var/cache/man /usr/sbin/nologin
lp x 7 7 lp /var/spool/lpd /usr/sbin/nologin
mail x 8 8 mail /var/mail /usr/sbin/nologin
news x 9 9 news /var/spool/news /usr/sbin/nologin

$0 會打印整行數據,$1 表示第一列數據,同理,我們將七列數據都打印了出來,$1$7 都是 print 函數的入參,事實上,它與這種寫法是一個意思:

cat 1.txt | awk -F : ' { print($1, $2, $3, $4, $5, $6, $7) } '

添加表頭

由於我們提供的數據沒有表頭,我們給它加上:

cat 1.txt | awk -F : ' BEGIN { print("No.", "R1", "R2", "R3", "R4", "R5", "R6", "R7") } { print(NR, $1, $2, $3, $4, $5, $6, $7) } '

在原來的基礎上,我們增加了 BEGIN { } 段落,爲此我們就給內容提供了表頭:

No. R1 R2 R3 R4 R5 R6 R7
1 root x 0 0 root /root /bin/bash
2 daemon x 1 1 daemon /usr/sbin /usr/sbin/nologin
3 bin x 2 2 bin /bin /usr/sbin/nologin
4 sys x 3 3 sys /dev /usr/sbin/nologin
5 sync x 4 65534 sync /bin /bin/sync
6 games x 5 60 games /usr/games /usr/sbin/nologin
7 man x 6 12 man /var/cache/man /usr/sbin/nologin
8 lp x 7 7 lp /var/spool/lpd /usr/sbin/nologin
9 mail x 8 8 mail /var/mail /usr/sbin/nologin
10 news x 9 9 news /var/spool/news /usr/sbin/nologin

NR 是行號的意思,爲了讓它好看一點,我們使用 printf 函數進行格式化打印:

cat 1.txt | awk -F : ' BEGIN { printf("%8s %5s %5s %5s %5s %5s %15s %20s\n", "No.", "R1", "R2", "R3", "R4", "R5", "R6", "R7") } { printf("%5s %8s %5s %5s %5s %5s %15s %20s\n", NR, $1, $2, $3, $4, $5, $6, $7) } '

打印的結果:

  No.    R1    R2    R3    R4    R5              R6                   R7
 1     root     x     0     0  root           /root            /bin/bash
 2   daemon     x     1     1 daemon       /usr/sbin    /usr/sbin/nologin
 3      bin     x     2     2   bin            /bin    /usr/sbin/nologin
 4      sys     x     3     3   sys            /dev    /usr/sbin/nologin
 5     sync     x     4 65534  sync            /bin            /bin/sync
 6    games     x     5    60 games      /usr/games    /usr/sbin/nologin
 7      man     x     6    12   man  /var/cache/man    /usr/sbin/nologin
 8       lp     x     7     7    lp  /var/spool/lpd    /usr/sbin/nologin
 9     mail     x     8     8  mail       /var/mail    /usr/sbin/nologin
10     news     x     9     9  news /var/spool/news    /usr/sbin/nologin

怎麼樣,這個表單看起來就很自然了吧!

表單篩選

想象一下,拿到手裏的是一個 Excel 表格,我們要對錶格進行分析,比如,我要隱藏 R2、R3、R4 這幾欄,十分簡單,打印的時候不寫出來就行了,

cat 1.txt | awk -F : ' BEGIN { printf("%8s %5s %5s %15s %20s\n", "No.", "R1", "R5", "R6", "R7") } { printf("%5s %5s %5s %15s %20s\n", NR, $1, $5, $6, $7) } '

結果是:

  No.    R1    R5              R6                   R7
 1  root  root           /root            /bin/bash
 2 daemon daemon       /usr/sbin    /usr/sbin/nologin
 3   bin   bin            /bin    /usr/sbin/nologin
 4   sys   sys            /dev    /usr/sbin/nologin
 5  sync  sync            /bin            /bin/sync
 6 games games      /usr/games    /usr/sbin/nologin
 7   man   man  /var/cache/man    /usr/sbin/nologin
 8    lp    lp  /var/spool/lpd    /usr/sbin/nologin
 9  mail  mail       /var/mail    /usr/sbin/nologin
10  news  news /var/spool/news    /usr/sbin/nologin

再比如,我們想拿到第六列包含 var 的內容,正則過濾下就行了

cat 1.txt | awk -F : ' $6 ~ /var/ { printf("%5s %5s %5s %15s %20s\n", NR, $1, $5, $6, $7) } '

執行結果是:

 7   man   man  /var/cache/man    /usr/sbin/nologin
 8    lp    lp  /var/spool/lpd    /usr/sbin/nologin
 9  mail  mail       /var/mail    /usr/sbin/nologin
10  news  news /var/spool/news    /usr/sbin/nologin

再來個複雜點的,我們想拿到 R4 範圍在 0~5,並且不包含 Root 的數據:

cat 1.txt | awk -F : ' $4 >= 0 && $4 <= 5 && $1 !~ /root/ { printf("%5s %5s %5s %15s %20s\n", NR, $1, $5, $6, $7) } '

篩選結果出來了:

2 daemon daemon       /usr/sbin    /usr/sbin/nologin
3   bin   bin            /bin    /usr/sbin/nologin
4   sys   sys            /dev    /usr/sbin/nologin

相關的篩選功能還有很多,這裏就不一一枚舉了,上述幾個篩選其實也已經可以滿足大部分的條件了。

增加表尾

我們想統計 R7 包含 nologin 信息的條目數量,展示在表尾,稍微複雜一點,這裏會用到一點編程知識:

cat 1.txt | awk -F : ' BEGIN { count = 0 } { if ($7 ~ /nologin/){ count = count + 1; } printf("%5s %8s %5s %5s %5s %5s %15s %20s\n", NR, $1, $2, $3, $4, $5, $6, $7) } END { printf("total %s items with nologin", count)}'

看到的結果是:

    1     root     x     0     0  root           /root            /bin/bash
    2   daemon     x     1     1 daemon       /usr/sbin    /usr/sbin/nologin
    3      bin     x     2     2   bin            /bin    /usr/sbin/nologin
    4      sys     x     3     3   sys            /dev    /usr/sbin/nologin
    5     sync     x     4 65534  sync            /bin            /bin/sync
    6    games     x     5    60 games      /usr/games    /usr/sbin/nologin
    7      man     x     6    12   man  /var/cache/man    /usr/sbin/nologin
    8       lp     x     7     7    lp  /var/spool/lpd    /usr/sbin/nologin
    9     mail     x     8     8  mail       /var/mail    /usr/sbin/nologin
   10     news     x     9     9  news /var/spool/news    /usr/sbin/nologin
total 8 items with nologin

結尾多了一行,我們來分析下這句命令,總共分爲三段:

  • BEGIN { count = 0 },聲明計數器
  • { if ($7 ~ /nologin/){ count = count + 1; } printf(...) },判斷並計數
  • END { printf("total %s items with nologin", count)},打印結果

小結

以上,揭開了 awk 的面紗以後,你會發現它並沒有那麼複雜。當然,awk 也遠不是上面我們看到的這麼簡單,它的使用可以變得非常複雜只不過在日常的運維過程中我們用不上而已。

把 awk 當做表單處理工具來理解,我相信你一定可以輕鬆記住它的所有命令,就算記不住,以後看到一長串命令也能夠很快地理解它。更多資料可以看這個手冊:The GNU Awk User’s Guide

發佈了80 篇原創文章 · 獲贊 32 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章