Shell編程之文本處理三劍客~乾貨滿滿!!

劍客一

grep

語法格式:

  • 第一種形式:grep [option] [pattern] [file1,file2...]
  • 第二種形式:command | grep [option] [pattern]

grep參數:

選項 含義
-v 不顯示匹配的行信息
-i 忽略大小寫
-n 顯示行號
-r 遞歸搜索
-E 支持擴展正則表達式
-F 不按正則表達式匹配,按照字符串字面意思匹配
-c 只顯示匹配行總數
-w 匹配整詞
-x 匹配整行
-l 只顯示文件名,不顯示內容
-s 不顯示錯誤信息

示例

# 準備file,內容如下
Python
Java
Python is good
Java is perfect
Php is the best lanuage

# 過濾含Python的行信息
grep Python file
->Python
->Python is good
grep -vi python file
->Java
->Java is perfect
->Php is the best lanuage

# 顯示行號
grep -n Python file
->1:Python
->3:Python is good

# 支持擴展正則表達式(也可以直接使用egrep代替)
grep 'python|Python' file
->
grep -E 'python|Python' file
->Python
->Python is good

劍客二

sed

      Sed是流編輯器(Stream Editor)。 流編輯器用於對輸入流(文件或來自管道的輸入)執行基本的文本轉換。儘管sed在某種程度上類似於允許進行粗略編輯(例如ed)的編輯器,但是Sed通過僅對輸入進行一次傳遞來工作,因此效率更高。由於sed能夠過濾管道中的文本,這使其與其他類型的編輯器特別不同。(java程序員應該很熟悉JDK8中的Stream)

語法格式:

stdout | sed [option] "pattern command"
sed [option] "pattern command" file

sed選項

選項 含義
-n 只打印模式匹配行(不會顯示原行信息)
-e 直接在命令行進行sed編輯,默認選項
-f 編輯動作保存在文件中,執行文件執行
-r 支持擴展正則表達式
-i 直接修改文件內容

示例

# 匹配Python
# 會發現沒匹配的行也被打印了,而匹配的行打印了兩遍
# 因爲sed默認就會把原行進行打印(p是打印command)
sed '/Python/p' file
->Python
->Python
->Java
->Python is good
->Python is good
->Java is perfect
->Php is the best lanuage

# 只打印匹配行,不打印原行
sed -n '/Python/p' file
->Python
->Python is good

# 多個pattern
sed -n -e '/Python/p' -e '/Java/p' file
->Python
->Java
->Python is good
->Java is perfect

# 擴展正則表達式
# 也許你會將上面兩個pattern合併在一起:/Python|Java/p.十分可惜,默認sed不支持擴展正則表達式,需要加上-r
sed -n -r '/Python|Java/p' file
->Python
->Java
->Python is good
->Java is perfect

# '批'處理
# 如果我們有很多動作需要操作,可以直接寫在文件裏,使用-f選項引入動作文件即可,新建multsed.sed(不一定要.sed),內容爲:
/Python/p
/Java/p

sed -n -f 'multsed.sed' file
->Python
->Java
->Python is good
->Java is perfect

sed的pattern

匹配模式 含義
10command 匹配到第10行
10,20command 匹配從第10行開始,到第20行結束
10,+5command 匹配從第10行開始,到第16行結束
/pattern1/command 匹配到pattern1行
/pattern1/,/pattern2/command 匹配到pattern1的行開始,到匹配到pattern2的行結束
10,/pattern1/command 匹配從第10行開始,到匹配到pattern1的行結束
/pattern1/,10command 匹配從匹配到pattern1的行開始,到第10行結束

示例

# 打印指定行號內容
sed -n '2p' file
->Java

# 打印起始行號到終止行號之間的信息(start不能小於1哦)
sed -n '1,3p' file
->Python
->Java
->Python is good

# 指定起始行號,打印後面n行
sed -n '1,+2p' file
->Python
->Java
->Python is good

# 打印匹配行
sed -n '/Python/p' file
->Python
->Python is good

# 打印兩個匹配之間的行
sed -n '/Python/,/good/p' file
->Python
->Java
->Python is good

