1. 程式人生 > >shell指令碼抽取文字檔案中指定字串的方法:sed+grep方法、awk+grep方法(必要時可以聯合sed以及grep)、grep+cut方法

shell指令碼抽取文字檔案中指定字串的方法:sed+grep方法、awk+grep方法(必要時可以聯合sed以及grep)、grep+cut方法

在linux中經常要對一些動態的文字檔案抽取指定的字串,比如執行ps命令後想要獲取指定的執行程序(如ps自己)的PID號(同一個程序每次啟動的時候pid號是隨機分配的)。該怎麼辦呢?當然,可以用一些擷取字串的方法,這裡介紹一下用2種方法來解決這類問題。

一、sed+grep方法:

首先大概瞭解一下sed,sed是linux裡面一個非互動性的文字流編輯器(好長的定義,反正我聽起來我很拗口)。解釋2點:

1,非互動性:這裡非互動性是指sed工具並不直接編輯目的文字檔案,而是編輯目的檔案的一個快取拷貝,只能夠修改拷貝檔案,不會對原始檔有任何的改動!也就是說編輯並不在目的檔案現場!比如我想要編輯example.log檔案,如果用vi編輯器必須直接開啟example.log檔案,然後在裡面編輯。而sed是編輯example.log檔案的一個拷貝,那麼你可能要問了,這個拷貝在哪兒呢?這個拷貝在快取裡面,不需要我們自己拷貝,只要你呼叫了sed工具來編輯,系統自動會幫我們拷貝出一個example.log檔案供你去編輯,從而保護好原始檔不被隨意改動。

2,文字流:文字流是指我們通過一定的shell指令對文字進行編輯,而不想vi編輯器一樣需要把檔案開啟之後才能夠進行編輯。比如我要刪除example.log裡面的第一行。vi裡面必須先開啟檔案,然後選中第一行,再按下backspace鍵刪除。如果你使用sed命令的話,就只要執行以下一行程式碼就可以乾脆利落的刪除掉第一行:sed '1d' example.log。現在可以知道,sed工具無需開啟檔案再進行操作,而是通過一系列命令操作文字流,所以叫做文字流編輯器。

好,現在來解決問題:

舉個例子,比如使用ipsec的時候經常需要使用的一個命令是setkey -D來檢視spi是否正常生成了。而每次ipsec連線的時候產生的spi是不一樣的,當spi產生失誤的時候,就會出現spi=000000000;現在我想通過一定的shell指令碼獲取spi的值。setkey -D產生而定輸出如下圖

我們在說sed,當然使用sed來解決該問題。

明確一下我們的目的:我們的目的是要獲取上圖中的一段資料:spi=152056446,並且每次如果ipsec重新啟動的時候spi後面的資料會不一樣(但長度一樣,並且都是數字)。那麼我們想到自然要用到正則表示式來匹配該段資料,然後輸出到一個變數或者檔案供我們後續使用。

再來看看sed給我們提供了什麼方法:sed可以很方便的把某一行打印出來,sed -n '2p' example.log會打印出第二行。也就是說sed很容易獲取行,這顯然不能滿足我們的需求。sed不會這麼簡單!sed還提供了刪除,追加,插入,替換等豐富的方法來對一段文字進行編輯。

刪除某一行:

追加:把hello,i was appended here 追加到有national這一行的的後面。

替換:sed 's/source string/destination string/' example.log

那麼,我想既然sed可以非常方便的獲取到某一行資料,而它又能夠對文字進行編輯,那麼,我的想法是,先用sed對文字進行一定的編輯,把目標字串編輯到某一行內,我就非常方便的獲取到了


好,照著這條思路走:

1,編輯文字,把目標字串放到單獨的一行裡面來:觀察之後可以看出,該段目的字串spi=152056446(0x0910327e)前後都有空格,那麼就用空格作為重新編輯的標記位,前後換行。

輸入命令:sed 's/ /\n/g' example.log。解釋下命令,s是替換的標記,第一個/ /裡面有一個空格,意思是查詢所有含有空格的行,最後的g表明要對該行的所有空格進行查詢,而不只是查詢到第一個就查詢下一行,第二個/\n/是一個換行符,結合前面的空格查詢語法,可以對所有的空格替換成換行符。裡面的命令執行後會把文字重新編排,遇到空格就換行,這樣,目標字串就已經到了單獨的一行裡面去了!!!

2,獲取目標行字串:現在就很簡單了,我們可以用grep來獲取(grep "spi" example.log),也可以用sed來獲取(sed '/spi/p' example),把螢幕輸出重定向到一個檔案裡面去,或者賦給一個變數,這樣,我們抽取目標字串的任務就完成了!!!

是不是很簡單!!!好,下面來看awk方法

 

二、awk方法:

同樣的,首先來大概瞭解一下awk方法的精髓,awk方法主要是對文字進行“列”的操作,這個對比一下sed和grep可能我們更容易理解一些,sed和grep主要是對文字進行“行”的操作,awk會把每一列都取一個名字,從第一列開始:分別為$1,$2...$n,好吧,這樣就可以按照名字來分別操作列了。

好吧,現在我們來例項操作一下。

我們從ps輸出裡面去查詢指定程序名字的PID,在我的終端直接輸入ps x之後會顯示當前程序的名字,其部分截圖如下:

現在我想獲取的是當前ps x輸出裡面的程序名為上圖中紅圈部分nautilus的PID值1355,由於相同程序會在不同的時候開啟的時候所獲取到的PID號不一樣,所以當前的PID值1355其實是個不一定的數字。於是,我們只能通過程序名字nautilus來獲取當前PID。好,目的明確之後進行操作:

1,配合使用管道,使用grep獲取到包含nautilus的行:

發現有2個nautilus,好吧,那我們就來操作2個nautilus的PID,只是需要配合使用一下sed,沒事,就當複習一下;

2,現在是awk派上用場的時候了:

繼續使用管道,可以看到,利用命令可以得到程序名為nautilus的PID列:1355和1858。

這裡先來個小插曲,簡單介紹一下awk命令的語法,awk基本語法為:awk [-F] "field-operator" 'comand' inputfiles,-F和field-operator一起使用,field-operator是域分隔符,如果不使用-F選項,則預設的域分隔符為空格。後面command命令一般需要用一堆“{}”括起來,然後進行必要的操作,比較全面一點的command命令'{if($1~/^A/) print $1}',翻譯一下這個命令就是,如果第一列($1)裡面有匹配(~)正則表示式(/^A/)的話,那麼就輸出(print)到標準輸出。需要注意的是,條件必須要用一堆"()"括起來,正則表示式需要用“//”括起來。當然,完全可以不要條件匹配,可以直接輸出指定列,如'{print $1}'。好,awk就介紹到這裡,不過awk是一個非常非常強大的文字格式化抽取的工具,需要專門的學習。後面我再寫一個關於awk學習的總結。

3,現在需要利用sed對"行"的操作了,我們獲取到的第一行就是第一個nautilus的PID值,上面我們已經介紹了sed命令,所以很簡單的,接著上面的管道,輸入以下命令:

很快,我們就可以得到第一個nautilus的PID值了

是不是很簡單,就簡單的三部操作,grep——awk——sed,這三真是文字操作裡面的拼命三郎啊

三、現在來稍微介紹一下cut方法

cut也是一個比較強大的工具,可以對一行字串進行多種模式匹配的剪下操作,也可以對一個排列非常整齊的文字進行操作,下面通過2個例子來大致說明 一下cut的操作。

1,先說明cut對一行字串的操作:

在終端執行echo $PATH命令,可以獲得當前預設的bash路徑,如下圖:

其中每一個路徑都被一個“:”分隔開來,現在我想獲取第1個和第2個冒號之間的路徑,使用cut將會非常之方便,在終端輸入命令:

確實,我們獲取到了第一個冒號與第二個冒號之間的字串“/usr/local/bin”,現在來稍微解析一下這條命令,-d和':'一起把管道輸入的一行字串進行了域的分隔,每一個分隔符(在這裡是個冒號“:”)前面的字串被稱為一個域,若有n個分隔符,這個域的編號則從1開始到n+1,第1個分隔符前面的字串為第一個域。這個域對應在該命令列則是-f後面的數字2,所以該命令`cut -d ':' -f 2`表達的意思是輸出第二個分隔符“:”之前的域。

如果我們執行

或者是cut -d ':' -f 2-4,則可以輸出第2個和第四個域的字串,但是還多了一個分隔符,這點我也沒搞懂。

上面是是用分隔符對一行字串進行操作,下面我們隊字串的每一個字母當成一個數組裡面的元素,只是第一個元素是從1開始,而不是傳統陣列的0開始。這樣可以進行另外一種cut操作,現在先定義一個字串變數,並從中截取出1-5之間的字元:

可見,這條命令很好的把字串“hello world”從第2個字母到第5個字母cut了下來,把最後的引數進行一下修改,如下:

則可以擷取到第2個字元以及後面的所有的字元。

2,cut對格式化整齊的文字進行操作:

這是一段格式化比較整齊的資料,每一行前面的“declare -x”都是一模一樣的,我現在想把這一段給去掉:

其實我們發現對該文字的操作跟上的一行字串的操作沒什麼區別。

那麼就驗證一下對分隔符的操作是否也一樣:

發現結果確實是一樣。這說明cut會把一個文本里面的每一行都獨立的對待,然後再操作。