638
技術社區[雲棲]
Linux下文本的高效處理
1 引言
所謂的文本處理是指對文本進行查找、替換、刪除、排序等操作, linux在文本處理方麵提供了大量優秀的工具, 使得在linux下進行文本處理極其的方便.
我們平常的工作中, 經常會用到文本處理, 比如日誌分析, 比如文本抽取, 等等, 所以掌握好文本處理, 將會對我們的工作起到極大的作用.
下麵我就來逐個介紹下這些強大的工具, 對於我覺得大家可能比較熟知的工具及用法, 我會略過, 或者粗講下.
2 關於輸入
Linux哲學中, 為了更好的組合各種命令達到更加強大的功能, 大多數文本處理命令的輸入既可以是文件, 也可以是標準輸入, 如果沒有指定輸入文件, 則默認從標準輸入讀數據. 輸出都是標準輸出, 方麵傳給管道線的下一個命令, 想要輸出到文件的話, 重定向下即可. 下麵介紹的這些命令, 如無特殊說明, 則都可以從文件或者標準輸入讀入數據.
3 文本輸出
3.1 整個輸出
-
echo
輸入為命令行參數
非常常用的命令, 主要用作輸出字符串. 如果隻是為了向管道線的下一個命令傳輸入的話, 可以使用Here String:
echo xxx | md5sum md5sum <<< xxx
後者速度上應該會快一點, 不需要經過管道.
-
-n
不輸出換行符(默認輸出換行符)
-
-e
解釋轉義符, 常用的轉義符 \t, \n; 這個命令還有個最常用的用途就是輸出ANSI顏色:echo -e '\033[1;31mHello, \033[0m\033[1;33mworld!\033[0m'
Hello, world!
這個工具可以更方便的輸出ANSI顏色.
-
-E
不解釋轉義符
-
-n
-
printf
輸入為命令行參數
更強大的輸出你想要的文本的命令, 類似C裏麵的printf
printf '\033[1;31m%s, \033[0m\033[1;33m%d\033[0m and \u4e2d\u6587!\n' "Hello" 34
Hello, 34 and 中文!
不過此命令較echo來說, 使用率會低很多, 大多數情況下echo就能搞定了.
-
yes [STRING]
輸入為命令行參數
不停的輸出字符串STRING, 默認是y. 這個命令用處比較少, 但會有用, 比如測試tail命令.
-
cat (concatenate)
此命令最大的用途應該就是顯示特殊字符了, 如果你告訴我你經常用它給管道線的下一個命令傳輸入的話, 那麼就太浪費了:cat file | grep xxx grep xxx file
前者相比後者多啟動了一個進程, 還經過了管道. file很大的話, 性能差距很容易就看出來了.
-
-E
在行尾顯示$, 有時候行尾是空格或者TAB, 看不清行尾是哪裏, 加了這個選項就知道了
-
-T
輸出的時候用^I代替TAB符號, 這樣很容易知道那一坨空白到底誰是TAB了
-
-v
用^和M-表示法輸出不可打印字符taoshanwen@taoshanwen-laptop ~$ echo -e '\r' | cat -v ^M
-
-e
相當於-vE
-
-t
相當於-vT
-
-A
相當於-vET, 一般這個選項用的最多
-
-n
顯示行號, 應該有同學感興趣
-
-b
隻對非空行顯示行號, 有人感興趣嗎?
-
-s
對連續的空行隻輸出一個空行, 見過好多同學有這樣的需求
-
-E
-
tac
看名字知道了, 它和cat是反的, 倒著輸出, 先輸出最後一行, 接著是倒數第二行, 最後輸出的是第一行-
-s, –separator=STRING
設定分隔符, 代替\n分割文本
-
-s, –separator=STRING
-
rev (reverse)
這個命令和cat也是反的, 不過它不像tac那樣, 它輸出行的順序和cat也是一樣的, 不過輸出每行的時候, 先輸出最後一個字符, 接著是倒數第二個字符, 最後才是第一個字符, 下麵這個命令可以把輸入全部倒過來:tac | rev
-
nl (number lines)
更強大的行號顯示工具, 可以控製行號的格式, 寬度. 沒有特殊的需求, 用cat -n就夠了.
3.2 部分輸出
-
head
隻顯示文本的開頭幾行, 比如head -2隻顯示前麵2行
4 文本搜索
4.1 grep (Global Regular Expression Print)
非常常用的命令, 打印文本中匹配模式的行, 下麵的選項最好都能掌握.
grep [OPTIONS] PATTERN [FILE...] grep [OPTIONS] [-e PATTERN | -f FILE] [FILE...]
-
-E, –extended-regexp
使用擴展正則表達式(ERE), 默認的是基礎正則表達式(BRE), BRE中元字符?, +, {, |, (, )失去特殊意義(你是否遇到grep "a|b"是否不能打印出含有a或者b的行?), 想要表達特殊意義的話, 需要用轉義字符進行轉義(\), \?, \+等
-
-F, –fixed-strings
把pattern當作一個固定的字符串, 不進行正則解析. 當你要搜索非正則的固定字符串時(還可能含有正則元字符), 這個選項會非常有用, 而且由於不需要解析正則, 速度會快些.
-
-P, –perl-regexp
把pattern解析為perl的正則(PCRE), 由於perl的正則強大而簡潔, 所以可以多嚐試著使用這個選項.
-
-i, –ignore-case
搜索pattern的時候, 忽略大小寫. 如果沒有這個選項, 可以使用PCRE, 比如:grep -P "(?i)AB"
-
-v, –invert-match
顯示不能匹配pattern的行
-
-e pattern
如果你想要指定多個搜索pattern, 或者你想要搜索的pattern由減號(-)開頭(如果直接grep pattern的話, 會被解析為grep的選項), 就可以用這個選項了.
-
-f FILE, –file=FILE
從文件中獲取pattern, 每行一個pattern
-
-x, –line-regexp
pattern必須要匹配整行, 這個選項等價於 "^pattern$"
-
-w, –word-regexp
pattern必須要匹配整個單詞, 這個選項等價於 "\bpattern\b"
-
-c, –count
不打印匹配的行, 隻打印匹配的行數, 等價於grep pattern | wc -l
-
–color[=WHEN], –colour[=WHEN]
用顏色高亮出匹配的:(require 'coding-settings) ("C-x U" revert-buffer-with-coding-system-no-confirm-sb))) ("C-x M-C" set-buffer-file-coding-system))) (set-buffer-file-coding-system 'unix)) (set-buffer-file-coding-system 'dos))
-
-m NUM, –max-count=NUM
當發現NUM個匹配行後, 停止掃描剩下的文本.
-
-q, –quiet, –silent
不打印任何信息, 發現匹配即退出, 並返回0, 否則返回1. 我們經常隻是想查看整個文本裏麵是否有匹配, 這時候這個選項就非常有用了, 速度會快很多.
-
-o, –only-matching
隻顯示匹配pattern的字符串, 匹配行的其餘部分不顯示
-
-n, –line-number
在匹配行前麵打印行號有時候想看看匹配行周圍都是啥, 下麵這幾個選項就非常有用了:
-
-A NUM, –after-context=NUM
打印匹配行的後麵NUM行
-
-B NUM, –before-context=NUM
打印匹配行的前麵NUM行
-
-C NUM, -NUM, –context=NUM
打印匹配行的周圍NUM行
-
-a, –text
有時候文件中含有一些非可打印字符, grep可能會把它識別成二進製文件, 這時候grep隻會打印出是否匹配pattern的信息, 並不會打印匹配的每行, 這個選項會強製grep把該文件當文本文件處理
-
-R, -r, –recursive
遞歸處理文件夾下的所有文件
-
-l, –files-with-matches
不打印匹配的行, 隻打印匹配的文件
-
-L, –files-without-match
和-l相反, 不打印匹配的行, 隻打印不匹配的文件
-
-h, –no-filename
搜索多個文件時, 會在每行前麵輸出文件名, 如果你不喜歡, 使用此選項吧.
4.2 fgrep
grep -F
4.3 egrep
grep -E
4.4 rgrep
grep -r
4.5 agrep (approximate grep)
grep的模煳匹配版本
4.6 zgrep
對壓縮文件進行grep, 接受的選項和grep完全一樣
4.7 sgrep (structured grep)
對結構化的文本, 如SGML、XML、HTML進行搜索、抽取, 功能非常強大
4.8 nrgrep (Nondeterministic Reverse grep)
類似agrep
5 文本摘要
5.1 wc (word count)
最主要的用途就是統計行數
-
-l, –lines
最常用的選項, 統計行數
-
-L, –max-line-length
輸出文本最長行的長度
-
-w, –words
輸出單詞數
-
-m, –chars
輸出字符數
-
-c, –bytes
輸出字節數
5.2 md5sum
打印文本的md5, 主要用作文件校驗, 防止文件傳輸時發生錯誤或者被篡改. -c選項檢查md5是否正確
6 排序去重
6.1 sort
非常常用的命令, 啥序都能排
-
-r, –reverse
逆序排序, 默認是按從小到大排, -r後就從大到小了
-
-c, –check, –check=diagnose-first
檢查輸入文件是否是有序的, 不是的話, 會打印哪行開始不是有序的
-
-C, –check=quiet, –check=silent
類似-c, 但是不打印錯誤信息, 隻返回錯誤碼1
-
-k, –key=POS1[,POS2]
這個應該是sort最nb的地方了, 可以精確控製要排序的對象. POS具備這樣的形式:F[.C][OPTS]
其中, F是字段號, C是字符號, OPTS是排序選項, 可以每個字段排序的規則不一樣. F, C都是從1開始
sort -t ' ' -k1,1d -k2.2,2n <<-EOF bb 113 aa 224 cc 323 dd 444 cc 513 EOF aa 224 bb 113 cc 513 cc 323 dd 444
-
-u, –unique
對輸出結果進行去重, 隻輸出重複的記錄中的第一條記錄
-
-m, –merge
對有序的輸入文件進行歸並, 這個選項使得你能夠在多核機器上優化大數據集的排序
-
-s, –stable
使得sort成為穩定排序
-
-T, –temporary-directory=DIR
設定指定的臨時文件夾, 存放中間數據. 當你排序非常大的文件時, 而且/tmp所在的分區空間不夠時, 就會用到該選項了
-
-n, –numeric-sort
把輸入當整數來排序, 可以有負數, 但是不能含有加號(+)的正數, 這種輸入用-g搞定吧
-
-g, –general-numeric-sort
把輸入當作數值來排序, 可以有浮點數. 如果輸入是整數的話, 就用-n搞定吧, 人家性能高些.
-
-h, –human-numeric-sort
可以排序2K, 1G等帶單位的數字, 很爽啊, 想排序某文件夾下所有文件和文件的大小嗎:du -sh * | sort -h
-
-M, –month-sort
按月份進行排序, `JAN' < `FEB' < … < `DEC'
-
-d, –dictionary-order
按字典序排序, 忽略字母、數字、空白字符外的所有字符
-
-V, –version-sort
你開發的軟件有很多版了沒? 排下吧, 根據版本號
-
-t, –field-separator=SEP
設置字段分隔符, 默認為空白字符. 可惜的是, 這個字段分隔符隻能為單個字符
-
-b, –ignore-leading-blanks
忽略前導空白字符
-
-f, –ignore-case
忽略大小寫
-
-i, –ignore-nonprinting
忽略不可打印字符
-
-R, –random-sort
隨機排序, 我想你會用到它的, 反正我用過幾次. 不過排序結果不完全隨機, 因為sort會先對每行進行hash, 然後對hash值進行排序, 所以相同的行一定會排到一塊. 不過也許, 這正是你想要的. 如果你想更亂或者更加強大的功能的話, 看這裏
-
陷阱
你是否經常sort一個中文文件卻得不到正確結果? 那就對了, 你肯定沒設置好語言環境(locale), 試試LC_ALL=C sort吧. sort會根據本地語言環境對輸入文本進行排序. LC_ALL=C表示會根據字節值來排序. 或許你說我怎麼見到的都是LANG=C sort啊, 來, 我們看看bash info上關於LANG和LC_ALL的解釋:LANG Used to determine the locale category for any category not specifically selected with a variable starting with LC_. LC_ALL This variable overrides the value of LANG and any other LC_ variable specifying a locale category. LC_COLLATE This variable determines the collation order used when sorting the results of pathname expansion, and determines the behavior of range expressions, equivalence classes, and collating sequences within pathname expansion and pattern matching.
- LANG
如果你沒有用LC_來設定某個分類的locale, 將會使用LANG來決定這個分類的locale - LC_ALL
該變量會覆蓋LANG和LC_ - LC_COLLATE
該變量設置排序時的locale
所以, sort時, 設置LC_ALL是最保險的做法.
- LANG
6.2 tsort (topological sort)
拓樸排序, 該命令可能會用的比較少
tsort <<EOF a b c d e f b c d e EOF
輸出:
a b c d e f
6.3 uniq
也是非常常用的一個命令. 這個命令主要用來對有序序列進行去重, 所以它常和sort聯合起來使用, 但是sort -u本身就有去重的功能, 所以當你僅僅隻是為了去重時, sort -u就可以幫你搞定了(當輸入文本巨大時, 可以用hash來去重提高性能, 比如awk的關聯數組), 所以呢, 當年需要對重複的數據進行統計時, 會用到uniq. 當然其實uniq相比sort -u而言, 對重複數據有更加強大的處理
-
-c, –count
在每行文本前麵輸出重複次數
-
-d, –repeated
隻顯示重複的行, 重複的行隻顯示一行
-
-D, –all-repeated[=delimit-method]
顯示所有重複的行, 注意該選項與選項-d的區別
-
-u, –unique
隻打印不重複的行
-
-i, –ignore-case
比較的時候不區分大小寫
-
-f, –skip-fields=N
不比較前麵N個字段, 字段分隔符為空白字符
-
-s, –skip-chars=N
不比較前麵N個字符
-
-w, –check-chars=N
每行最多比較前麵N個字符
-
實例演示
大家看了uniq上麵幾個選項後, 是不是有uniq沒有太大用處的感覺? 這都是錯覺, 下麵我給大家演示下uniq在集合運算(統計中有大量的應用)方麵巧妙的應用.-
並集
sort A B | uniq
-
交集
sort A B | uniq -d
-
差集(A-B)
sort A B B | uniq -u
-
並集
-
缺陷
- 不能控製字段分隔符
- 不能像sort -k那樣精確的控製要比較的對象
6.4 comm
逐行比較兩個有序文件, 分三列輸出文件1獨有的行、文件2獨有的行、文件12共有的行,
$ cat ab ax by cz $ cat ac ax bd cz $ comm ab ac ax bd by cz
-
-1
不輸出第一列(文件1獨有的行)
-
-2
不輸出第二列(文件2獨有的行)
-
-3
不輸出第三列文件3獨有的行
-
–check-order
檢查輸入文件是否有序
-
–nocheck-order
不檢查輸入文件是否有序
-
–output-delimiter=STR
設定輸出分隔符, 默認為TAB
-
實例演示
-
交集
comm -12 <(sort A) <(sort B)
-
差集(A-B)
comm -23 <(sort A) <(sort B)
-
交集
7 操作字段
7.1 cut
挺常用的一個命令, 能非常方便的取某個字段
-
-f, –fields=LIST
選擇要輸出的字段
-
-c, –characters=LIST
選擇要輸出的字符
-
-b, –bytes=LIST
選擇要輸出的字節
-
-d, –delimiter=DELIM
設定字段分隔符, 默認是TAB. 可惜的是, 該分隔符也隻能是單個字符.
-
–complement
取設定的選擇LIST的補集
-
-s, –only-delimited
忽略不包含分隔符的行, 默認操作是輸出整行
-
–output-delimiter=STRING
設定輸出分隔符
-
LIST
-f, -c, -b選項使用的列表, 可以有下麵幾種形式:- N
第N個字段/字節/字符 - N-
從第N個到最後一個 - M-N
從第M個到第N個 - -M
從第一個到第N個
列表可以有多個, 之間以逗號分割, 比如:
cut -f1-3,4-7
- N
7.2 paste
這個命令很有意思, 把兩個文件按行粘貼到一塊, 曾經我想自己寫個程序搞定這個需求, 後來發現linux下竟然已經有這玩意了(linux總能給你帶來驚喜)
$ cat num2 1 2 $ cat let3 a b c $ paste num2 let3 1 a 2 b c
-
-d, –delimiters=LIST
paste兩個文件的時候, 默認是用TAB分割, 這個選項設定分隔符, 同為可惜的是, 隻能為單個字符(主要是paste可以粘貼多個文件, 這個選項的第二個字符用來分割第二個和第三個文件)
-
-s, –serial
默認paste是豎著粘貼的, 加了這個選項後, 就橫著粘貼了:$ paste -s num2 let3 1 2 a b c
7.3 join
這是一個稍微高級點的命令, 它把輸入文件當成一個key/value對, 然後會把同一個key的所有value粘貼到一塊, 來個例子:
$ cat file1 a 1 b 2 c 3 $ cat file2 a 4 c 6 $ join file1 file2 a 1 4 c 3 6
join默認把第一額字段當作key, 字段之間以空格分割, 作為key的字段必須有序.
-
-i, –ignore-case
比較字段時, 忽略大小寫
-
-t CHAR
使用CHAR作為字段分隔符, 又是隻能為單個字符(杯具…)
-
-1 FIELD
設定第一個文件的key為第FIELD個字段
-
-2 FIELD
設定第二個文件的key為第FIELD個字段
-
-j FIELD
-1 FIELD -2 FIELD
-
-a FILENUM
join默認隻打印擁有相同key的行, 該選項會打印第FILENUM個文件中沒有匹配上的行
-
-v FILENUM
和-a選項有點類似, 該選項隻打印第FILENUM個文件中沒有匹配上的行, 不會打印匹配上的行
-
–check-order
檢查輸入文件作為key的字段是否有序
-
–nocheck-order
不檢查輸入文件作為key的字段是否有序
-
-o FIELD-LIST
高級的控製輸出對象的選項, FIELD-LIST中的每個元素具有下麵這樣的形式:- 0
表示做為key的字段 - M.N
M為文件號, 取值為0或者1, N為字段號, M.N就是取第M個文件第N個字段
每個元素之間以逗號或者空格分割
- 0
-
-e EMPTY
-o選項中, 可能文件M中沒有字段號N, 這時候輸出的時候用EMPTY代替.
8 操作字符
8.1 tr (translate)
主要對文本中的字符進行替換、刪除.
- 該命令隻支持標準輸入, 不支持從文件輸入.
- tr僅支持單字節字符.
tr [OPTION]... SET1 [SET2]
字符集合可以由一係列的字符構成, 也可以具有以下形式:
- CHAR1-CHAR2
從CHAR1到CHAR2的所有字符 - [CHAR*]
這種形式隻能出現在SET2中, 表示拷貝CHAR直到SET2和SET1的長度相等 - [CHAR*REPEAT]
REPEAT個CHAR - [:alnum:]
所有的字母和數字 - [:alpha:]
所有的字母 - [=CHAR=]
和CHAR屬於同一個字符類中的所有字符
當提供2個字符集合時, 表示把SET1中的字符替換成SET2中的對應的字符, 比如:
tr a A < file # 把文件file中的小寫a都變成大寫A tr '[:lower:]' '[:upper:]' < file # 把文件file全部大寫
-
-d, –delete
刪除出現在集合1中的所有字符. 下麵的命令把文件file中所有的行連成一行:tr -d "\r\n" < file
-
-s, –squeeze-repeats
把SET1中連續的字符都替換成1個字符, 當SET2也提供時, 首先執行刪除連續字符操作, 然後才執行替換操作
-
-c, -C, –complement
使用SET1的補集
-
-t, –truncate-set1
首先把SET1中的字符截斷到和SET2長度相等
-
陷阱
經常見到有同學對會含有中文的文件用上麵的方法進行大小寫轉換:# 終端編碼為GB18030編碼 $ tr '[:upper:]' '[:lower:]' <<< 琄 琸
為什麼琄會變成琸呢?
上麵我們說到, tr是按字節來處理的, 而GB18030編碼第二個字節編碼範圍為0×40-0×7E和0×80-0×FE, 這樣, 第二個字節就可能出現ASCII碼, 我們來看下上麵2個漢字的GB18030編碼值:$ od -c <<< 琄 0000000 254 K \n 0000003 $ od -t x1 <<< 琄 0000000 ac 4b 0a 0000003 $ od -c <<< 琸 0000000 254 k \n 0000003 $ od -t x1 <<< 琸 0000000 ac 6b 0a 0000003
看來確實如此, 琄的第二個字節是字符大K, 琸的第二個字節是字符小k.
看來, 如果文本裏含有多字節字符, 使用tr的時候得小心咯.
8.2 expand
每個編輯器對TAB的顯示設置不一樣, 有的顯示為8個字符, 有的顯示為4個字符, 這樣就造成了在A編輯器下排版很漂亮, 到了B編輯器下變得一團糟, 所以編碼的時候最好使用空白字符代替TAB(Emacs中這樣設置: (setq-default indent-tabs-mode nil), ),
expand命令也可以幫你把TAB轉換成空格
-
-i, –initial
不轉換非空白字符後的TAB
-
-t, –tabs=NUMBER
設置一個TAB轉換成幾個空格, 默認是8
8.3 unexpand
Makefile縮進的時候, 必須是TAB, 所以有時候又需要把空格變成TAB, 就靠unexpand了
-
-a, –all
轉換所有的空格, 默認情況下隻轉換開頭的空格
-
–first-only
隻轉換開頭的空格
-
-t, –tabs=N
幾個空格轉換成一個TAB, 默認是8
8.4 colrm (COLumn ReMove)
colrm [start [stop]]
該命令隻支持標準輸入, 不支持從文件輸入.
刪除每行從start到stop之間的字符, 如果stop沒有指定的話, 則刪除到末尾. 需要注意的是, TAB被認為占8列(不知道為啥這樣搞)
9 文本切割
9.1 split
切割文本INPUT成文件PREFIXaa, PREFIXab … 默認每個文件1000行, PREFIX為x
split [OPTION]... [INPUT [PREFIX]]
-
-l, –lines=NUMBER
按行切割, 每個輸出文件NUMBER行, 比較常用的切割方式
-
-b, –bytes=SIZE
按字節切分
-
-C, –line-bytes=SIZE
每個文件最多SIZE個自己, 但是每行都完整的保存到一個輸出文件中, 即不像-b那樣, 可能一個整行被拆分到多個文件中去
-
-d, –numeric-suffixes
使用數字做為後綴名
9.2 csplit (context split)
根據模式切割文件, 簡單了解即可
csplit [OPTION]... FILE PATTERN...
10 文本編碼
10.1 iconv
經常會用到, 主要用來轉換編碼
-
–list, -l
列出可以識別的編碼
-
-c
轉換的時候, 忽視無效的字符, 如果沒有加這個選項, iconv碰到這個無效字符會直接報錯退出, 所以最好加上這個選項
10.2 enca
我們經常需要知道文件的編碼, 這個命令幫你搞定
11 文本格式化
打扮一下你的文本吧.
11.1 column
按列漂亮的輸出:
$ (printf "PERM LINKS OWNER GROUP SIZE DAY HH:MM NAME\n"; ls -l | sed 1d) | column -t
PERM LINKS OWNER GROUP SIZE DAY HH:MM NAME drwxr-xr-x 3 taoshanwen taoshanwen 4096 2012-04-03 22:54 ai drwxr-xr-x 26 taoshanwen taoshanwen 4096 2012-04-15 11:59 algorithm drwxr-xr-x 2 taoshanwen taoshanwen 4096 2012-04-09 13:35 arch drwxr-xr-x 5 taoshanwen taoshanwen 4096 2012-04-03 22:47 c-c++ drwxr-xr-x 6 taoshanwen taoshanwen 4096 2012-04-14 20:33 CIP drwxr-xr-x 5 taoshanwen taoshanwen 4096 2012-04-03 22:47 computer-chess drwxr-xr-x 2 taoshanwen taoshanwen 4096 2012-04-15 00:23 computer-go drwxr-xr-x 3 taoshanwen taoshanwen 4096 2012-04-10 16:25 database drwxr-xr-x 3 taoshanwen taoshanwen 4096 2012-04-15 00:57 distributed drwxr-xr-x 5 taoshanwen taoshanwen 4096 2012-04-03 22:47 genetic-prog drwxr-xr-x 3 taoshanwen taoshanwen 4096 2012-04-03 22:47 infosec drwxr-xr-x 2 taoshanwen taoshanwen 4096 2011-03-19 20:40 iphone drwxr-xr-x 20 taoshanwen taoshanwen 4096 2012-04-15 00:38 java drwxr-xr-x 94 taoshanwen taoshanwen 16384 2012-04-17 20:01 linux drwxr-xr-x 7 taoshanwen taoshanwen 4096 2012-04-10 19:29 math drwxr-xr-x 2 taoshanwen taoshanwen 4096 2012-04-17 15:37 mysql drwxr-xr-x 2 taoshanwen taoshanwen 4096 2011-10-19 17:04 nosql drwxr-xr-x 11 taoshanwen taoshanwen 4096 2012-04-16 12:54 other drwxr-xr-x 2 taoshanwen taoshanwen 4096 2012-04-07 14:03 perl drwxr-xr-x 3 taoshanwen taoshanwen 4096 2012-04-15 00:18 python drwxr-xr-x 6 taoshanwen taoshanwen 4096 2012-04-03 22:50 ruby drwxr-xr-x 52 taoshanwen taoshanwen 4096 2012-04-15 00:59 search-engine drwxr-xr-x 9 taoshanwen taoshanwen 4096 2012-04-15 00:23 software-engineering drwxr-xr-x 5 taoshanwen taoshanwen 4096 2010-10-11 22:56 svnroot drwxr-xr-x 7 taoshanwen taoshanwen 4096 2012-04-14 20:33 web drwxr-xr-x 66 taoshanwen taoshanwen 12288 2012-04-17 23:47 work
11.2 fold
將一個比較長的文本行輸出進行"折行".
11.3 fmt
將輸入按照指定寬度進行折行, 功能較fold強大些
12 微語言
下麵介紹文本處理中兩個最強大的命令sed和awk, 它們已經具有一些程序設計語言的特征了, 特別是awk, 所以, 我們的腳本中, 放眼望去, 皆是awk阿. 熟練掌握這兩個命令, 你的文本處理功力將會極大的提升阿.
12.1 sed (Stream EDitor)
sed是一個流編輯器, 類似ed(行編輯器, 通過各種命令編輯文件), 它提供了各種替換、刪除的命令, 使得這些編輯操作能自動化起來.
-
工作流程
sed維護2快內存(也可以理解為2個變量, 或者說是2個寄存器), 分別叫做 pattern space 和 hold space, sed對每行輸入執行下麵的循環:- 讀入輸入行, 去掉尾部的換行符, 存入pattern space
- 執行sed命令, 每條sed命令都可以有個地址與它關聯, 這個地址就類似於條件語句, 隻有這個條件語句通過驗證時, 其對應的命令才會執行
- 執行完所有的sed命令後, 如果沒有指定sed的-n選項, 將會打印pattern space的內容, 然後再輸出換行符. 最後繼續讀入下一行, 進行下一次的循環
每次循環開始時, pattern space的內容會被清空, hold space則不會
-
地址格式
sed地址可以具有以下的形式:- NUMBER
指定執行命令的行號, 隻有在這行, 對應的命令才會被執行, 行號從1開始, 另外, 如果沒有指定-i或者-s選項的話, 所有的輸入文件會被當成一個輸入流, 行號就會一直累加的 - FIRST~STEP
在FIRST、FIRST+STEP、FIRST+2*STEP、、FIRST+3*STEP行執行對應的命令 - $
最後一行 -
REGEXP
在匹配上正則REGEXP的行執行對應的命令, 如果REGEXP中含有/, 需要用\轉義 - \%REGEXP%
在匹配上正則REGEXP的行執行對應的命令, %也可以是其他字符, 如果REGEXP中含有%, 需要用\轉義 - /REGEXP/I, \%REGEXP%I
忽略大小寫 - /REGEXP/M, \%REGEXP%M
可以匹配多行, M表示multi-line
如果沒有指定地址的話, 表示所有行對執行命令. 還可以提供2個地址, 指定一個地址範圍, 這2個地址之間以逗號分割, 比如:
ADDRESS1,ADDRESS2
這樣, 第一次匹配上ADDRESS1的行與第一次匹配上ADDRESS2的行之間的所有行都會執行對應的命令.
GNU sed還支持下麵幾種地址範圍:- ADDR1,+N
匹配ADDR1, 以及接下來的N行 - ADDR1,~N
匹配ADDR1, 直到行號為N倍數的行
在地址或者地址範圍後加感歎號(!), 表示取反.
- NUMBER
-
常用命令
由於sed默認會打印pattern space, 所以不加任何命令的話, 就和cat一樣, 打印所有的輸出:$ cat ab ab ab ac ad ae ac ab $ sed "" ab ab ab ac ad ae ac ab
-
d
刪除pattern space, 立即進入下一輪循環.
ls輸出的時候, 第一行有個摘要, 如果不想顯示的話, 這樣:ls -l | sed 1d
-
p
打印pattern space.
輸出文件ab第5到第10行:sed -n 5,10p ab
-
q [EXIT-CODE]
立即以返回碼EXIT-CODE(默認為0)退出sed, 如果沒有加-n選項的話, 當前的模式空間也會打印出來.
如果文件很大的話, 下麵的方法輸出文件ab第5到第10行會快很多:sed -n "5,10p; 10q" ab
-
n
如果沒有加-n的話, 打印模式空間, 然後直接進入下一輪循環.
不打印第18行:sed -n "p; 18n"
-
{ COMMANDS }
一組命令, 這組命令共用同一個地址.
打印第8行:sed -n "8 {p; q}"
-
s/REGEXP/REPLACEMENT/FLAGS
這個命令估計是大家用的最多的命令了. 前麵幾個命令大家不知道的情況下, 可能通過其他命令解決了, 但是這個命令的功能除了awk, 其他的做不了, 而sed比awk更簡潔.
這個命令主要是對pattern space進行替換, 對匹配REGEXP的部分用REPLACEMENT進行替換, 用來分割的/可以由其他字符組成, 比如s:REGEXP:REPLACEMENT:FLAGS. REPLACEMENT可以由原始的字符組成, 也可以由下麵帶有特殊意義的串組成:- &
匹配REGEXP的部分 - \d
d為1-9的數字, \d表示REGEXP中第d個括號匹配的部分, 比如REGEXP為:a(..(..))
pattern space為abcde, 那麼\1為bcde, \2為de
- \L
把REPLACEMENT中的字符變成小寫, 直到遇到\U和\E. 比如:$ cat ab AB AB ac ad ae ac AB $ sed -r 's/(AB)/\L\1YYY/' ab abyyy abyyy ac ad ae ac abyyy
上例中, 本來\1應該是AB, 但是\L把它全變成小寫了. 而且後麵的YYY也變成小寫了.
- \l
把下一個字符變成小寫 - \U
把REPLACEMENT中的字符變成大寫, 直到遇到\L和\E. - \u
把下一個字符變成大寫 - \E
結束\L和\U的作用
s命令後的FLAGS可以由下麵幾種:
- g
s命令默認隻替換第一個匹配, g可以讓它全部替換 - NUMBER
替換第NUMBER個匹配 - p
如果發生了替換, 打印模式空間.
搜索文件ab中xxx並替換成yyy打印出來:sed s/xxx/yyy/p ab
- i, I
正則匹配忽略大小寫
- &
-
y/SOURCE-CHARS/DEST-CHARS/
類似tr命令, 用DEST-CHARS對應的字符替換出現在SOURCE-CHARS中的字符. 和s命令一樣, 分隔符/也可以是其他字符.
-
=
打印行號. 下麵的命令類似grep -n:sed -n '/xxx/ {=; p}'
-
d
-
高級命令
-
h
用pattern space替換hold space
-
H
先在hold space追加換行符, 再往hold space追加pattern space
-
g
用hold space替換pattern space
-
G
先在pattern space追加換行符, 再往pattern space追加hold space
-
x
交換pattern space和hold space的內容
-
D
刪除模式空間的第一行. 如果模式空間中還有內容的話, 開始進入下一輪循環, 但不讀入輸入. 如果沒有內容的話, 讀入輸入並進行下一輪循環.
-
N
追加換行符到pattern space, 並讀入下一行輸入追加到pattern space, 如果已經沒有任何輸入, 直接退出sed, 不再處理任何命令.
-
P
大寫p, 打印pattern space第一行
-
z
清空pattern space
-
h
-
專家命令
-
: LABEL
設定標簽, 類似C語言中設定一個標簽, 然後可以goto之
-
b [LABEL]
跳轉到標簽, 如果沒有提供標簽的話, 直接進入下一輪循環
-
t [LABEL]
這輪循環中, 如果s命令替換成功過, 則跳轉到標簽. 如果沒有提供標簽的話, 直接進入下一輪循環
-
T LABEL
和t LABEL相反, s命令替換失敗, 才跳轉到標簽
-
: LABEL
-
命令選項
-
-n, –quiet, –silent
禁止自動打印pattern space
-
-i[SUFFIX], –in-place[=SUFFIX]
原地編輯文件, 文本修改後會直接影響到輸入文件
-
-r, –regexp-extended
啟用擴展正則, 默認是基礎正則
-
-s, –separate
把每個文件當作單獨的輸入流, 而不是一個輸入流
-
-n, –quiet, –silent
-
給我一個寄存器, 我可以幹好多事
各位看完上麵所說的sed命令後, 是不是覺得sed隻能做一些替換、刪除等操作, 為什麼稱為微語言呢, 我之所以把它歸到微語言這一類是有原因的, 它具有下麵幾個語言的特征:- 有條件判斷能力, sed的地址就是一種條件判斷, 還有標簽命令也是條件判斷
- 有流程控製能力, 標簽命令就可以做到
- 有變量, 雖然很少, 隻有2個, 但是仍然可以幹好多事了, 看下麵的例子
更多的例子參加sed info
-
tac
模擬tacsed -n '1!G; $p; h'
-
為數字字串增加逗號分隔符號, 將1234567變為1,234,567
sed -r ':a; s/(.*[0-9])([0-9]{3})/\1,\2/; ta' <<< 124523536543652
12.2 awk (Alfred V. Aho, Peter J. Weinberger, Brian W. Kernighan.)
awk是文本處理的利器, 前麵那些命令能幹的事它都能搞定.
-
工作流程
awk的工作方式有點類似sed, sed是地址+命令, awk則是pattern+action, pattern是要匹配的模式, action是要執行的命令, pattern可以由下麵幾種形式:- BEGIN
awk程序開始處理輸入時 - END
awk程序結束處理輸入時 - BEGINFILE
awk程序開始處理每個文件時 - ENDFILE
awk程序結束處理每個文件時 - regular expression
- relational expression
關係表達式 - pattern && pattern
- pattern || pattern
- pattern ? pattern : pattern
- (pattern)
- ! pattern
- pattern1, pattern2
action要以大括號括起來, 比如:
awk '0{print} 1{print}' .emacs
- BEGIN
-
內置變量
- ARGC
awk輸入參數的個數, 不包括awk自己 - ARGV
命令行參數 - ARGIND
當前處理的文件在ARGV中的位置, ARGV[ARGIND]表示當前正在處理的文件, 可以通過這個變量來對不同的輸入文件做不同的處理 - FNR (File Number Record)
當前文件的記錄總數 - NR (Number Record)
目前處理的記錄總數 - NF (Number of Field)
當前記錄的字段數
- ARGC
-
網絡編程
awk能開發網絡程序, 你相信嗎?$ cat test.awk #!/usr/bin/awk -f BEGIN { str = "GET /index.html HTTP/1.1\r\nHost: www.baidu.com\r\n\r\n" print str |& "/inet/tcp/0/www.baidu.com/80" "/inet/tcp/0/www.baidu.com/80" |& getline print } $ awk -f test.awk HTTP/1.1 200 OK
-
陷阱
-
tolower/toupper
和tr一樣, 這2個函數也是對字節進行處理
-
判斷元素是否存在
你是否這樣判斷某元素是否存在於某數組:if (a[e] != 2) { ... }
如果輸入很大的話, 過會你就會發現你的awk占了很多內存, 原因就是a[e]的時候, 如果awk發現a中沒有e, 就會把e插入到a中, 這樣一來內存自然越來越大, 正確的判斷方法是:
if (!(e in a)) { ... }
用過python的朋友可能會這樣寫:
if (e not in a) { ... }
很不幸, 沒有這樣的語法, 而且還不報錯, 我猜awk把e not連接成一個字符串了…
-
tolower/toupper
13 語言
- a2p
- s2p
- perl
- python
14 實例
14.1 我的正則會數學
- multi-sort
15 參考文獻
- 相關命令的info及coreutils的info
- 高級Bash腳本編程指南: 文本處理命令
- Survey of Global Regular Expression Print (GREP) Tools
最後更新:2017-04-03 16:59:42