# 從指定起始行開始到匹配行結束
sed -n '2,/good/p' file
->Java
->Python is good

# 從匹配行開始到指定的行結束(如果匹配到的行號小於指定的結束行那就只會顯示匹配行信息)
sed -n '/good/,4p' file
->Python is good
->Java is perfect

sed -n '/good/,2p' file
->Python is good

sed的命令

類別 命令 含義
增加 a 行後追加
i 行前追加
r 外部文件讀入,行後追加
w 匹配行寫入外部文件
刪除 d 刪除
修改 s/old/new 將行內第一個old替換爲new
s/old/new/g 將行內全部old替換爲new
s/old/new/2g 只替換第2個開始到剩下所有的old
s/old/new/ig 將行內old全部替換爲new,忽略大小寫
查詢 p 打印
行號 = 顯示行號

示例

# 在第1行後追加內容(如果需要修改文件則需要加上-i選項)
sed '1aScala' file
->Python
->Scala
->Java
->Python is good
->Java is perfect
->Php is the best lanuage

# 在匹配到的行前加上內容
sed '/Python/iHello Python' file
->Hello Python
->Python
->Java
->Hello Python
->Python is good
->Java is perfect
->Php is the best lanuage

# 讀入外部文件讀入(不寫匹配模式則在每一行後面都追加數據)
sed 'rmultsed.sed' file
->Python
->/Python/p
->/Java/p
->Java
->/Python/p
->/Java/p
->Python is good
->/Python/p
->/Java/p
->Java is perfect
->/Python/p
->/Java/p
->Php is the best lanuage
->/Python/p
->/Java/p

# 將匹配到的行信息寫入外部文件
sed '/Python/wpython.txt' file

# 將行內第一個匹配替換
sed -n 's/Python/Java/p' file
->Java Python python
->Java is good Python is good

# 將行內所有匹配替換
sed -n 's/Python/Java/gp' file
->Java Java python
->Java is good Java is good

# 將行內所有匹配替換(忽略大小寫)
sed -n 's/Python/Java/igp' file
->Java Java Java
->Java is good Java is good

# 替換第3個匹配後的所有old(忽略大小寫)
sed -n 's/Python/Java/3igp' file
->Python Python Java

# 顯示匹配行行號
sed -n '/Python/=' file
->1
->3

# 如果pattern使用的是變量的值那麼必須使用雙引號,除非你也給變量標上單引號
var1=Python
sed -n '/$var1/=' file
->
sed -n "/$var1/=" file
->1
->3

sed -n '/'$var1'/=' file
->1
->3

      如果pattern使用的是變量的值那麼必須使用雙引號,除非你也給變量標上單引號。

反向引用

      在sed中引用pattern匹配到的整個串這一行爲我們稱作反向引用,從下面這一例子來說明。

# 需求:將file按如下方式進行修改Python->Pythoner,Java->Javaer,Php->Phper
# 如果不知道反向引用我們就會多次使用修改命令進行修改
# 如果只是查找這幾個單詞倒十分簡單
sed -n -r '/Python|Java|Php/p' file
# 如果需要替換則有一個難點:下面的這個新字符我們是無法給一個統一的
sed -n -r 's/Python|Java|Php/新字符er/p' file

# 藉助反向引用
sed -n -r 's/Python|Java|Php/&er/p' file
->Pythoner Python python
->Javaer java
->Pythoner is good Python is good
->Javaer is perfect
->Phper is the best lanuage

# 除了使用&還可以使用\1,\2,\3等(記住括號),後者可以提取單獨的某一個group
sed -n -r 's/(Python|Java|Php)/\1er/p' file
->Pythoner Python python
->Javaer java
->Pythoner is good Python is good
->Javaer is perfect
->Phper is the best lanuage

echo -e "Python Java Php" | sed -e "s/\(Python\) \(Java\) \(Php\)/\1er \2s \3best/g"
->Pythoner Javas Phpbest

小試牛刀

      需求描述:處理一個類似MySQL配置文件my.cnf的文本,示例如下,編寫腳本實現以下功能:輸出文件有幾個段,並且針對每個段可以統計參數總個數。

