Shell文本處理三劍客之awk

awk 是一個文本處理工具,一般用於處理數據並生成結果報告。其命名源於三位創始人姓氏首字母:Alfred Aho、Peter Weinberger、Brian Kernighan。

語法:docker

  • awk [options] 'BEGIN{} pattern {commands} END{}' file
  • stdout | awk [options] 'BEGIN{} pattern {commands} END{}'

說明:shell

  • options 選項
  • BEGIN{} 正式處理數據以前執行
  • pattern 匹配模式
  • {commands;...} 處理命令,可能多行
  • END{} 處理完全部匹配數據後執行

<!-- more -->數組

內置變量

變量名 說明
$0 整行內容
$1-$n 當前行的第 1 - n 個字段(列)
NF Number Field,當前行字段個數(多少列)
NR Number Row,當前行的行號,從 1 開始計數
FNR File Number Row,多文件處理時,每一個文件行號單獨計數,都是從 0 開始
FS Field Separator,輸入字段分隔符(默認空格或 tab 鍵)
RS Row Separator,輸入行分隔符(默認回車換行)
OFS Output Field Separator,輸出字段分隔符(默認空格)
ORS Output Row Separator,輸出行分隔符(默認回車換行)
FILENAME 當前輸入的文件名字
ARGC 命令行參數個數
ARGV 命令行參數數組

示例:bash

# 以 : 分隔,輸出第 1 列
➜  awk 'BEGIN{FS=":"} {print $1}' /etc/passwd

# 以 -- 分隔成行,以 : 分隔成列,輸出第 一、2 列
➜  awk 'BEGIN{FS=":";RS="--"} {print $1,$2}' /etc/passwd

# 以 : 分隔列,輸出最後一列,由於 NF 變量是總列數
➜  awk 'BEGIN{FS=":"} {print $NF}' /etc/passwd

格式化輸出(printf)

格式符 說明 修飾符 說明
%s 字符串 - 左對齊
%d 十進制 + 右對齊
%f 浮點數 # 八進制前面加 0,十六進制前面加 0x
%x 十六進制
%o 八進制
%e 科學計數法
%c 單個字符的 ASCII 碼

示例:函數

# printf "%+20s %-20s\n",$1,$7
#   - 左對齊;+ 右對齊
#   20 列寬,不足則補空
#   s 打印字符串
#   .3f 打印保留 3 位數的浮點數
➜  awk 'BEGIN{FS=":";OFS="-"}{printf "%+20s %20.3f %-20s\n",$1,$3,$7}' /etc/passwd
                root                0.000 /bin/bash
                 bin                1.000 /sbin/nologin
              daemon                2.000 /sbin/nologin
                 adm                3.000 /sbin/nologin
                  lp                4.000 /sbin/nologin

模式匹配(pattern)

  • RegExp/patern/
  • 關係運算<><=>===!=~ 正則匹配、!~ 非正則匹配、&& 與、|| 或、!

示例:工具

# 打印以 root 開頭的行
➜  awk 'BEGIN{FS=":"} /^root/ {print $0}' /etc/passwd
root:x:0:0:root:/root:/bin/bash

# 打印第 3 列大於 1000 的行
➜  awk 'BEGIN{FS=":"} $3>1000 {print $0}' /etc/passwd
nfsnobody:x:65534:65534:Anonymous NFS User:/var/lib/nfs:/sbin/nologin

# 打印第 7 列是 /sbin/nologin 的行
➜  awk 'BEGIN{FS=":"} $7=="/sbin/nologin" {print $0}' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin

# 打印第 7 列以 nologin 結尾的行
➜  awk 'BEGIN{FS=":"} $7~/.*nologin$/ {print $0}' /etc/passwd
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin

# 打印第 3 列大於 500,而且第 7 列以 nologin 結尾的行
➜  awk 'BEGIN{FS=":"} $3>500 && $7~/.*nologin$/ {print $0}' /etc/passwd
chrony:x:997:995::/var/lib/chrony:/sbin/nologin
dockerroot:x:996:993:Docker User:/var/lib/docker:/sbin/nologin

計算表達式

示例:this

# 數學計算
➜  awk 'BEGIN{x=10;y=2; print x+y}'
12
➜  awk 'BEGIN{x=10;y=2; print x*y}'
20
➜  awk 'BEGIN{x=10;y=2; print x^y}'
100
➜  awk 'BEGIN{x=10;y=2; print x**y}'
100
➜  awk 'BEGIN{x=10;y=x++; print x,y}'
11 10
➜  awk 'BEGIN{x=10;y=++x; print x,y}'
11 11


# 打印空行行號、統計空行數量
➜  awk 'BEGIN{idx=0;} /^$/ {idx++; print NR} END{print idx;}' /etc/passwd

流程控制語句

語法:命令行

# 條件判斷
if(condition1) {
    # do something
} else if(condition2) {
    # do something
} else {
    # do something
}

# 循環
while(condition) {
    #do something
}

do
    # do something
while(condition)

for(i=0;i<10;i++) {
    # do something
}

示例:code

