shell 腳本之文本處理

我學習shell 最初目的就是用於文本處理,以及自動化處理一些繁雜的操作,shell 腳本在這方面正是非常好用的工具。

本文只介紹常見的文本處理,對於其中涉及到的命令,以及正則表達式則不過多介紹;如果想用好shell, linux 文本操作的一些命令,還有正則表達式都必須要掌握。學習正則表達式開始比較難,多動手練習後,其實還是非常有幫助的。在java 和一些腳本語言中也有正則表達式的概念,因此強烈建議學好它,而且非常值得。


本文圍繞以下幾個問題展開討論,針對每個問題再細化出另外一些問題:

1、如何查找文本?

2、 如何提取文本?

3、 如何替換文本?

4、 如何刪除文本?

5、 如何插入文本?

6、 二進制文件處理


一、 查找文本,過濾文本

查找文本非常有用, 比如在查看打印時可以從大量打印中篩選出我們需要的打印。

查找或過濾文本當然用grep,非常強大。

假設我手上有一個文件:onelife.txt

文件內容如下:

  You Have Olny One Life


There are moments in life when you miss someone so much that
you just want to pick them from your dreams and hug then for
real! Dream what your want to dream; to whree you want to go;
be what you want to be, because you have only one life and one
chance to do all the things you want to do.

hello# world

string test "start " end
start123end

1. 查找文件中是否出現life?

$ cat onelife.txt |grep 'life'

通過上面的命令可以將含有life的所有行都打印出來。

如果我們只需要知道結果是否包含該文本,則可以如下:

$ cat onelife.txt |grep 'life' > /dev/null

$ echo $?

通過返回值來判斷,0 表示查找成功,1 表示查找失敗。

2. 如何顯示既包含life 的行又包含miss的行?

其實這個問題就是怎麼表示邏輯與的問題

$ cat onelife.txt |grep 'life' |grep 'miss'

通過管道實現’與‘操作


3. 如何顯示包含life 或者包含miss的行?

$cat onelife.txt |grep  -E 'life | miss'

這裏使用-E參數 調用egrep,不調用egrep也可以, 在 | 符號前面加個轉義字符就行。


4. 如何顯示所有不包含 life的行?

cat onelife.txt|grep -v 'life'

這裏藉助grep 的-v 參數,來顯示所有不匹配行。


5. 如何顯示包含數字的行?

cat onelife.txt |grep -E '[0-9]+'

這重匹配一類的問題就需要使用到正則表達式了 


二、 提取文本

提取文本主要會用到grep/ cut /awk/, 對於二進制文件,需要結合od 命令

1. 如何提取出網頁中的所有網頁鏈接?

$ grep -E -o 'https?://www\.[a-zA-Z0-9_]+\.[a-zA-Z]{2,4}' baidu.txt |sort|uniq
https://www.baidu.com
http://www.baidu.com
http://www.beian.gov
http://www.hao123.com
http://www.nuomi.com
http://www.w3.org

這裏的baidu.xtx文件是百度網頁的源碼(用360瀏覽器可以直接右鍵查看源碼),只是提取了一級網頁, 在上面用到了grep的-o 參數, 這個參數專門用於提取匹配成功的文本內容;在‘’單引號包含的部分是一個正則表達式。最後刪除一些重複的網站。

同理, 也可以提取圖片, 視頻,電子郵件等等內容。 在部分網頁中可能沒有視頻的下載地址,對於不太懂js的人來說,那麼我們可以查看該網頁的源碼,然後保存成文本,再從中提取出視頻地址。


2. 如何提取出雙引號中包含的字符串?

其實這個問題和上一個一樣,同樣可以用grep來,只是因爲比較常用,因此單獨列出來。

$ echo '"hello" world' |grep -o '".*"'|sed 's/"/ /g'
 hello 

其中grep提取字符串, sed 用來刪除雙引號


*** 基本上只要能寫出正則表達式的,都可以用grep來提取;但是,對於有些比較難寫出正則表達式的就要借用分隔符來提取了。


3. 在linux下如何提取出用戶ID?

 eg:

$ cat /etc/passwd |tail -n 5|cut -d':' -f1,3
lfc:1018
zhq:1019
ljl:1020
lpf:1006
test:1009

 passwd 文件下包含有用戶名、ID和組ID等內容, 這個文件以:爲分隔符,文本內容都比較相識,因此用正則表達式不好匹配,cut 或awk 正合適。

這個例子中我只提取最後五個用戶的信息,提取了第一和第三個字段,也就是用戶名和ID, 它們可以指定一個分隔符,然後對每個字段進行提取。

用awk 可以這樣提取:

$ cat /etc/passwd |tail -n 5|awk -F ':' '{printf("%-10s\t%-10s\n",$1,$3)}'
printf用於格式化輸出, 和C用法一樣。