my.cnf文件內容如下:

# this is read by the standalone daemon and embedded servers
[client]
port=3306
socket=/tmp/mysql.socket

#ThisSegmentForserver
[server]
innodb_buffer_pool_size=91750M
innodb_buffer_pool_instances=8
innodb_buffer_pool_load_at_startup=1
innodb_buffer_pool_dump_at_shutdown=1
innodb_data_file_path=ibdata1:1G:autoextend
innodb_flush_log_at_trx_commit=1
innodb_log_buffer_size=32M
innodb_log_file_size=2G
innodb_log_files_in_group=2
innodb_max_undo_log_size=4G
innodb_undo_directory=undolog
innodb_undo_tablespaces=95

#thisisonlyforthemysqldstandalonedaemon
[mysqld]
port=3306
socket=/tmp/mysql.sock
basedir=/usr/local/mysql
datadir=/data/mysql
pid-file=/data/mysql/mysql.pid
user=mysql
bind-address=0.0.0.0
sort_buffer_size=16M
join_buffer_size=16M
thread_cache_size=3000
interactive_timeout=600
wait_timeout=600

#ThisSegmentFormysqld_safe
[mysqld_safe]
log-error=/var/log/mariadb/mariadb.log
pid-file=/var/run/mariadb/mariadb.pid
max_connections=1000
open_files_limit=65535
thread_stack=512K
external-locking=FALSE
max_allowed_packet=32M

#thisisonlyforembeddedserver
[embedded]
gtid_mode=on
enforce_gtid_consistency=1
log_slave_updates
slave-rows-search-algorithms='INDEX_SCAN,HASH_SCAN'
binlog_format=row
binlog_checksum=1
relay_log_recovery=1
relay-log-purge=1


#usethisgroupforoptionsthatolderserversdon'tunderstand
[mysqld-5.5]
key_buffer_size=32M
read_buffer_size=8M
read_rnd_buffer_size=16M
bulk_insert_buffer_size=64M
myisam_sort_buffer_size=128M
myisam_max_sort_file_size=10G
myisam_repair_threads=1
lock_wait_timeout=3600
explicit_defaults_for_timestamp=1
innodb_file_per_table=1

#!/bin/bash

FILE_NAME="my.cnf"


function get_all_segment
{
	echo "`sed -n '/\[.*\]/p' $FILE_NAME | sed -r 's/(\[|\])//g'`"
}

# 統計每個段中配置項個數
function count_items_in_segment
{
	#段名
	segname=$1
	itemcount=`sed -n "/\[$segname\]/,/\[.*\]/p" $FILE_NAME | grep -v ^# | grep -v ^$ | grep -v "\[.*\]" | grep -c ""`
	echo $itemcount
}

sum=1
for segname in `get_all_segment`
do
	echo "配置項$sum$segname `count_items_in_segment $segname`"
	sum=`expr $sum + 1`
done

劍客三

awk

      AWK是一種處理文本文件的語言,是一個強大的文本分析工具。之所以叫 AWK 是因爲其取了三位創始人 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的 Family Name 的首字符。

語法格式:

stdout | awk 'BEGIN{}pattern{commands}END{}'
awk 'BEGIN{}pattern{commands}END{}' file_name

語法格式解釋:

語法格式 含義
BEGIN{} 正式處理數據之前執行
pattern 匹配模式
{commands} 處理命令,可能多行
END{} 處理完所有匹配數據後執行

awk內置變量

內置變量 含義
$0 整行內容
$1~$n 當前行的第1-n個字段
NF 當前行的字段個數,也就是有多少列
NR 當前行的行號,從1開始計數
FNR 多文件處理時,每個文件行號單獨計數都是從0開始
FS 輸入字段分隔符,不指定則默認以空格或tab鍵分隔
RS 輸入行分隔符,默認回車換行
OFS 輸出字段分隔符,默認爲空格
ORS 輸出行分隔符,默認回車換行
FILENAME 當前輸入的文件名字
ARGC 命令行參數個數
ARGV 命令行參數數組

