歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> SHELL編程

Linux.Shell編程筆記-基本文本處理

第五章 基本文本處理

排序文本

文本處理是UNIX/LINUXShell編程中幾乎最重要的一部分。在UNIX/LINUX 的設計中,一切都是文件,而系統中許多程序的協同工作是通過文木或者文本流來實現的。因此,UNIX/LINUX 中的文本處理以及文本流的設計就成了重要的環節.

管道是UNIX/LINUX中的一個重要發明,管道連接了各種處理工具,組建文本流。在UNIX/LINUX 中,文本處理工具常常被設計成過濾器的形式。通過管道連接不同過濾器,這樣,很簡單的拼接就能實現需求的功能。

Sort命令的行排序

許多數據(文本)文件都按照一定的格式組織,這些文木文件以可一讀的方式提供信息檢索和處理。一般來說,這種有格式的文本文件都可以排序。排序後的文木文件更利於檢索。

常見的排序算法很多,例如胃泡排序、歸並排序、快速排序等,化是排序效率各不相同。

UNIX/LINUX下提供的排序工具sort可以高效工作,並且,通常情況下,它比你寫的排序算法高效很多。因此,即使不了解排序的具體實現細節,你也一可以放心人膽地使用sort排序。

sort命令將輸入看成是具有多條記錄的數據流,而記錄由字段組成,記錄是以換行符為界定符號,每行對應於一條記錄。字段則是以空白字符為界定,sort命令一也提供用戶指定字段界定字符的參數。

在未提供參數的情況下,sort命令會根據當前字符集(locale )所定義的次序排序。例如,在傳統的C looal。中,sort命令會按照ASCII順序排序,如果需要改變排序規則,可以修改當前字符集。

[houchangren@ebsdi-23260-oozie data]$ catfruits.txt   //查看文件內容
apple
%%banae
banana
apple
Presimmon
Banana
orange
presimmon
[houchangren@ebsdi-23260-oozie data]$ sortfruits.txt   //默認排序
apple
apple
%%banae
banana
Banana
orange
presimmon
Presimmon
[houchangren@ebsdi-23260-oozie data]$ echo$LANG  //查看默認設置字符集
zh_CN.UTF-8
[houchangren@ebsdi-23260-oozie data]$LANG=EN_US sort fruits.txt  //設置成EN_Us的排序
%%banae
Banana
Presimmon
apple
apple
banana
orange
presimmon

-d參數 是按照字典排序(默認)

-f 是排序前都按照都寫來排序(小寫全轉成大寫)

 [houchangren@ebsdi-23260-oozie data]$ sort -d-f fruits.txt    
apple
apple
%%banae
banana
Banana
orange
presimmon
Presimmon

-u 去重復,相同行去掉

[houchangren@ebsdi-23260-oozie data]$ sort-d -f -u fruits.txt
apple
%%banae
banana
orange
Presimmon
Sort命令的字段排序

sort命令還可以對字段進行排序。在sort的參數列表中,-k參數可以選定排序字段,而-t參數可以選擇字段分界符,如果沒有設定,則默認是空白字符。