awk 非常強大,在處理分組,統計方面很有優勢,甚至可以當成一門語言,它的語法格式非常簡單,對於基本用法還是很容易掌握的。


三、 替換文本?

簡單的字符串替換可以使用tr命令, 當然最好用的還是sed了,可以匹配正則表達式,sed可以完成所有你希望的替換工作。


1. 將 字符串"abcdefgHIJklmn" 全部轉換成大寫?

$ echo abcdefgHIJklmn |tr [a-z] [A-Z] 

ABCDEFGHIJKLMN

$ echo abcdefgHIJklmn |sed 's/[a-z]/\u&/g'
ABCDEFGHIJKLMN

用sed 和 tr都可以,tr比較直觀,sed則需要掌握一些元字符的意義;\u用於將後面的字符轉換成大寫, \l 用於轉換成小寫。


2. 如何將單詞的首字母變成大寫該如何實現呢?

$ echo hello world |sed 's/\b\w/\u&/g'
Hello World

這裏用tr就不太好實現了。


3. 如何將文本中的單引號轉換成雙引號? 

$ echo "hello '123' world"|sed 's/'\''/"/g'
hello "123" world
$ echo 'hello "123" world'|sed 's/"/'\''/g'
hello '123' world

上面 分別實現將123的單/雙引號進行轉換, 最外面的引號只是爲了把這個字符串當成一個字符串處理,實際echo時並不會出現


4. 替換指定行

$ cat onelife.txt |sed '2c change line'

將第二行 替換成 change line

四、 刪除文本

刪除文本我經常使用tr 或sed, grep命令

1. 如何刪除空行?

$ cat onelife.txt |tr -s '\n'

-s 用於縮減連續字符,只保留一個字符。

$ cat onelife.txt |sed '/^$/d'

sed 中有 d 命令,用於刪除一行,^$ 用於匹配空行


2. 如 刪除Windows文件“造成”的'^M'字符?

在window下編輯腳本,然後放到linux下執行,很容易出現莫名其妙的錯誤,其實就可能是\r字符導致的,刪掉它就可以了。

因爲window下換行符爲 \r\n , linux下爲\n

$ cat onelife.txt | tr -d "\r"

$ cat onelife.txt|sed 's/\r//'

需要注意的是, tr 刪除的是“”中所有的字符, 如tr -d “abc”  會刪除所有a b c , 而不是abc 字符串


3. 如何刪除雙引號內(包括雙引號)的字符串?

$ echo 'hello "123" world'|sed 's/".*"//'
hello  world

如果要刪除整行,可以用sed  d 命令, grep -v 參數


4. 如何刪除指定行? 如刪除第一行

$ cat onelife.txt |sed '1d'


五、 插入文本


1. 行內插入字符,行首和行尾插入一個雙引號, 行內的數字字符串插入單引號,如 hello 123 word  變成 “hello '123' world”?

$ echo 'hello 123 word' |sed -e 's/^/"/; s/$/"/; s/[0-9]\+/'\''&'\''/' 
"hello '123' word"

^表示行首, $表示行尾, &表示匹配成功的pattern


2. 插入一行。 

a.  在指定行插入一行:

cat onelife.txt |sed '2i "insert line"'

在第二行插入 “insertline”


b. 在匹配行之前、之後插入一行

cat onelife.txt |sed -e '/dreams/ i\#if 0' -e '/dreams/ a\#endif'

在包含dreams 行的前面插入 #if 0, 後面一行插入#endif

用來註釋某句話,還是很有用的哈, 雖然很少程序員會這樣註釋多個這樣的行。


六、 二進制文件處理

假設現在有一個二進制文件data.bin, 其中有兩個字節內容


1. 如果提取二進制文件中的第二個字節內容?

$ od -t xC dss.dat |head -1 |cut -d' ' -f3
02

od 命令用於查看二進制文件,默認爲八進制顯示,也可以指定顯示格式, -t  x 表示以十六進制顯示。C 字符表示按字符分割, 默認地od 將4個字節一起作爲分割,也就是會將0002 當成一個單元來顯示。

也可以直接用hexdump來查看十六進制文件。

$ hexdump -C dss.dat
00000000  00 02                                             |..|
00000002

2. 如何將文件分割?

先生成一個測試文件

dd if=/dev/zero bs=100k count=1 of=data.file

這個測試文件全是0, 在當前目錄下 data.file


按大小分割:

$split -b 10k data.file -d -a 4 split_

 -d 表示後綴爲數字, -a 表示後綴長度爲4 ,指定文件名前綴爲 split_ 

按行分割:

split -l data.file -d -a 2 split_

由於這個文件實際大小爲0, wc 統計到的行數也爲0, 因此這個 -l參數無效。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章