示例

以下所有示例文件爲/etc/passwd,請將其拷貝一份使用


# 打印整行內容
awk '{print $0}' passwd

# 使用":"號作爲分隔符,輸出第一個字段
awk 'BEGIN{FS=":"}{print $1}' passwd
->root
->bin
->daemon
->adm
->lp
->sync
->shutdown
->...

# 輸出行號
awk '{print NR}' passwd

# 多個文件行號單獨計數
awk '{print FNR}' passwd file2

# 指定行分隔符
echo "hello-world-hello-linux-hello-java" | awk 'BEGIN{RS="-"}{print $0}'
->hello
->world
->hello
->linux
->hello
->java
->

# 指定輸出字段分隔符
awk 'BEGIN{FS=":";OFS=":"}{print NR,$1}' passwd
->1:root
->2:bin
->3:daemon
->4:adm
->5:lp
->...

printf格式化輸出

格式符 含義
%s 打印字符串
%d 打印十進制數
%f 打印一個浮點數
%x 打印十六進制數
%o 打印八進制數
%e 打印數字的科學計數法形式
%c 打印單個字符的ASCII碼
- 左對齊
+ 右對齊
# 顯示8進制在前面加0,顯示16進制在前面加0x

示例

# 以字符串格式打印第1個字段,以":"作爲分隔符
awk 'BEGIN{FS=":"}{printf "%s\t",$1}' passwd
->root	bin	daemon	adm	lp ...

# 以字符串格式打印第1個字段和對應行號,輸出格式爲"行號:字段內容"
awk 'BEGIN{FS=":"}{printf "%d:%s\n",NR,$1}' passwd
->1:root
->2:bin
->3:daemon
->4:adm
->5:lp
->...

# 左對齊
# 在不指定位數的情況下默認左對齊,指定位數後爲右對齊(必須指定位數)
awk 'BEGIN{FS=":"}{printf "%10d:%s\n",NR,$1}' passwd
->         1:root
->         2:bin
->         3:daemon
->         4:adm
->         5:lp
->...

awk 'BEGIN{FS=":"}{printf "%-10d:%s\n",NR,$1}' passwd
->1         :root
->2         :bin
->3         :daemon
->4         :adm
->5         :lp
->...

兩種匹配模式

模式 含義
正則 按正則表達式匹配
關係運算 按關係運算匹配

關係運算符:

  •  <  : 小於
  •  >  : 大於
  • <= : 小於等於
  • >= : 大於等於
  • == : 等於
  • !=  : 不等於
  •  ~  : 匹配正則表達式
  • !~  : 不匹配正則表達式

布爾運算符:

  •  ||   : 或
  • && : 與
  •   !   : 非
# 匹配文件行中含有root字符串的所有行
awk 'BEGIN{FS=":"}/root/{print $0}' passwd
->root:x:0:0:root:/root:/bin/bash
->operator:x:11:0:operator:/root:/sbin/nologin

# 匹配第3個字段小於50的所有行信息
awk 'BEGIN{FS=":"}$3<50{print $0}' passwd

# 匹配文件中包含mail或ftp的所有行信息
awk 'BEGIN{FS=":"}/mail/||/ftp/{print $0}' passwd
->mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
->ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin

# 匹配文件中第3個字段小於50並且第4個字段大於50的所有行信息
awk 'BEGIN{FS=":"}$3<50 && $4 > 50{print $0}' passwd
->games:x:12:100:games:/usr/games:/sbin/nologin

# 匹配文件中第3個字段小於50並且第7個字段匹配/bin/bash的所有行信息
awk 'BEGIN{FS=":"}$3<50 && $7 ~ /\/bin\/bash/{print $0}' passwd
->root:x:0:0:root:/root:/bin/bash

awk表達式

運算符 含義
+
-
*
/
%
^或** 乘方
++x/--x 在返回x變量之前,x變量加(減)1
x++/x-- 在返回x變量之後,x變量加(減)1
# 統計空白行數目
awk '/^$/{sum++}END{print sum}' /etc/services
->17