[houchangren@ebsdi-23260-oozie data]$ cat sort.txt
keyDataXSZKCZY:XXXSCK:604834:AFS:3636351      d_20131220170748-1600299142     2
keyDataXSZKCZY:XXXSCK:604836:AFS:3636353      d_20131220170748-1600299142     3
keyData XSZKCZY:XXXSCK:604838:AFS:3636355       d_20131220170748-1600299142     1
keyDataXSZKCZY:XXXSCK:605180:AFS:3639304      d_20131220170748-1600299142     6
keyDataXSZKCZY:XXXSCK:606728:AFS:3658757      d_20131220170748-1600299142     5
keyDataXSZKCZY:XXXSCK:607072:AFS:3661194       d_20131220170748-1600299142     2
keyDataXSZKCZY:XXXSCK:607188:AFS:3661453      d_20131220170748-1600299142     23
keyDataXSZKCZY:XXXSCK:607195:AFS:3661460      d_20131220170748-1600299142     4
keyDataXSZKCZY:XXXSCK:607197:AFS:3661462      d_20131220170748-1600299142     2
keyDataXSZKCZY:XXXSCK:607199:AFS:3661464      d_20131220170748-1600299142     2
[houchangren@ebsdi-23260-oozie data]$ sort -t $'\t' -k4 sort.txt    //因為\t比較特殊所以要用$
keyDataXSZKCZY:XXXSCK:604838:AFS:3636355      d_20131220170748-1600299142     1
keyDataXSZKCZY:XXXSCK:604834:AFS:3636351      d_20131220170748-1600299142     2
keyDataXSZKCZY:XXXSCK:607072:AFS:3661194      d_20131220170748-1600299142     2
keyDataXSZKCZY:XXXSCK:607197:AFS:3661462      d_20131220170748-1600299142     2
keyDataXSZKCZY:XXXSCK:607199:AFS:3661464      d_20131220170748-1600299142     2
keyDataXSZKCZY:XXXSCK:607188:AFS:3661453      d_20131220170748-1600299142     23
keyDataXSZKCZY:XXXSCK:604836:AFS:3636353      d_20131220170748-1600299142     3
keyDataXSZKCZY:XXXSCK:607195:AFS:3661460      d_20131220170748-1600299142     4
keyDataXSZKCZY:XXXSCK:606728:AFS:3658757      d_20131220170748-1600299142     5
keyDataXSZKCZY:XXXSCK:605180:AFS:3639304      d_20131220170748-1600299142     6
 
[houchangren@ebsdi-23260-oozie data]$ sort -t $'\t' -k4 -n sort.txt   
keyDataXSZKCZY:XXXSCK:604838:AFS:3636355      d_20131220170748-1600299142     1
keyDataXSZKCZY:XXXSCK:604834:AFS:3636351      d_20131220170748-1600299142     2
keyData XSZKCZY:XXXSCK:607072:AFS:3661194       d_20131220170748-1600299142     2
keyDataXSZKCZY:XXXSCK:607197:AFS:3661462      d_20131220170748-1600299142     2
keyDataXSZKCZY:XXXSCK:607199:AFS:3661464      d_20131220170748-1600299142     2
keyData XSZKCZY:XXXSCK:604836:AFS:3636353       d_20131220170748-1600299142     3
keyDataXSZKCZY:XXXSCK:607195:AFS:3661460      d_20131220170748-1600299142     4
keyDataXSZKCZY:XXXSCK:606728:AFS:3658757      d_20131220170748-1600299142     5
keyData XSZKCZY:XXXSCK:605180:AFS:3639304       d_20131220170748-1600299142     6
keyDataXSZKCZY:XXXSCK:607188:AFS:3661453      d_20131220170748-1600299142     23

-n參數讓sort命令按照整數數字進行比較。之前的排序很好的說明了,23在中間

Sort 小結

sort是一個重要的命令,你可以在許多重要的UNIX/LINUX shell中見到它的身影:只要有數據存在,並有檢索的需求,往往就有sort的支持。如果給UNIX/LINUX的命令論資排輩,sort絕對可以進入前十名。

上邊所做的只是sort命令冰山一角,sort命令的強大之處遠不止於此。除了對文本

行和字段進行排序,sort命令和其他命令結合使用〔它本身就被設計成過濾器的模式)將實現強大的功能,例如,可以對文本塊進行排序處理等.

並且,sort命令的效率是值得稱道的:自從它問世起,已經有許多人對它進行研究、優化和調整。它肯定比你寫的排序算法工作得更好,相信它,而不要嘗試去重復發明輪子〔編寫冗余的脆弱的排序算法)。

但是,注意,sort命令是不穩定的口排序算法的穩定性指的是:兩條相同的記錄輸入順序和輸出順序保持不變。

排序字段都相同,但是輸出和輸入的順序卻不一致。所以sort不是穩定的排序實現。GUN的coreutils包中的sort命令彌補了這個不足。現在。你可以通過一stable選項來使輸入輸出的相同記錄順序不變了。但是,這個參數相應會降低sort命令的效率(采用稍微低效的排序算法來獲得穩定性).

當sort作為過濾器在管道中使用時,排序算法的穩定性就變得重要了。因此,你需要根據穩定性(stable )是否對自己的命令重要,來決定是否采用--stable參數。

