AWK


AWK là một ngôn ngữ lập trình dành cho text. Khi làm việc với Linux ta dùng terminal rất là nhiều, chính vì vậy awk là công cụ rất tốt để làm việc với text.

Cấu trúc của awk như sau.

awk 'pattern {action}' file

pattern là điều kiện, nếu đúng sẽ thực hiện action, hành động này sẽ được áp vào file. Để dễ hiểu ta xem ví dụ sau.

[~]$ cat data.txt 
First Last Age  job  date 
Jerry Tran 30     itengineer 2021/11/12
Terry Huynh 48  chef  2020/12/01
Lisa Huynh 23    singer  2019/11/01
Tom Tran 24    developerengineer 2010/10/02

[~]$ 

Ta có file tên là data.txt. Ta có thể chỉ xuất ra tên dùng awk nhé.

[~]$ awk '{print $1}' data.txt
First
Jerry
Terry
Lisa
Tom



[~]$ 

$1 là cột đầu tiên trong file. Xuất ra 2 cột First và Last.

[~]$ awk '{print $1 $2}' data.txt
FirstLast
JerryTran
TerryHuynh
LisaHuynh
TomTran


[~]$ 

Ta thấy không có khoảng trắng. Ta Thêm khoảng trắng vào.

[~]$  awk '{print $1 " " $2}' data.txt 
First Last
Jerry Tran
Terry Huynh
Lisa Huynh
Tom Tran
 
 
[~]$

Hoặc dùng 'printf'

[~]$  awk '{ printf("%s %s\n", $1 , $2) }' data.txt
First Last
Jerry Tran
Terry Huynh
Lisa Huynh
Tom Tran
 
[~]$ 

Còn muốn in ra toàn file.

[~]$ awk '{print $0}' data.txt
First Last Age  job  date 
Jerry Tran 30     itengineer 2021/11/12
Terry Huynh 48  chef  2020/12/01
Lisa Huynh 23    singer  2019/11/01
Tom Tran 24    developerengineer 2010/10/02


[~]$ 

Hoặc dùng cách sau.

[~]$ awk '{print }' data.txt 
First Last Age  job  date 
Jerry Tran 30     itengineer 2021/11/12
Terry Huynh 48  chef  2020/12/01
Lisa Huynh 23    singer  2019/11/01
Tom Tran 24    developerengineer 2010/10/02


[~]$ 

Nếu muốn tìm tuổi của Jerry. Ta dùng pattern.

[~]$ awk '/Jerry/ {print $3}' data.txt 
30
[~]$ 

Ta dùng pattern /Jerry/ để tìm ra dòng có tên là Jerry và in ra cột 3. Hoặc ta dùng điều kiện như sau.

[~]$ awk '$1 == "Jerry" {print $3}' data.txt
30
[~]$ 

Tìm trong cột 1 có ai tên là 'Jerry' và in ra cột 3 là tuổi.Kết quả cũng giống như trên. Nếu muốn chỉ in ra những người trên 30 tuổi.

[~]$ cat data.txt 
First Last Age  job  date 
Jerry Tran 30     itengineer 2021/11/12
Terry Huynh 48  chef  2020/12/01
Lisa Huynh 23    singer  2019/11/01
Tom Tran 24    developerengineer 2010/10/02


[~]$ awk '$3 > 30 {print $0}' data.txt 
First Last Age  job  date 
Terry Huynh 48  chef  2020/12/01
[~]$ 

Nếu muốn tìm ra ai có nghề nghiệp là 'singer'.

[~]$ awk '$4 ~ /singer/ {print}' data.txt 
Lisa Huynh 23    singer  2019/11/01
[~]$ 

'$4 ~/singer/' có nghĩa là xem cột có ai match với singer hay không. Nếu match thì in ra.

Nếu muốn tìm những người không là singer.

[~]$ cat data.txt 
First Last Age  job  date 
Jerry Tran 30     itengineer 2021/11/12
Terry Huynh 48  chef  2020/12/01
Lisa Huynh 23    singer  2019/11/01
Tom Tran 24    developerengineer 2010/10/02


[~]$ awk '$4 !~ /singer/ {print}' data.txt
First Last Age  job  date 
Jerry Tran 30     itengineer 2021/11/12
Terry Huynh 48  chef  2020/12/01
Tom Tran 24    developerengineer 2010/10/02


