awk 文本處理

awk 文本處理

awk 是一種樣式掃描和處理語言,使用 Linux 的 awk 命令能夠高效快捷地進行文本處理。awk 掃描文本的每一行並執行指定的命令。html

awk 誕生於 1977 年,借鑑了 C 語言等編程語言,名字取自三位設計者 Alfred Aho、Peter Weinberger 和 Brian Kernighan 的姓氏。awk 的版本衆多,本文中使用的是 Ubuntu 上的 GNU Awk,在 MacOS 上可使用 HomeBrew 安裝 gawk。node

用法

awk 能夠直接在命令行中執行,也能夠編寫.awk後綴的文件而後執行。awk 以行爲單位進行文本處理,對於接收到的每一行都會執行指定的行爲。正則表達式

命令行執行

$ awk [ -F fs ] [ -v var=value ] 'pattern {action}' [ file ...  ]
複製代碼

其中 -F 指定分隔符,-v 指定 awk 的內置變量。編程

例如對於 /etc/passwd 文件中的內容:bash

root:x:0:0:root:/root:/usr/bin/zsh
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
複製代碼

若是要輸出每一行的內容,可使用編程語言

$ awk '{print $0}' /etc/passwd
複製代碼

其中 $0 表示掃描到的文本行。函數

文件執行

.awk 文件能夠分紅三個部分來寫,以下:ui

# passwd.awk

BEGIN{
  FS="\n";
  print "Before action";
}
{
  print $0;
}
END{
  print "After action";
}
複製代碼

BEGIN 塊用於定義處理每一行以前的行爲,能夠用來設置 awk 的內置變量,設置好後在後面處理每一行時都會生效。spa

END 塊用於定義處理完文本以後的行爲,能夠用來輸出一些總結信息。命令行

BEGIN 和 END 中間的塊是對於每一行的操做。直接在命令行中執行 awk 時也能夠用 BEGIN 和 END 塊。

寫好文件後在命令行中執行:

Before action
root:x:0:0:root:/root:/usr/bin/zsh
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
After action
複製代碼

變量

$ + 數字

$0 表示掃描到的行,$1表示將該行分隔後的第 1 項,$2表示將該行分隔後的第 2 項,以此類推。

爲了輸出 /etc/passwd 的用戶名(第一項),能夠執行下面語句:

$ awk -F ':' '{print $1}' /etc/passwd
root
daemon
bin
sys
複製代碼

這裏以處理第一行 root:x:0:0:root:/root:/usr/bin/zsh 爲例,awk 處理是會先按照 -F 設置的分隔符 : 將這行分割爲 root​ x ​0 0 root /root /usr/bin/zsh而後輸出第一項 root