文本去重

由於 sort -u去重復的命令只是對指定排序的列相同就會去重,但是可能其他字段不同,所以有時候可能不是我們想要的。

UlSIX\Linux系統中有另一條命令用於數據一記錄去重,它足uniq.uniq命令會去除數據流中重復的記錄,只留下第一條記錄.它常常被用於管道中,例如,接在sort命令之後用於去重。

uniq命令主要有3個選項,-c 用於顯示出現重復行的計數,-d選項僅顯示重復行,-u選項僅顯示不重復的行

[houchangren@ebsdi-23260-ooziedata]$ cat fruit.txt
%%banae 
banana  
apple
Presimmon
%%banae 
apple 
Banana  
orange  
presimmon
[houchangren@ebsdi-23260-ooziedata]$ sort fruit.txt | uniq -c     //這個地方兩個apple因為後邊的空格導致認為是兩個記錄
      1 apple
      1 apple 
      2 %%banae 
      1 banana  
      1 Banana  
      1 orange  
      1 presimmon
      1 Presimmon
[houchangren@ebsdi-23260-ooziedata]$ sort fruit.txt | uniq -c -d  //僅顯示重復行
      2 %%banae 
[houchangren@ebsdi-23260-ooziedata]$ sort fruit.txt | uniq -c -u    //僅顯示不重復行
      1 apple
      1 apple 
      1 banana  
      1 Banana  
      1 orange  
      1 presimmon
      1 Presimmon
[houchangren@ebsdi-23260-ooziedata]$

統計文本行數、字數以及字符數

Unix/linux中的wc命令可以提供文本的行數、字數、字符數統計。wc命令也是POSIX中標准的一部分,你可以放心大膽地使用。

-c 參數含義是讓WC命令顯示字符的個數。

-w 參數含義是讓WC命令顯示單詞的個數。

-l 參數含義是讓WC命令文本行的行的個數。

[houchangren@ebsdi-23260-oozie data]$ wc/etc/passwd
 61   92 2867 /etc/passwd
[houchangren@ebsdi-23260-oozie data]$ wc -c/etc/passwd
2867 /etc/passwd
[houchangren@ebsdi-23260-oozie data]$ wc -c-w /etc/passwd
  922867 /etc/passwd
[houchangren@ebsdi-23260-oozie data]$ wc -c-w -l /etc/passwd
 61   92 2867 /etc/passwd
[houchangren@ebsdi-23260-oozie data]$

//查找上層目錄中name是.sh結尾的個數

[houchangren@ebsdi-23260-oozie data]$ find../ -iname "*.sh" |wc -l
11 

//找出/etc/passwd文件中包含bash字符串的行的個數。一般來說,就是系統中啟動

shell為bash的用戶的個數。

[houchangren@ebsdi-23260-oozie data]$ grepbash /etc/passwd | wc -l
25

//用純grep達同樣效果

[houchangren@ebsdi-23260-oozie data]$ grep-c bash /etc/passwd
25 

// WC命令統計當前目錄下所有以.sh結尾的文件,統計它們中的字符數、單詞數和行

數,並且在最一行將總計的結果打印出來。

[houchangren@ebsdi-23260-oozie shell]$ wc*.sh
  6    7   51 add.sh
 19   29  337 a.sh
  7   16   85 checkUserIsExist.sh
  8   11   55 if.sh
 16   58  284 testalg.sh
  8   20  206 testcase.sh
   6   7   42 testfor.sh
 26   51  319 testwhile.sh
 11   27  132 user_login.sh
 15   26  193 whileexample2.sh
 17   38  253 whileexample.sh
 139  2901957 總計

Note

Wc命令的執行會隨著locale的設定有不同的結果。因為不同的locale會影響Wc命令解釋字節序列時的字符/單詞分隔器。

打印和格式化輸出

Linux下打印和格式化文本的工具很多,例如pr, fmt, fold等。它們各有不同的用途。

使用pr打印文件