[~]$ 

Tìm người nghề nghiệp bắt đầu là 'it'.

[~]$ awk '$4 ~ /^it/ {print}' data.txt
Jerry Tran 30     itengineer 2021/11/12
[~]$ 

Tìm người nghề nghiệp kết thúc bằng 'f'.

[~]$ awk '$4 ~ /f$/ {print}' data.txt
Terry Huynh 48  chef  2020/12/01
[~]$ 

Hoặc tìm người nghề nghiệp là 'chef'.

[~]$ awk '$4 ~ /chef/ {print}' data.txt 
Terry Huynh 48  chef  2020/12/01
[~]$ awk '$4 ~ /^chef$/ {print}' data.txt
Terry Huynh 48  chef  2020/12/01
[~]$ 

Như ta đã thấy mặc định awk xác định các cột dựa vào khoảng trắng giữa các cột. Nhưng một số file như /etc/passwd thì lại dùng ':' để phân chia các cột. Ta cũng có thể đổi từ khoảng trắng sang ':' cho awk.

[~]$ awk '{print }' /etc/passwd 
root:x:0:0:root:/root:/usr/bin/zsh
bin:x:1:1::/:/usr/bin/nologin
daemon:x:2:2::/:/usr/bin/nologin
mail:x:8:12::/var/spool/mail:/usr/bin/nologin
...
[~]$

Ta in ra cột cuối cùng dùng $NF.

[~]$ awk -F ':' '{print $NF}' /etc/passwd
/usr/bin/zsh
/usr/bin/nologin
/usr/bin/nologin
/usr/bin/nologin
...
[~]$ 

$NF là giá trị cột cuối cùng. Còn nếu muốn thứ tự cột cuối ta dùng NF.

[~]$ cat data.txt 
First Last Age  job  date 
Jerry Tran 30     itengineer 2021/11/12
Terry Huynh 48  chef  2020/12/01
Lisa Huynh 23    singer  2019/11/01
Tom Tran 24    developerengineer 2010/10/02
[~]$ awk  '{print NF}' data.txt
5
5
5
5
5
[~]$ 

In ra thứ tự cột là cột 5. Còn in ra giá trị cột 5 ta dùng $NF.

[~]$ awk  '{print $NF}' data.txt
date
2021/11/12
2020/12/01
2019/11/01
2010/10/02
[~]$ 

Còn in ra số dòng ta dùng NR.

[~]$ awk  '{printf "%s %s\n" ,NR, $0}' data.txt
1 First Last Age  job  date 
2 Jerry Tran 30     itengineer 2021/11/12
3 Terry Huynh 48  chef  2020/12/01
4 Lisa Huynh 23    singer  2019/11/01
5 Tom Tran 24    developerengineer 2010/10/02
[~]$ 

Ngoài ra ta củng có thể tạo file.awk và áp vào file.

awk -f max.awk data.txt
[~]$ cat max.awk 
BEGIN {
	max= 0
}


$0 !~ /Age/ {
	if ($3 > max) {
		max = $3
	}
}

END {
	printf("Max age is %s\n", max)
}
[~]$

Ta BEGIN để định nghĩa biến max = 0, dùng END để in ra biến max. '$0 !~ /Age/' sẽ loai bỏ dòng đầu First Last Age Job date, và so sánh cột 3, có dòng nào tuổi > max thì gán làm max.

[~]$ awk -f max.awk data.txt   
Max age is 48
[~]$ 

Ngoài áp lên file data.txt. Ta cũng có thể dùng awk với các câu lệnh. Ví dụ khi ta muốn xem ip trên máy tính ta dùng 'ip address'

[~]$ ip address                   
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
4: enp0s20u2u2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0e:c6:c0:e1:2f brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.2/24 brd 192.168.1.255 scope global dynamic noprefixroute enp0s20u2u2
       valid_lft 65590sec preferred_lft 54790sec
    inet6 2001:ee0:56b2:3c30:20e:c6ff:fec0:e12f/64 scope global dynamic mngtmpaddr 
       valid_lft 751sec preferred_lft 751sec
    inet6 fe80::20e:c6ff:fec0:e12f/64 scope link 
       valid_lft forever preferred_lft forever