特殊變量

  1. FS(field separator)

    FS 是輸入字段分隔符,例如上面設置的 :,其默認值是空格,能夠在命令行中使用 -F 設置,也能夠在 BEGIN 塊中經過 FS= 設置爲某個字符串或者一個正則表達式。例如:

    $ awk -F ":" '{print $1,$2,$3}' /etc/passwd
    root x 0
    daemon x 1
    bin x 2
    sys x 3
    複製代碼
  2. OFS(output field separator)

    OFS 是輸出字段鏈接符,上面例子中的輸出默認使用空格做爲輸出字段鏈接符,經過設置 OFS 變量進行修改:

    $ awk -F ":" -v OFS="-" '{print $1,$2,$3}' /etc/passwd
    root-x-0
    daemon-x-1
    bin-x-2
    sys-x-3
    複製代碼
  3. RS(record separator)

    前面提到的例子中,awk 都默認以行爲單位處理文本,每一行保存了一條記錄,這是由於默認的記錄分隔符 RS 爲 "\n"。還有一些文本在存儲時並非想 csv 等文件同樣以行單位,例如:

    # people.txt
    
    P1
    male
    15
    
    p2
    female
    20
    
    p3
    male
    19
    複製代碼

    上面的文件中使用 "\n\n" 分隔記錄,每一個記錄中又使用 "\n" 分隔字段,能夠這樣處理:

    $ awk -F "\n" -v RS="\n\n" '{print $1,$2,$3}' people.txt 
    P1 male 15
    p2 female 20
    p3 male 19
    複製代碼
  4. ORS(output field separator)

    與 RS 相似,ORS 設置輸出的記錄分隔符。

    $ awk -F "\n" -v RS="\n\n" -v ORS="\n***\n" '{print $1,$2,$3}' people.txt
    P1 male 15
    ***
    p2 female 20
    ***
    p3 male 19
    ***
    複製代碼
  5. NR(number of records)

    NR 表示當前正在處理的記錄的是第幾項,若是 NR 出如今 END 塊中則表示已處理的記錄數

    $ awk -F ":" '{print "line" NR ":" $1,$2,$3}' /etc/passwd           
    line1:root x 0
    line2:daemon x 1
    line3:bin x 2
    line4:sys x 3
    複製代碼

    若是同時處理多個文件,那麼這個項數會累加

    $ awk -F ":" '{print "record" NR ":" $1,$2,$3}' people.txt /etc/passwd
    record1:P1  
    record2:male  
    record3:15  
    record4:  
    record5:p2  
    record6:female  
    record7:20  
    record8:  
    record9:p3  
    record10:male  
    record11:19  
    record12:root x 0
    record13:daemon x 1
    record14:bin x 2
    record15:sys x 3
    複製代碼
  6. NF(number of fields)

    NF 表示一條記錄中分隔後的字段數,所以這個值與設置的 FS 有關:

    # 以 ":" 爲分隔符
    $ awk -F ":" '{print "record" NR " with " NF " fields:" $1,$2,$3}' /etc/passwd
    record1 with 7 fields:root x 0
    record2 with 7 fields:daemon x 1
    record3 with 7 fields:bin x 2
    record4 with 7 fields:sys x 3
    
    # 以 "o" 爲分隔符
    $ awk -F "o" '{print "record" NR " with " NF " fields:" $1,$2,$3}' /etc/passwd
    record1 with 7 fields:r  t:x:0:0:r
    record2 with 5 fields:daem n:x:1:1:daem n:/usr/sbin:/usr/sbin/n
    record3 with 3 fields:bin:x:2:2:bin:/bin:/usr/sbin/n l gin
    record4 with 3 fields:sys:x:3:3:sys:/dev:/usr/sbin/n l gin
    複製代碼
  7. FILENAME

    FILENAME 是當前正在處理的文件的名字

    $ awk -F ":" '{print FILENAME}' /etc/passwd people.txt
    /etc/passwd
    /etc/passwd
    /etc/passwd
    /etc/passwd
    people.txt
    people.txt
    people.txt
    people.txt
    people.txt
    people.txt
    people.txt
    people.txt
    people.txt
    people.txt
    people.txt
    複製代碼

    這個值在開始處理記錄後纔有意義,所以在 BEGIN 塊中嘗試輸出 FILENAME 將獲得空值

  8. FNR

    前面的 NR 表示的項數在處理多個文件時會累加,而FNR 表示記錄位於當前文件的第幾項

    awk -F ":" '{print "record" FNR ":" $1,$2,$3}' people.txt /etc/passwd
    record1:P1  
    record2:male  
    record3:15  
    record4:  
    record5:p2  
    record6:female  
    record7:20  
    record8:  
    record9:p3  
    record10:male  
    record11:19  
    record1:root x 0
    record2:daemon x 1
    record3:bin x 2
    record4:sys x 3 
    複製代碼

內置函數

awk 提供了一些內置函數,便於文本和運算的處理,包括獲取字符串長度的 length(),獲取隨機數的 rand(),計算正餘弦的 sin()cos()

這些函數能夠在官方手冊中查詢。

記錄篩選

上面的全部的例子都對每一條記錄進行了操做,事實上還能夠用條件進行篩選。

正則判斷

使用正則表達式能夠對記錄進行模式匹配:

$ awk -F ':' '/root/ {print $1,$2,$3}' /etc/passwd 
root x 0
複製代碼

這裏篩選出了包含 root 的記錄。

條件判斷

結合 awk 的內置變量和函數也能夠進行篩選:

# 輸出第一個字段長度大於 2 且在 第 1 條記錄以後的記錄
$ awk -F ':' 'length($1)>3 && NR>1 {print $0}' /etc/passwd
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
複製代碼

if 語句

awk 也提供了 if 語句:

# 輸出第 3 個字段爲 0 的記錄
$ awk -F ':' '{if ($3==0)print $0}' /etc/passwd
root:x:0:0:root:/root:/usr/bin/zsh
複製代碼

awk 還有 for 語句,與 C 語言中形式相似:

$ awk -v ORS="," 'BEGIN{ for(i=1;i<5;i++) print i}'  
1,2,3,4,
複製代碼

字符與數字

awk 提供了對數學運算符和邏輯運算符的支持的支持,在 awk 中字符串與數字間還能夠直接進行強制類型轉換,+0 能夠強制轉換爲數字,與空格拼接能夠轉換爲字符串:

awk 'BEGIN{print "origin\tnumber\tstring"}{print $0,"\t",$0+0,"\t",$0 ""}' people.txt
origin  number  string
P1       0       P1
male     0       male
15       15      15
         0       
p2       0       p2
female   0       female
20       20      20
         0       
p3       0       p3
male     0       male
19       19      1
複製代碼

這裏使用了 $0 "" 的表示方式將原記錄與空格進行了拼接,但在某些狀況下會出現問題,考慮下面的語句:

$ awk 'BEGIN { print -12 " " -24 }'
-| -12-24
複製代碼

這裏想要在 -12 和 -24 中間加一個空格,但沒有獲得想要的結果,這是因爲數學運算符的優先級高於拼接操做,因此解析順序以下:

-12 (" " - 24)
⇒ -12 (0 - 24)
⇒ -12 (-24)
⇒ -12-24
複製代碼

要獲得正確的正確的結果須要使用括號結合:

$ awk 'BEGIN { print -12 " " (-24) }'
-| -12 -24
複製代碼
相關文章
相關標籤/搜索