UNIX/Linux的 pr命令可以用來將文本轉換成適合打印的文件。這個工具的一個基本用途就是將較大的文件分割成多個頁面,並為每個頁面添加標題。  

  比如,pr可以將一個150行文本的文件轉換成三個文本頁,然後讓用戶進行打印。

  在默認情況下,每個頁面會包含66行文本,不過通過pr的-l參數,用戶可以改變這一規則。  

  可以用來控制文本輸出效果的參數很多,一般來說,每頁的標題就是這個文檔的文件名。當然,用戶也可以自行定義標題,比如:

  $ pr -h "My report" file.txt

  如果不使用上面的-h參數,打印的頁面會用“file.txt”作為標題,而加上-h參數後,頁面會使用該參數後指定的“My report”作為標題。

  用戶還可以使用pr命令將文本分列打印。這對於語句短小的文本來說比較有用,如果語句比較長,pr會在適當的位置進行換行。比如,要將file.txt文件按兩列打印,可以使用以下命令:

  $ pr -2 -h "My report" file.txt  

  默認情況下,pr會為每個頁面加入換行符(比如空行),不過用戶也可以使用制表符來代替空行。可以下面這段命令使制表符來代替空行:

  $ pr -f file.txt  

  如果用戶只是想打印文件,而不想保存它,那麼這個功能比較合適,但是如果用戶同時也要保存文件,那麼添加的制表符會讓文件看起來比較亂。

  需要記住的是,pr是一個標准的輸出工具,可以直接輸出到打印機,如果你希望將結果保存在文件中,則需要重定向它的輸出,如下面這個例子:

$ pr file.txt>file.output

測試實例:

[houchangren@ebsdi-23260-ooziedata]$ pr -f  fruit.txt
2014-01-1810:56                    fruit.txt                     Page 1
%%banae 
banana  
apple
Presimmon
%%banae 
apple 
Banana  
orange  
presimmon
[houchangren@ebsdi-23260-ooziedata]$ pr -f -c1  fruit.txt
2014-01-1810:56                    fruit.txt                     Page 1
%%banae 
banana  
apple
Presimmon
%%banae 
apple 
Banana  
orange  
presimmon
[houchangren@ebsdi-23260-ooziedata]$ pr -f -c1 -h "test" fruit.txt
2014-01-18 10:56                       test                       Page 1
%%banae 
banana  
apple
Presimmon
%%banae 
apple 
Banana  
orange  
presimmon
[houchangren@ebsdi-23260-ooziedata]$ pr -f -c1 -t fruit.txt
%%banae 
banana  
apple
Presimmon
%%banae 
apple 
Banana  
orange  
presimmon

在pr命令中 –c 參數是顯示多少欄, -t 參數表示不顯示標題

NOTE

在pr命令裡,直接使用-10的效果和使用-c10是一樣的‘這是因為pr有一個參數是-Column,這個參數即直接在連字符後面接數字,表示輸出的欄數.

使用fmt命令格式化文本

除了pr命令,UNIX/Linux下還有一條fmt命令可以格式化文本段落,使文本不要超出可見的屏幕范圍口fmt指令會從指定的文件裡讀取內容,將其依照指定格式重新編排後,輸出到標准輸出設備。若指定的文件名為“一”,則fmt指令會從標准輸入設備讀取數據口

-w 參數告訴fmt命令每行的最大字符數。

-s參數告訴fmt命令只拆開字數超出每列字符數的列,但不合並字數不足每列字符數的列

NOTE

警告,無論是pr還是fmt,在不同版本系統中的行為都不盡相同。因此你需要在不同版本的系統上

通過查閱manpage,來確定格式化輸出工具的功能。

使用fold限制文本寬度

LINIXILinux的fold指令會從指定的文什裡讀取內容,將超過限定列寬的列加入增列字符

後,輸出到標准輸出設備。若不指定任何文件名稱,或是所給予的文件名為”-“,則fold指令會從標准輸入設備讀取數據。

Note

注意了,fold的-w參數和fmt的-w參數並不相同。fold的-w參數很生硬地將文本輸出行截斷,但是並不判斷是否單詞也被截斷。而fmt的-w.參數會判斷單詞是否能夠正常顯示〔不被截斷顯示),遇到無法正常顯示的,fmt會將該單詞整體移到下一行,而fold會將之攔腰截斷。

提取文本開頭和結尾

//查看tomcat前20行日志