# 若是第 3 列小於 10 而且第 7 列是 /sbin/nologin 的行打印 this is if
# 若是第 3 列大於 500 的打印 this is else if
# 不然打印 this is else
➜  awk 'BEGIN{FS=":"} { if($3<10 && $7="/sbin/nologin") {print "this is if"} else if($3>500) {print "this is else if"} else {print "this is else"}}' /etc/passwd
this is if
this is if
this is else
this is else if
this is else

# 計算 1-10 相加的結果
# 注意:變量不須要提早聲明
➜  awk 'BEGIN{ while(i<10) { sum+=i; i++}; print sum}'
45
➜  awk 'BEGIN{do { sum+=i; i++; } while(i<10); print sum}'
45
➜  awk 'BEGIN{ for(i=0;i<10;i++) { sum+=i; }; print sum}'
45

字符串函數

函數名 說明 返回值
length(str) 計算字符串長度 整數長度值
index(str,sub_str) 在 str 中查找 sub_str 的位置 位置索引,從 1 計數
tolower(str) 轉小寫 轉換後的小寫字符串
toupper(str) 轉大些 轉換後的大寫字符串
substr(str,start,length) 從 str 第 start 個字符開始,截取 length 位 截取後到子串
split(str,arr,fs) 按 fs 拆分字符串,結果保存到 arr 拆分後子串的個數
match(str,reg) 在 str 中按 reg 查找,返回位置 索引位置
sub(reg,new_sub_str,str) 在 str 中搜索符合 reg 的子串,將其替換爲 new_sub_str,只替換第一個 替換的個數
gsub(reg,new_sub_str,str) 相似 sub,替換全部 替換的個數

示例:索引

# sub(/oo/,"11",$1) 返回替換的個數;後面的 $1 爲替換後的值
➜  awk 'BEGIN{FS=":"} { print length($1),toupper($1),substr($1,0,2),sub(/oo/,"11",$1),$1}' /etc/passwd
4 ROOT ro 1 r11t
3 BIN bi 0 bin
6 DAEMON da 0 daemon
4 SYNC sy 0 sync

# 數組下標從 1 開始
➜  awk 'BEGIN{str="Shell;Python;C;C++;Java;PHP"; split(str,arr,";"); print arr[2]}'
Python
➜  awk 'BEGIN{str="Shell;Python;C;C++;Java;PHP"; split(str,arr,";"); for(i in arr) { print arr[i]; }}'
C++
Java
PHP
Shell
Python
C

經常使用選項(options)

  • -v 參數傳遞
  • -f 指定腳本文件
  • -v 指定分隔符
  • -V 查看 awk 版本

示例:

# 引入外部變量
➜  var1=10
➜  var2="hello awk"
➜  awk -v var1="$var1" -v var2="$var2" 'BEGIN{print var1,var2}'
10 hello awk

# 把全部操做抽離到一個獨立文件
# 建議:複雜操做優先使用這種方式,更易於程序理解和管理
➜  touch script.awk
BEGIN{
    FS=":"
}

{
    if($3<10 && $7="/sbin/nologin") {
        print "this is if"
    } else if($3>500) {
        print "this is else if"
    } else {
        print "this is else"
    }
}

➜  awk -f script.awk /etc/passwd

# -F: 至關於 BEGIN{FS=":"}
$ awk -F: '{print $1}' pwd
root
bin
daemon

數組

shell 中的數組操做以下:

操做 示例 輸出
定義一個數組 arr=("Python" "PHP" "Java" "Go" "Rust")
某個數組元素(下標從 0 開始) echo ${arr[2]} Java
數組元素個數 echo ${#arr[@]} 5
某個元素的長度 echo ${#arr[0]} 6
修改元素值 arr[2]="JAVA"
刪除數組元素 unset arr[1]
打印全部數組元素) echo ${arr[@]} Python JAVA Go Rust
分片訪問 echo ${arr[@]:0:2} Python JAVA
數組元素替換(找到的第一個) echo ${arr[@]/A/a} Python JaVA Go Rust
數組元素替換(全部) echo ${arr[@]//A/a} Python JaVa Go Rust
數組遍歷 for a in ${arr[*]}; do echo $a; done
而 awk 中數組的使用略有不一樣,它使用 關聯數組提供數組功能,即數組的索引能夠是 數字任意字符串

語法示例:

# 定義
# 語法:array_name[index]=value
➜  awk 'BEGIN{arr[0]=0; arr["second"]="2"; print arr[0],arr["second"];}'
0 2

# 數組元素參與計算
➜  awk 'BEGIN{arr[0]=0; arr["second"]="2"; print arr[0]+3,arr["second"];}'
3 2

# 刪除數組元素
# 語法:delete array_name[index]
➜  awk 'BEGIN{arr[0]=0;arr["second"]="2"; delete arr["second"]; print arr["second"];}'

# 遍歷數組
# 方式一:for ... in 是無序輸出
➜  awk 'BEGIN{str="Python Rust PHP Go"; arrLen=split(str,arr," "); for(i in arr){ print i,arr[i] }}'
4 Go
1 Python
2 Rust
3 PHP
# 方式二:for(i=1;i<=len;i++) { ... } 有序輸出
➜  awk 'BEGIN{str="Python Rust PHP Go"; arrLen=split(str,arr," "); for(i=1;i<=arrLen;i++){ print i,arr[i] }}'
1 Rust
2 Go
3 Python
4 PHP
相關文章
相關標籤/搜索