# 統計一下學生成績總分和平均分,報表形式展示
姓名    語文    數學    英語    物理
張三     80     60      85      90
李四     85     65      80      75
王五     70     60      85      90
李華     65     80      84      91
王八     90     90      95      90

awk 'BEGIN{printf "%-8s%-5s%-5s%-5s%-5s%-5s%-8s\n","姓名","語文","數學","英語","物理","總分","平均分"}{total=$2+$3+$4+$5;avg=total/4;printf "%-8s%-8d%-6d%-8d%-7d%-5d%0.2f\n",$1,$2,$3,$4,$5,total,avg}' stu.txt

姓名      語文   數學   英語   物理   總分   平均分     
張三      80      60    85      90     315  78.75
李四      85      65    80      75     305  76.25
王五      70      60    85      90     305  76.25
李華      65      80    84      91     320  80.00
王八      90      90    95      90     365  91.25

條件語句和循環語句

# 條件語句
if (條件表達式1){
    action1
}else if (條件表達式2){
    action2
}else{
    action3
}

# 以":"爲分隔符,如果第3個字段小於50,打印小於50,如果等於50則打印等於50,否則打印大於50
awk 'BEGIN{FS=":"}{if($3<50){print "小於50" }else if($3==50){ print "等於50"}else{print "大於50"}}' passwd

# 我們可以將命令保存在一個文件(eg:oper.awk,後綴不硬性要求哦!)中使用-f選項引入
awk -f oper.awk passwd

# 循環語句 while
while(條件表達式){
    action
}

# 循環語句 do while
do{
    action
}while(條件表達式)

# 循環語句 for
for(初始化計數器;測試計數器;變更計數器){
    action
}

# 計算1+2+3+...+100的和。
awk 'BEGIN{do{i++;sum+=i;}while(i<100)print sum}'
awk 'BEGIN{while(i<100){i++;sum+=i;}print sum}'
awk 'BEGIN{for(i=0;i<=100;i++){sum+=i;}print sum}'

字符串函數

函數名 含義 函數返回值
length(str) 計算字符串長度 整數長度值
index(str1,str2) 在str1中查找str2的位置 位置索引,從1計數
tolower(str) 轉換爲小寫 轉換後的小寫字符串
toupper(str) 轉換爲大寫 轉換後的大寫字符串
substr(str,m,n) 從str的m個字符開始,截取n位 截取後的子串
split(str,arr,fs) 按fs切割字符串,結果保存在arr中 切割後的子串個數
match(str,RE) 在str中按照RE查找,返回位置 返回索引位置
sub(RE,RepStr,str) 在str中搜索符合RE的子串,將其替換爲RepStr,只替換第一個 替換的個數
gsub(RE,RepStr,str) 在str中搜索符合RE的子串,將其替換爲RepStr,替換所有 替換的個數

示例

# 以:爲分隔符,返回文件每行中每個字段的長度
awk 'BEGIN{FS=":"}{for(i=1;i<=NF;i++){if(i==NF){printf "%d",length($i)}else{printf "%d:",length($i)}}print ""}' passwd

# 搜索字符串"I am a student"中student子串的位置
echo "I am a student" | awk '{print index($0,"student")}'
->8

awk常用選項

選項 含義
-v 參數傳遞
-f 指定腳本文件
-F 指定分隔符
-V 查看awk版本號
# 使用-v引用外部變量
num1=100
num2=200
awk -v var1="$num1" -v var2="$num2" 'BEGIN{print var1+var2}'

數組

      awk中數組(數組學習看這裏)使用小括號包圍起來,每一項之間使用空格分隔,如:arr=("one" "two" "three" "four" "five").在awk中數組下標從1開始,需要注意哦~
      awk可以使用關聯數組這種數據結構,索引可以是數字或字符串。awk關聯數 組也不需要提前聲明其大小,因爲它在運行時可以自動的增大或減小。

# 統計主機上所有的TCP,按照TCP狀態進行分類
netstat -an | grep tcp | awk '{array[$6]++}END{for(item in array){print item,array[item]}}'
->LISTEN 8
->ESTABLISHED 26
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章