Head 查看文件的開頭

head -n 指定行

[root@ebsdi-23260-oozie logs]# head -20  catalina.out

tail命令查看文件的結尾

tail –n 指定行

[root@ebsdi-23260-oozie logs]# tail  -20 catalina.out    

-f參數動態查看

[root@ebsdi-23260-oozie logs]# tail -f catalina.out

字段處理

Cut取文本中的某個字段

-d參數規定了cut命令接受的字段分隔符。

-f參數規定了cut命令獲取的字段列,後邊跟數字多個用逗點隔開。

[houchangren@ebsdi-23260-oozie data]$ cut-d  ':' -f 1,7 /etc/passwd | grep bash |head -10
root:/bin/bash
mapred:/bin/bash
hdfs:/bin/bash
wangjuntao:/bin/bash
yangzhi:/bin/bash
huanghu:/bin/bash
zhangguochen:/bin/bash
houchangren:/bin/bash
hadoop:/bin/bash
neil:/bin/bash
join 連接字段

Linux一下的join命令可以連接不同的文件,使得具有相同key值的記錄信息連到一起。它會根據指定欄位,找到兩個文件中指定欄位內容相同的行,將它們合並,並根據要求的格式輸出內容。該命令對於比較兩個文件的內容很有幫助。

[houchangren@ebsdi-23260-oozie data]$ catstuff.txt
1      zhangsan
2       lisi
3      wangwu
6      maliu
[houchangren@ebsdi-23260-oozie data]$ catsalary.txt
1      1000
2      2000
3      2350
5      2400
[houchangren@ebsdi-23260-oozie data]$ joinstuff.txt salary.txt
1 zhangsan 1000
2 lisi 2000
3 wangwu 2350
[houchangren@ebsdi-23260-oozie data]$ joinstuff.txt salary.txt  -a1
1 zhangsan 1000
2 lisi 2000
3 wangwu 2350
6 maliu
[houchangren@ebsdi-23260-oozie data]$ joinstuff.txt salary.txt  -a2
1 zhangsan 1000
2 lisi 2000
3 wangwu 2350
5 2400
其他字段處理方法

Awk,後邊會專有章節介紹

文本替換

UNIX\Linux下的文本替換有很多種實現方法,例如,可以通過sed命令。如果在文本編輯器

中,則有更多的選擇。但是,最簡單的命令還是tr.

使用tr替換字符

tr命令從標准輸入刪除或替換字符,並將結果寫入標准輸出。在命令需要小范圍內做文本替換時,tr命令非常有用。

tr命令格式

a.      tr str1 str2
b.      tr {-d|-s} str1

tr完成的操作

a. 轉換字符
如果str 1和str2兩者都已指定,但-d標志沒有指定,tr命令就會從標准輸入中將str1中所包含的每一個字符都替換成str2中相同位置上的字符。

b. 使用-d標志刪除字符
如果-d標志已經指定,tr命令就會從標准輸入中刪除str1中包含的每一個字符。

c. 使用-s標志除去序列

如果-s標志已經指定,tr命令就會除去包含在str1或str2中的任何字符串系列中的除

第一個字符以外的所有字符。對於包含在str1中的每一個字符,tr命令會從標准輸出中除去除第一個出現的字符以外的所有字符。對於包含在str2中的每一個字符,tr命令除去標准輸出的字符序列中除第一個出現的字符以外的所有字符。

