Shell 編程之文本處理
前言
在日常工作學習中,不免經常要對文本文件(例如日誌文件)進行處理工作,包括但不限於分割、查找、替換、刪除等操作,Shell
中有沒有相應的命令供我們使用呢?那麼本篇文章,我們就一起來學習下吧!
cut
grep
命令可以查找文件中符合條件的行,cut
命令則可以根據分隔符,提取行中的列,默認分隔符爲 TAB
。
# cut [選項] 文件名
選項
-f 列號: 提取第幾列
-d 分隔符: 按照指定分隔符分割列
# cut默認分隔符是TAB
測試一下這個命令就容易理解了。我們首先新建一個文件,然後使用 TAB
分隔,然後使用 cut
命令提取列。
示例一
[root@VM-0-5-centos ~]# vim student.txt
ID NAME Gender MARK
1 zs M 98
2 ls F 99
# 1. 提取第二列
[root@VM-0-5-centos ~]# cut -f 2 student.txt
NAME
zs
ls
# 2. 提取第二列和第四列
[root@VM-0-5-centos ~]# cut -f 2,4 student.txt
NAME MARK
zs 98
ls 99
# 3. 測試使用分隔符提取列
# 3.1 這是我們需要處理的文本
[root@VM-0-5-centos ~]# cat /etc/passwd | grep /bin/bash | grep -v root
lifelmy:x:1000:1000::/home/lifelmy:/bin/bash
user1:x:1001:1001::/home/user1:/bin/bash
user3:x:1002:1003::/home/user3:/bin/bash
# 3.2 提取第一列
[root@VM-0-5-centos ~]# cat /etc/passwd | grep /bin/bash | grep -v root | cut -f 1 -d :
lifelmy
user1
user3
從上面的例子我們可以看出,cut
命令可以提取行中的列,但是 cut
命令有一個缺點,就是分隔符的長度必須一致。如果列與列之間,有的是一個空格符,有的是兩個空格符的話,cut
命令就不能正確劃分列了。
示例二
[root@VM-0-5-centos ~]# df -h
文件系統 容量 已用 可用 已用% 掛載點
devtmpfs 909M 0 909M 0% /dev
tmpfs 919M 24K 919M 1% /dev/shm
tmpfs 919M 540K 919M 1% /run
tmpfs 919M 0 919M 0% /sys/fs/cgroup
/dev/vda1 50G 5.6G 42G 12% /
tmpfs 184M 0 184M 0% /run/user/0
# 使用 空格 作爲分隔符,查出第一列
[root@VM-0-5-centos ~]# df -h | cut -f 1 -d " "
文件系統
devtmpfs
tmpfs
tmpfs
tmpfs
/dev/vda1
tmpfs
# 但是查第二列的時候,得到的都是空格,不是我們想要的"容量"那一列
[root@VM-0-5-centos ~]# df -h | cut -f 2 -d " "
awk
從上面的示例二可以看出,cut
還是有些缺點的,那麼這個問題有沒有其他命令可以解決呢,這就輪到我們的 awk
命令出場了。在詳細介紹 awk
命令前,我們先來看一下標準輸出命令。
在 awk 命令的輸出中支持 print
和 printf
命令
-
print: 在每個輸出之後自動加入一個換行符(Linux 默認沒有 print 命令)
-
printf: 標準格式輸出命令,並不會自動加入換行符,如果需要換行,需要手動加入換行符
printf
printf "輸出類型 輸出格式" 輸出內容
[輸出類型]:
%ns: 輸出字符串,n表示輸出幾個字符
%ni: 輸出整數,n表示輸出幾個數字
%m.nf: 輸出浮點數,m、n分別表示輸出的位數以及其中的小數位數. %8.2f表示共輸出8位數,其中2位是小數,6位整數
[輸出格式]:
\a: 輸出警告聲音
\b: 輸出退格鍵
\f: 清除屏幕
\n: 換行
\r: 回車
\t: 水平輸出製表符
\v: 垂直輸出製表符
示例
# %-5s 格式爲左對齊且寬度爲5的字符串代替('-'表示左對齊),不使用則默認右對齊。
# %-4.2f 格式爲左對齊寬度爲4,保留兩位小數。
[root@VM-0-5-centos ~]# printf "%-5s %-10s %-4s\n" NO Name Mark
NO Name Mark
[root@VM-0-5-centos ~]# printf "%-5s %-10s %-4.2f\n" 01 Tom 90.3456
01 Tom 90.35
[root@VM-0-5-centos ~]# printf "%-5s %-10s %-4.2f\n" 02 Jack 89.2345
02 Jack 89.23
[root@VM-0-5-centos ~]# printf "%-5s %-10s %-4.2f\n" 03 Jeff 98.4323
03 Jeff 98.43
[root@VM-0-5-centos ~]# printf '%d %d %d\n' 12 34 56
12 34 56
awk
是一種編程語言,用於在 linux/unix 下對文本和數據進行處理。數據可以來自標準輸入 (stdin)、一個或多個文件,或其它命令的輸出。它支持用戶自定義函數和動態正則表達式等先進功能,是 linux/unix 下的一個強大編程工具。它在命令行中使用,但更多是作爲腳本來使用。awk 有很多內建的功能,比如數組、函數等,這是它和 C 語言的相同之處,靈活性是 awk 最大的優勢。
awk 'BEGIN{動作} 條件1{動作1} 條件2{動作2} END{動作}' file
條件(Pattern):
一般使用關係表達式作爲條件
x>10
x<=10
動作(Action):
格式化輸出
流程控制語句
一個 awk 腳本由三部分組成:BEGIN 語句塊、能夠使用條件匹配的通用語句塊、END 語句塊 3 部分組成,這三個部分是可選的。任意一個部分都可以不出現在腳本中。
BEGIN 條件,會在讀取文件第一行之前執行相應的動作;END 條件,讀取完文件後執行。
測試
# 1. 在處理第一行文件時先執行BEGIN對應的動作,然後無條件對每一行使用默認的空格分隔後取出第5列,最後執行 END 對應的動作
[root@VM-0-5-centos ~]# df -h | awk 'BEGIN{printf "this is begin \n"} {printf $5 "\n"} END{printf "this is end \n"}'
this is begin
已用%
0%
1%
1%
0%
12%
0%
this is end
# 2. 在處理第一行文件前賦值 i=0,然後每處理一行就將i加一,最後輸出i的值
[root@VM-0-5-centos ~]# df -h | awk 'BEGIN{ i=0 } { i++ } END{ print i }'
7
# 3. 將1、2兩個例子結合起來
[root@VM-0-5-centos ~]# df -h | awk 'BEGIN{i=0 ;printf "start \n"} {printf $5 "\t"} {i++; printf i "\n"} END{printf "end\n"}'
start
已用% 1
0% 2
1% 3
1% 4
0% 5
12% 6
0% 7
end
由於 awk
命令的默認分隔符是空格 (沒有數量限制),因此對於分隔符不是空格的文件來說,BEGIN
的主要作用就是在處理文件前設置分隔符,其中FS
用來設置分隔符。
# 要處理的數據
[root@VM-0-5-centos ~]# cat /etc/passwd | grep syslog
syslog:x:996:994::/home/syslog:/bin/false
# 使用 BEGIN 設置分隔符爲":",然後輸出第三列數據
[root@VM-0-5-centos ~]# cat /etc/passwd | grep syslog | awk 'BEGIN {FS=":"} {printf $3 "\n"}'
996
# 使用關係運算符條件
[root@VM-0-5-centos ~]# cat /etc/passwd | awk 'BEGIN {FS=":"} $3>995{printf $3 "\n"}'
999
998
997
996
1000
1001
1002
下面給出一個具體問題,思考下你會怎麼處理。上面我們已經使用過了 df -h
命令,該命令是用於查看 Linux 的磁盤佔用情況,我們想要設置個定時任務,每天去檢查某個磁盤的佔用情況,如果磁盤佔用比例到了 80%,就發送郵件報警。這裏我們只處理第一步,就是判斷磁盤佔用情況是否超出了設置的閾值。
腳本文件
#!/bin/bash
read -p 'please input rate: ' r
rate=$(df -h | grep 'vda1'| awk '{printf $5}' | cut -d '%' -f 1)
if [ $rate -gt $r ]
then
echo 'alarm'
fi
測試
[root@VM-0-5-centos ~]# chmod 755 test.sh
[root@VM-0-5-centos ~]# ./test.sh
please input rate: 20
[root@VM-0-5-centos ~]# ./test.sh
please input rate: 10
alarm
sed
sed
是一種幾乎包括所有 unix 平臺(包括 Linux)的輕量級流編輯器,它是文本處理中非常重要的工具,能夠完美的配合正則表達式使用,功能不同凡響。
處理時,把當前處理的行存儲在臨時緩衝區中,稱爲 “模式空間”(pattern space),接着用 sed
命令處理緩衝區中的內容,處理完成後,把緩衝區的內容送往屏幕。接着處理下一行,這樣不斷重複,直到文件末尾。文件內容並沒有改變,除非你使用重定向存儲輸出。sed
主要用來自動編輯一個或多個文件;簡化對文件的反覆操作;編寫轉換程序等。
# sed [選項] '動作' 文件名
選項:
-n 一般sed命令會把所有數據都輸出到屏幕,如果加入此選擇,則只會把經過sed命令處理的行輸出到屏幕
-e 允許對輸入數據應用多條sed命令編輯
-i 用sed的修改結果直接修改讀取數據的文件,而不是由屏幕輸出
動作:
a: 追加,在當前行後添加一行或多行。添加多行時,除最後一行外,每行末尾需要用'\' 代表數據未完結
c: 行替換,用'c' 後面的字符串替換原數據行,替換多行時,除最後一行外,每行末尾用'\' 表示數據未完結
i: 插入,在當前行前插入一行或多行。插入多行時,除最後一行外,每行末尾用'\' 表示數據未完結
d: 刪除,刪除指定的行
p: 打印,輸出指定的行
s: 字符串替換,用一個字符串替換另一個字符串。格式爲 "第幾行s/舊字串/新字串/g",使用 '/g'表示替換該行中的所有匹配 (與vim類似)
測試
# 要處理的文件
[root@VM-0-5-centos ~]# cat student.txt
ID NAME Gender MARK
1 zs M 98
2 ls F 99
# 打印輸出第2行,默認不僅會輸出指定的行,還會輸出原有文件中的所有行
[root@VM-0-5-centos ~]# sed '2p' student.txt
ID NAME Gender MARK
1 zs M 98
1 zs M 98
2 ls F 99
# -n,只輸出sed處理的數據
[root@VM-0-5-centos ~]# sed -n '2p' student.txt
1 zs M 98
# 刪除第2行的數據,但不修改原文件 (在緩衝區修改的)
[root@VM-0-5-centos ~]# sed '2d' student.txt
ID NAME Gender MARK
2 ls F 99
[root@VM-0-5-centos ~]# cat student.txt
ID NAME Gender MARK
1 zs M 98
2 ls F 99
# 在第3行後追加一條數據,不修改原數據 (在緩衝區修改的)
[root@VM-0-5-centos ~]# sed '3a hello world' student.txt
ID NAME Gender MARK
1 zs M 98
2 ls F 99
hello world
# 在第3行前插入一條數據,不修改原數據 (在緩衝區修改的)
[root@VM-0-5-centos ~]# sed '3i hello world' student.txt
ID NAME Gender MARK
1 zs M 98
hello world
2 ls F 99
# 替換第3行的數據,不修改原數據 (在緩衝區修改的)
[root@VM-0-5-centos ~]# sed '3c hello world' student.txt
ID NAME Gender MARK
1 zs M 98
hello world
# 替換字符串字段,不修改原數據 (在緩衝區修改的)
[root@VM-0-5-centos ~]# sed '2s/98/100/g' student.txt
ID NAME Gender MARK
1 zs M 100
2 ls F 99
# 字符串替換,直接修改文件
[root@VM-0-5-centos ~]# sed -i '2s/98/100/g' student.txt
[root@VM-0-5-centos ~]# cat student.txt
ID NAME Gender MARK
1 zs M 100
2 ls F 99
# 替換多處
[root@VM-0-5-centos ~]# sed -i '2s/M/F/; 2s/zs/ww/' student.txt
[root@VM-0-5-centos ~]# cat student.txt
ID NAME Gender MARK
1 ww F 100
2 ls F 99
總結
本篇文章一共學習了三個命令:
-
cut:用於提取一行文本中的列,默認分隔符是
TAB
, 同時也可以指定分隔符。如果分隔符是空格,需要保證每列之間的空格數相同; -
awk:一種編程語言,功能強大,支持數組、函數等,類似 C 語言;
-
sed:功能強大的流式文本編輯器,可以很方便的處理文件中的每一行數據。
PS:本文只是簡單的介紹了命令的常用部分,如果想要更加深入瞭解,可以去這裏 https://wangchujiang.com/linux-command/ 學習。
更多
個人博客: https://lifelmy.github.io/
微信公衆號:漫漫 Coding 路
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/g7CnIAtlAMimn9V_J2lFng