Nhưng ta chỉ muốn xem ip thôi ta xem cột 2.

[~]$ ip address | awk '{print $2}'
lo:
00:00:00:00:00:00
127.0.0.1/8
forever
::1/128
forever
enp0s20u2u2:
00:0e:c6:c0:e1:2f
192.168.1.2/24
65586sec
2001:ee0:56b2:3c30:20e:c6ff:fec0:e12f/64
746sec
fe80::20e:c6ff:fec0:e12f/64
forever
[~]$ 

Nhưng như vậy awk sẽ in ra tất cả cột 2. Ta chỉ muốn ip là 192.168.1.2/24.

[~]$ ip address | awk '$NF ~ /^enp0s/{print $2}'
192.168.1.2/24
[~]$ 

$NF là giá trị cột cuối, so sánh card mạng tên bắt đầu là enp0s thì sẽ in ra cột 2. Còn muốn lấy ra MAC.

[~]$ ip address | awk '$1 ~ /link\/ether/ {print $2}'
00:0e:c6:c0:e1:2f
[~]$

Ví dụ như muốn kiểm tra ip và mac của gateway ta dùng.

[~]$ arp -a
_gateway (192.168.1.1) at a4:f4:c2:17:9b:d9 [ether] on enp0s20u2u2
[~]$ 
[~]$ arp -a | awk '{printf("Your gateway is %s and mac is %s\n",  $2, $4) }' 
Your gateway is (192.168.1.1) and mac is a4:f4:c2:17:9b:d9
[~]$ 

Để xem máy tính có những kết nối nào ta dùng 'netstat'. Nếu ta muốn xem các kết nối tcp và hiện ra chương trình nào đang chạy ta dùng câu lệnh sau.

[~]$ sudo netstat -natp                 

[sudo] password for dt: 
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.1:631           0.0.0.0:*               LISTEN      948/cupsd           
tcp        0      0 192.168.1.2:34700       142.251.8.94:443        ESTABLISHED 1620/brave --type=u 
tcp        0      0 192.168.1.2:48302       185.199.108.153:443     ESTABLISHED 1620/brave --type=u 
tcp6       0      0 :::8080                 :::*                    LISTEN      15634/node          
tcp6       0      0 ::1:631                 :::*                    LISTEN      948/cupsd           
tcp6       0      0 :::3001                 :::*                    LISTEN      15634/node          
tcp6       0      0 ::1:8080                ::1:46888               FIN_WAIT2   -                   
tcp6       0      0 ::1:8080                ::1:46890               FIN_WAIT2   -                   
tcp6       0      0 ::1:46896               ::1:8080                ESTABLISHED 1620/brave --type=u 
tcp6       0      0 ::1:8080                ::1:46898               FIN_WAIT2   -                   
tcp6       1      0 ::1:46898               ::1:8080                CLOSE_WAIT  1620/brave --type=u 
tcp6       1      0 ::1:46890               ::1:8080                CLOSE_WAIT  1620/brave --type=u 
tcp6       1      0 ::1:46888               ::1:8080                CLOSE_WAIT  1620/brave --type=u 
tcp6       0      0 ::1:8080                ::1:46896               ESTABLISHED 15634/node          
[~]$

Nhưng ta muốn filter bớt chỉ những connect nào là 'ESTABLISHED'

[~]$ sudo netstat -natp | awk '$6 ~/ESTABLISHED/ {print $0}'

tcp        0      0 192.168.1.2:48302       185.199.108.153:443     ESTABLISHED 1620/brave --type=u 
tcp6       0      0 ::1:46896               ::1:8080                ESTABLISHED 1620/brave --type=u 
tcp6       0      0 ::1:8080                ::1:46896               ESTABLISHED 15634/node          
[~]$ 

Nếu chỉ muốn xem source ip là 192.168.1.2.

[~]$ sudo netstat -natp | awk ' $4 ~/192.168.1.2/ && $6 ~/ESTABLISHED/ {print $0}'
443
tcp        0      0 192.168.1.2:41648       142.250.204.67:443      ESTABLISHED 1620/brave --type=u 
tcp        0      0 192.168.1.2:48302       185.199.108.153:443     ESTABLISHED 1620/brave --type=u 
[~]$ 

Như vậy là ta đã thấy sức mạnh của awk rồi nhé. ^_^