[houchangren@ebsdi-23260-oozie data]$ cat fruit.txt
%%banae 
banana  
apple
Presimmon
%%banae 
apple 
Banana  
orange  
presimmon
[houchangren@ebsdi-23260-oozie data]$ tr'a-z' 'A-Z' < fruit.txt > fruit.txt.upper
[houchangren@ebsdi-23260-oozie data]$ catfruit.txt.upper
%%BANAE 
BANANA  
APPLE
PRESIMMON
%%BANAE 
APPLE 
BANANA  
ORANGE  
PRESIMMON
[houchangren@ebsdi-23260-oozie data]$ catfruit.txt | tr -d 'a' > fruit.txt.rm
[houchangren@ebsdi-23260-oozie data]$ catfruit.txt.rm
%%bne 
bnn  
pple
Presimmon
%%bne 
pple 
Bnn  
ornge  
presimmon
[houchangren@ebsdi-23260-oozie data]$ catfruit.txt.rm |tr -s '%%' '$$' > fruit.txt.rep
[houchangren@ebsdi-23260-oozie data]$ catfruit.txt.rep
$bne 
bnn  
pple
Presimmon
$bne 
pple 
Bnn  
ornge  
persimmon
其他實例
#若要將大括號轉換為小括號,請輸入
>>> tr '{}' '()' < textfile> newfile
#若要將大括號轉換成方括號,請輸入:
>>> tr '{}' '\[]' <textfile> newfile
#若要創建一個文件中的單詞列表,請輸入;
>>> tr -cs '[:lower:][:upper:]''[\n*]' <textfile > newfile
#若要從某個文件中刪除所有空字符,請輸入:
>>> tr -d '\0' <textfile > newfile
#若要用單獨的換行替換每一序列的一個或多個換行,清輸入
>>> tr -s  '\n' < textfile >new file
或者
>>> tr -s '\012' <textfile >newfile
#若要以“?”(問號)替換每個非打印字符〔有效控制字符除外),請輸入:
>>> tr -c '[:print:][:cntrl:]''[?*]' <textfile > newfile
#若要以單個“非”字符替換<space>字符類中的每個字符序列,請輸入:
>>> tr -s '[:space:]' '[#*]'其他選擇

其實,tr的文本替換功能僅僅實現了最簡單的操作,如果有更復雜的需求,例如有條件判斷等邏輯的,就需要更強大和復雜的工具了。而這類工具,往往都可以稱之為語言。

Linux下常見的具有這類功能的工具有如下這些項。

perl強大的正則表達式文持在UNIX/linux世界無出其右,文木替換自然是小菜;

sed sed工具處理文本流,亦可以輕松地實現文木替換.

awk awk語言具有邏輯判斷與循環等特性支持,並且,在文本處理時根據需求,強大的可定制性也是其長盛不衰的原因。

python作為目前UNIX、Linux社區最流行的語言之一,python的文本處理模塊相當強大,

亦可以完成幾乎你想要的替換功能。但是,python的執行效率比較慢,並且,使用python這麼龐大的語一言處理簡單的文本操作似乎有點大材小用.

一個稍微復雜的例子

需求:獲取一天中訪問最多的前100名

步驟:cut -> tr-> cut-> sort->uniq->sort->head

第一個字段後是\t第二字段後是空白符(多個)所以得截取了兩次

[houchangren@ebsdi-23260-oozie data]$ catips.txt
 
20:32  10.12.165.1     1238723
20:31  10.12.165.7     123923
20:32  10.12.165.18    128323
20:12  10.12.165.20    1234623
20:32  10.12.165.25    1232443
20:32  10.12.165.26    1232433
20:32  10.12.165.31    1234523
20:32  10.12.165.32    123
20:32  10.12.165.33    12345
20:32  10.12.165.33    123245
20:32  10.12.165.36    123333
20:32  10.12.165.37    123423
20:32  10.12.165.38    12423
20:32  10.12.165.39    12623
20:32  10.12.165.40    12523
20:32  10.12.165.255   12423
20:32  224.0.0.2       12123
20:32  224.0.0.22      12223
20:32  224.0.0.251     12322
20:32  224.0.0.252     12322
[houchangren@ebsdi-23260-oozie data]$ cut-d $'\t' -f 2 ips.txt |tr -s '[" "]' $'\t'|cut -d $'\t' -f1|sort|uniq -c|sort -r|head -100
     2 10.12.165.33
     1 224.0.0.252
     1 224.0.0.251
     1 224.0.0.22
     1 224.0.0.2
     1 10.12.165.7
     1 10.12.165.40
     1 10.12.165.39
     1 10.12.165.38
     1 10.12.165.37
     1 10.12.165.36
     1 10.12.165.32
     1 10.12.165.31
     1 10.12.165.26
     1 10.12.165.255
     1 10.12.165.25
     1 10.12.165.20
     1 10.12.165.18
     1 10.12.165.1
     1
Copyright © Linux教程網 All Rights Reserved