文字處理的金剛鑽 —— sed (GNU/sed)

_葉帥發表於2014-07-27

本文為誰而寫?

  • 想要高效率文字處理操作的程式設計師或非程式設計師
  • 對 sed 一知半解或懞懞懂懂,希望更進一步學習的程式設計師或非程式設計師(我自認為本文是個不錯的起點)
  • 深刻的認識到作為個體的“人”的文字操作效率的重要地位

誰與本文無緣?

  • 那些從七八十年代就開始搗鼓 Unix 的一臉大鬍子禿頂不刷牙不洗臉不洗澡衣服髒了反穿然後再髒了脫下來看看哪面更乾淨一點繼續穿的 hacker 們,你們根本不需要看我這個小屁孩兒寫的東西,簡直浪費生命(當然如果你是其中一個,我 非常 十分 特別 desperately 的希望你能夠用最惡毒的語言給我整點建議,讓我也有機會在若干個十年後一臉大鬍子禿頂不刷牙.......)
  • 從沒用過 shell 的程式設計師,實惠點說,你只能在山腳下望望,sed 中有些東西相當晦澀,如果你連 shell 都沒用過,本文會讓你作嘔,為您的健康著想,請迅速關閉網頁
  • 熱愛 windows ,深深的認可 Microsoft 哲學、喜歡滑鼠點來點去、拖動這個視窗到那邊、聽經典開機鈴聲咚(-)咚(-)咚(\/)咚(-)、看防毒軟體忙得不可開交併且熱衷於藉著給妹紙修復 windows 的機會然後約會的帥鍋們,請繞行。TMD(注1)
  • 認為人的“效率”是個無關痛癢的話題,不值得為“它”做點什麼,嗯,請出門左拐,注意腳下
  • 認為我說的就是一派胡言,或許...你是對的,不過當你羨慕別人高效的文字操作時,請重新評估自己的位置(注2)
  • 對“中文完整敘述一句話後面不帶句號結束的潔癖症患者”敬而遠之的人,請.......給我捐點錢讓我繼續治療吧

最後,任何有信用卡的人,還有那些沒有信用卡的人,都可能成為最上面那欄或次上面那欄中的一個,請對號入座吧(注3)

acknowledgapologize(致歉)

  • 至少到目前為止,圖靈社群還沒有帖子分享關於 sed 的相關積累,這讓我有機可乘
  • 恰巧我最近系統的學習了下 sed ,而且恰巧我看到“圖靈最新書目(2014.07).pdf P41 左下角”即將出版的《Software Design 中文版 03》中將 sed 和 awk 表述為“文字處理的金剛鑽”,恰巧我又是一個從小學到大學經常考試打小抄的屢教不改的慣犯,畢業後再也沒有機會實踐了,如今天賜良緣,所以本文標題可以說是舊病復發也。如果我的帖子讓圖靈即將出版的《Software Design 中文版 03》滯銷的話,絕非我願,在此提前致以我十二萬分的歉意;如果我的帖子能夠使更多的人關注 sed 並且恰巧有能拉動《Software Design 中文版 03》的銷量的話,我就再寫一篇(可能需要好幾篇)關於 awk 的(awk 的東西更多,啊“A”我“W”靠“K”!這個行業裡的那些死老頭子們...)(注4),以達到我不可告人的利益訴求

知識體系基礎

  1. 你需要對 shell 有一定程度的掌握,不只是 cd, ls 這樣的簡單的命令,我說的是 grep, find 這個級別的
  2. 你需要對正規表示式有著紮實的掌握,sed(和 awk)強烈依賴正規表示式,當然,對於通用程式語言中的環視功能是可選的(雖然除了 perl 其它語言對環視的支援也是多多少少半殘),但是至少應該掌握 Unix 中的 BRE 和 ERE ,這樣我們才有的聊。如果覺得有欠缺,強烈推薦 Jeffrey E.F. Friedl 的《Mastering Regular Expressions》和餘晟的《正則指引》(注5)
  3. 最好會用一個牛逼的文字編輯器,例如 GNU/Emacs(注6) 或者 vim

sed 版本

我的 Ubuntu 上 sed 的版本是 GNU sed 4.2.1 ,並不是 POSIX sed ,我認為看這篇帖子的大部分人用的都是 GNU sed ,所以這篇帖子的內容對大部分人都適用。GNU sed 對 POSIX sed 做了一些擴充套件,所以有些 option 和命令對於 POSIX sed 是不可用的,所以,當你用帖子中的內容做實驗時,請事先確認你的 sed 版本(sed --version),如果你不幸用的是 POSIX sed 或是其他別的什麼我都沒聽過的各種奇葩的 GNU/Linux Distribution 上的各種奇葩的 sed 版本,請去搞一份對應的 manual 吧!如果你恰巧用的也是 GNU sed ,那麼這篇帖子就是為你寫的,對,就是你,只有你會看,你好啊!(注7)

強烈建議!!!

  • 瞭解你的輸入!在使用 grep 之前,仔細檢查你的輸入檔案
  • 從測試檔案中出現的小示例開始。在示例上執行你的指令碼並確信能正常工作。重要的是確保指令碼在你不想讓它工作的時候不能工作
  • 做之前要仔細考慮
  • 要實用

心理準備

如果你以前沒有用過 sed ,那我告訴你,本文的餘下部分很可能會讓你喪失一些信心,這是一場不遜於奧馬哈灘頭的硬仗(注8)!不過你得挺住(你一定能的,連我這德性的都挺過來了),挺過來了就有機會向那些不刷牙不洗臉的死老頭子們看齊....

生理準備

別等到口渴了再喝水,要使勁喝水,使勁上廁所(注9)。多吃點核桃這樣不飽和脂肪酸含量高的食物,有助於提高智商(如果吃多了便祕那就再多吃點香蕉)。再有,別吃太飽,否則大腦忙不過來,單核 CPU 的 context 切換的成本很高。彆著急,還沒到上路的時候呢!

上路嘍!

請問 sed : 你是如何看待你自己的?
sed :I'm the special one!!!(注10)

的確,sed 很另類,或者經歷過那個時代的老頭子們看我們也很另類。sed 是 stream editor 的縮寫,與現在廣泛應用的互動式的編輯器不同,它是“非互動式”的、面向字元流的編輯器。我並不確定 sed 這個單詞(嚴謹地說是 s , e, d 這三個字母)怎麼讀,根據我對英語的慣用發音方式的理解和搜尋引擎的幫助,/sed/是一個被廣泛接受的讀法,在此,我遵循大多數人(注11)

sed 用途

  • 在一個或多個檔案上自動實現編輯操作
  • 簡化對多個檔案執行相同的編輯處理工作
  • 編寫轉換程式

sed 的學習障礙

  • 如何使用 sed ,換句話說就是學習使用 sed 的語法,習慣它的工作方式
  • 正則
  • 如何與 shell 互動
  • 編寫 sed 程式的技巧。這個是最難的,你需要經驗,經驗來自於大量充分的實踐,而且實踐就難免會犯錯,當你見過各種大場面、犯過足夠多的錯誤之後,於是乎啥也不說了,你就具備了這個牛力(系統運維流傳這樣一個段子:沒把系統搞死過的運維不是好運維)(注12)

(請確保理解 sed 的工作原理、pattern space 和 hold space ,否則後面會很晦澀)

工作原理

sed 是面向字元流的編輯器,而且考慮到 sed 是“那個硬體坑爹的年代”發明出來的玩意兒,你猜也能猜得到:sed 不會一口氣把一個檔案的全部內容都 load 到記憶體中的(額...GNU/Emacs 就是這麼幹的...實際上 GNU/Emacs 的最早可工作版本是 1985 年初,而 sed 早在 10 年前就出生了,是 Unix V7 最早引入的,肯大爺(注13)還是很有先見之明的!嗯,就這麼勉強理解吧...),sed 的正常工作流程是每次從 file(s) 讀取一行至 pattern space ,然後對 pattern space 中的內容依次執行每一個命令,最後輸出 pattern space 中的內容(所以,sed 不會更改原檔案 file(s) 的,儘管放心“食用”——當然是在你沒有指定 -i option 的情況下)

pattern space

啥是 pattern space? TMD(注14),咋解釋呢?你可以把 pattern space 想象成一個大洗腳盆,sed 把巧克力、醬油和辣椒粉依次(每行文字)倒進去,你負責(各種 sed 命令)加工,隨著汗水、鼻涕、眼淚啥的各種液體一起攪拌(sed 命令執行),最後產生重口味沙拉(輸出),你喜歡這個味道嗎?對,.*(注15),就是這個味兒,美好的事物總是青睞大膽嘗試的人...........

正經點,pattern space 就是一個 sed 負責維護的緩衝區,當應用編輯命令時在那裡儲存單個輸入行,並依次執行每一個命令,當應用了所有命令後,當前行被輸出並把 file(s) 的下一行讀入,迴圈往復。pattern space 中的內容是動態的,任何命令都可以為應用下一個命令而改變 pattern space 的內容。一些命令會改變 sed 的工作流程,彆著急,蛋疼的地兒在這兒呢!

(有些書把 pattern space 翻譯成模式空間,我認為“模式空間”這種譯法並不能幫助我很好的理解這個概念,相反,我為了理解 pattern space ,大腦還需要多一次額外的翻譯,所以,在這我保留了英文的標準稱謂。如果你跟我一樣,喜歡直接的東西,你真幸福,很少有帖子能夠像我這樣照顧到我這樣的讀者,我只能自己滿足自己了...下面的 hold space 同樣保留標準稱謂不翻譯)

hold space

孫行者來了,行者孫也來了,現在又來了個者行孫,TMD ,你讓我說點啥?(注16)你可以把 hold space 理解成與 pattern space 很相似的一個預留(set-aside)緩衝區。pattern space 的內容可以複製到 hold space ,同樣,hold space 的內容也可以複製到 pattern space 。有一組命令專門用於在 pattern space 和 hold space 之間移動資料。hold space 用於臨時儲存,比如你有這麼一個蛋疼...用過的詞就不愛再用了...乳酸的需求:命令 y 用於文字轉換,比如大小寫轉換,但是這個命令會影響整個 pattern space 中的內容,如果想要在輸入行上轉換某個單詞,使用 hold space 就可以完成——簡單點說,輸出要更改單詞的那一行之前的所有行,刪除這些行,將單詞後面的行復制到 hold space ,在 pattern space 中轉換這個單詞,然後將 hold space 中的內容追加到 pattern space 中

(該聊聊 fuck fruit 了)(注17)

命令基本格式

sed [options] 'program' file(s) 

或者

sed [options] -f sed_script file(s)

第一種不解釋了,第二種也很容易理解,是吧?畢竟我寫的這麼清晰......你可以把 sed 的命令寫進一個純文字檔案 sed_script ,然後使用 -f option 引用這段程式來對 file(s) 操作(注18)

sed_script 中可以用下列的語法形式:

address{  
program1  
program2  
program3  
..  
}  

畢竟對一個 address 的操作很多時候是需要多個命令的,而且這樣寫的好處是不必像第一種那樣,每次都得用單引號把 program 括起來(要把 program 頂到一行的開頭寫,否則會出現錯誤)

也可以選擇其他字元作為定界符,例如分號:

sed 's;/home/yeshuai;/home/yes;'   

/home/yeshuai 字串替換成 /home/yes 字串

address

sed 不需要必須指定 address ,如果沒有 address ,sed 就會在 file(s) 的每一行上執行 sed_script 。address 後面緊跟 ! 感嘆號匹配非 address 的行

number 只匹配特定行
first~step 匹配以 first 行起始,step 為遞增量的 address 。例如:sed -n 1~2p file 只會會列印奇數行。first 可以為 0 ,sed 就會認為從第 step 行遞增。GNU 擴充套件
$ 匹配檔案的最後一行
/regexp/ 正則匹配,不解釋了
\cregexpc 同上。c 可以為任意字元
0,addr2 從 file(s) 第 1 行開始,直到匹配到 addr2 的行。這相當於 1,addr2 ,但是有一點區別:0,addr2 會在地址區間的結尾,而 1,addr2 會在地址區間的起始。只有當 addr2 是一個正規表示式時才有效
addr1,+N 匹配從 addr1 的行開始,直到下面的 N 行
addr1,~N 匹配從 addr1 的行開始,直到為 N 的倍數的行

options (有些 options 有短選項,有些沒有,有長選項的我會以逗號分隔顯示)

-n, --quite, --slient 阻止預設輸出。預設情況下,sed 會將 file 的內容每行都輸出,-n 可以抑制預設輸出。通常和 p 命令一起使用,只輸出 pattern space 中改變了的行
-e 'program', --expression='program' 如果你有多個 program 需要執行,可以指定 -e 引數。例如:sed -e 'program1' -e 'program2' -e 'program3'... file(s)
-f sed_script, --file=sed_script 剛解釋過了,就上面那幾行
--follow-symlinks 處理符號連結(沒用過,感覺很牛逼的樣子)
-i[SUFFIX], --in-place=[SUFFIX] 對 file(s) 進行更改。如果提供了 SUFFIX ,則對原檔案 file(s) 先進行備份,例如:sed -i_bak 'program' xxx 會先生成 xxx_bak 這個內容與檔案 xxx 一模一樣的檔案,然後對 xxx 文 件進行更改
-l N, --line-length=N 對“l 命令”(l 命令稍後講解)指定自動換行的字元數
--posix 禁用所有 GNU sed 擴充套件。這樣,你的 sed 就成了 POSIX sed(真慘啊...)
-r, --regexp-extended 使用 ERE 。POSIX sed 只能使用 BRE ,GNU sed 的這個擴充套件很實用,沒那麼多的轉義了
-s, --separate 將 file(s) 是為單個獨立的檔案,而不是將它們視為整個連續的字元流。其實 -s 很符合直覺,只不過 POSIX sed 並不這麼認為
-u, --unbuffered 從 file(s) 載入儘可能少量的資料並且更頻繁的 flush(說實話,這個也不怎麼理解,沒用過)
--help 不解釋了
--version 不解釋了

命令

s (替換命令)

[address1[,address2]]s/pattern/replacement/flag

用 replacement 替代每個定址行的 pattern

flag(通常組合使用)
n :1 ~ 512的一個數字。表示對匹配模式的每行中第 n 次出現的情況進行替換
g :全域性替換。沒有 g 時通常只對第一次的出現進行替換
p :列印 pattern space 的內容
w file :將 pattern space 的內容寫到檔案 file 中

replacement
& :用正規表示式匹配的內容進行替換
\n :匹配第 n 個分組子串。
\ :當在替換部分包含 & 符號時,反斜槓 “\” 和替換命令的定界符可用 \ 轉義它們。另外,它用於轉義換行符並建立多行 replacement 字串(除了 正規表示式 可以有元字元外,replacement 部分也可以有元字元)

replacement 元字元
\ :反斜槓一般用於轉義其它元字元 例如:用換行符取代每行上的第二個製表符(注意:製表符用 * 演示):(反斜槓後面不允許有空格)
sed 's/*/\
/2'

& :同 replacement 節的 & ,用於引用分組,要想輸出 & 字元的字面值,用反斜線轉義
\n :引用分組(最多 9 個)

sed 還會記得最後一個正規表示式:
sed 's/foo/bar/3' 替換第三個 foo
sed 's//quux/' 再替換第一個 foo

# (註釋命令)
與 ruby 的單行註釋非常像,GNU sed 可以將註釋放到任何地方,但是最好放在單獨的一行

d (刪除命令)

[address1[,address2]]d

從 pattern space 中刪除行。因此行沒有被傳遞到標準輸出。一個新的輸入行被讀入,並用第一個 program 來編輯

D (多行刪除命令)

[address1[,address2]]D

刪除由命令 N 建立的多行 pattern space 中的第一部分(直到嵌入的換行符),並且用 sed_script 的第一個 program 來編輯。如果這個命令使 pattern space 為空,那麼將讀取一個新的輸入行,與 d 命令一樣

a (追加命令)

[address]a\
text

在與 address 匹配的每行後面追加 text 。如果 text 多於一行,必須用反斜槓將這些行前面的換行符隱藏起來。text 將被沒有用這種方法隱藏起來的第一個換行符結束。text 在 pattern space 中不可用並且後續 program 不能應用於它。當所有 program 執行完後,追加命令的結果將被送到標準輸出,而不管在 pattern space 中的當前行發生了什麼

i (插入命令)

[address]i\
text

將 text 插入到每個和 address 匹配的行的前面。其它行為與 a 命令相同

c (更改命令)

[address1[,address2]]c\
text

用 text 來替代(改變)由地址選定的行。當指定的是一個行範圍時,將所有這些行作為一個組並用 text 取代。每個 text 的行後面的換行符必須用反斜槓將其轉義,最後一行除外。實際上,pattern space 中的內容將被刪除,後續的 program 不能應用於 text

l (列表命令)

[address1[,address2]]l

列出 pattern space 中的內容,將不可列印字元表示為 ASCII 碼。長的行將被折行

l width (列表命令)GNU 擴充套件

[address1[,address2]]l width

列出 pattern space 中的內容,將不可列印字元表示為 ASCII 碼。長的行以 width 規定的字元數折行

y (轉換命令)

[address1[,address2]]y/abc/xyz

按位置將字串 abc 中的字元轉換成字串 xyz 的相應位置替換根據字元的位置來進行。因此,它沒有“詞”的概念,這樣,在改行上的任何 a 都被替換成了 x ,而不管 a 後面是不是 b 。這個命令的一個可能的用途就是大小寫字母的轉換 y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/

p (列印命令)

[address1[,address2]]p

列印所定址的行。注意,這將導致重複的輸出,除非指定了 -n option 。它既不清除 pattern space 中的內容,也不改變 sed_script 中的控制流。它頻繁的用在改變控制流的命令(d, N, b)之前。通常這樣使用:sed -n '/pattern/replacement/p'

= (列印行號命令)

[line-address]=

這個命令不能應用與地址範圍,只能作用於單行。

n (next 命令)

[address1[,address2]]n

首先輸出 pattern space 的內容,然後讀取下一行,繼續執行 sed_script 中餘下的 program ,說白了,就是 sed_script 中剩餘的命令對替換後的行繼續執行

r (讀命令)

[line-address]r file

讀取 file 的內容並追加到 pattern space 的後面。後續的命令不會對新讀入的行起作用

R (讀命令)GNU 擴充套件

[line-address]R file

每次從 file 讀取一行內容並追加到 pattern space 的後面。後續的命令不會對新讀入的行起作用

w (寫命令)

[address1[,address2]]w file

將 pattern space 中內容追加到 file 。如果 file 不存在,將直接建立 file 。如果 file 存在,則每次 sed_script 執行時都會改寫 file 的內容。在一個 sed_script 內,多次 w file 會追加內容至 file

W (寫命令)GNU 擴充套件

[address1[,address2]]W file

只將 pattern space 中的第一行內容追加到 file 。如果 file 不存在,將直接建立 file 。如果 file 存在,則每次 sed_script 執行時都會改寫 file 的內容。在一個 sed_script 內,多次 w file 會追加內容至 file

q (退出命令)

[line-address]q

當遇到 line-address 時退出,sed_script 終止執行。定址的行首先被寫到輸出(如果沒有 -n option),包括前面的 a 命令和 r 命令為它追加的文字

Q (退出命令)GNU 擴充套件

[line-address]Q

當遇到 line-address 時退出,sed_script 終止執行。

N (多行 next 命令)

[address1[,address2]]N

將下一個輸入行的內容追加到 pattern space 之後,追加的行與 pattern space 中的行用換行符分割。這個命令用於實現跨兩行的模式匹配。這個換行符可以用 \n 來匹配。

D (多行刪除)

[address1[,address2]]D

刪除由 N 命令建立的多行 pattern space 中的第一部分(直到換行符為止,換行符也一起刪除)。並且用 sed_script 繼續編輯。如果這個命令使 pattern space 為空,則繼續讀取下一個輸入行,和執行了 d 命令一樣

P (多行列印)

[address1[,address2]]P

列印有 N 命令建立的 pattern space 的第一部分(直到第一行為止)。如果沒有將 N 用於某一行則與 p 相同

hold space 的內容到了....

h 將當前 pattern space 中的內容複製到 hold space ,hold space 原來的內容被清除
H 將一個換行符 + pattern space 的內容追加到 hold space ,即使 hold space 為空,H 命令也會新增換行符

g 將 hold space 中的內容複製到 pattern space ,pattern space 中原來的內容被清除
G 將行符 + hold space 中的內容追加到 pattern space ,如果 hold space 的內容為空,則則相當於只追加了一個換行符

x

[address1[,address2]]x

交換 pattern space 和 hold space 的內容

來吧,看看乳酸的那個需求吧!

$ cat > U
find the Match statement
Consult the Get statement
using the Read statement to retrieve the data

$ cat > U.sed
/the .*statement/{
h
s/.*the \(.*\)statement.*/\1/
y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
G
s/\(.*\)\n\(.*the \).*\(statement.*\)/\2\1\3/
}

$ sed -f U.sed U
find the MATCH statement
Consult the GET statement
using the READ statement to retrieve the data

不解釋了,慢慢讀吧

:lable (標籤命令)
定義一個分支使用 :mylable

b (分支命令)

[address]b[mylable]

如果沒有指定 mylable ,則跳轉到 sed_script 結尾。否則無條件的跳轉到 mylable 處

(肯定執行 cmd1 和 cmd2)
:top
cmd1
cmd2
/pattern/b top
cmd3

很像一個迴圈吧?還有下面:
(肯定執行 cmd1 和 cmd3)
cmd1
/pattern/b end
cmd2
:end
cmd3

(cmd2 或 cmd3 執行一個)
cmd1
/pattern/b dottree
cmd2
b
:dottre
cmd3

t (測試命令)

[address]t[mylable]

(其實 t 很像 shell 的 case 語句)通常和 s 命令一起使用,當 s 命令成功執行替換後,t 命令才會起作用

/pattern/{
cmd1
t mylable
cmd ...
cmd ...
cmd ...
:mylable
cmd ...
cmd ...
}

T (測試命令)GNU 擴充套件

[address]T[mylable]

與 t 命令正好相反,如果 s 命令沒有執行替換,T 命令才會起作用

/pattern/{
cmd1
T mylable
cmd ...
cmd ...
cmd ...
:mylable
cmd ...
cmd ...
}  

未完待續...

  • 沒有解釋 BRE 和 ERE 的知識,本文假設讀者已經具備相關知識
  • 沒有相關的程式碼片段。說實話,太長了,而且如果為了講解知識點硬憋幾段文字出來,恐怕讀者認為 sed 的能力不過如此(注19)
  • 有些需要 sed 費盡九牛二虎之力才能解決的問題只需要 awk 一行就搞定,其實我不想寫 awk 的東西了,攔不住嘴賤...no zuo no die...改天我一定蛋定的寫一篇
  • 本文一定有錯誤,針對錯誤,我也不想掩飾,該罵就罵,你不罵,我下回寫的還一定有錯誤;你罵了,估計也還是有錯誤,但是你罵完舒服了,我也沒啥事,這才是我所關注的

  1. 是指“挺美的” —— 《嗨翻 C 語言》
  2. 來自於給我影響最大的一本書 —— Andy Hunt 和 Dave Thomas 的《The Pragmatic Programmer》中 chapter3.16小節 —— 強力編輯
  3. 來自 O'Reilly《Head First》系列圖書
  4. POSIX awk 的作者 Alfred Aho, Peter Weinberger 和 Brian Kernighan
  5. 《正則指引》的作者餘晟正是《Mastering Regular Expressions》的譯者
  6. 事實上本文就是在 GNU/Emacs 中完成的,然後 copy 到圖靈社群 markdown 排版的
  7. 句式來自於我非常喜歡的一名程式設計師 Steve Yagge 的一篇部落格《巴別塔》,如果你願意的話,可以花點錢把他的書《程式設計師的吶喊》(說白了就是他的部落格精選集)買來看看,不願意的話,我在這裡給一個這篇部落格的連結(不保證永久有效):http://www.oschina.net/news/28301/tour-de-babel
  8. 諾曼底登陸最慘烈的一戰
  9. 同樣來自 O'Reilly《Head First》系列
  10. 04/05 賽季穆里尼奧登陸史丹佛橋時首次新聞釋出會的自我評價。隨後,他帶領切爾西獲得聯賽三連冠。不過後來 07/08 賽季伊始被老闆阿布炒魷魚,弗格森爵士率領曼聯重奪聯賽冠軍,並於那個賽季的歐洲冠軍盃決賽點球戰勝切爾西奪得冠軍,C羅獲得職業生涯的第一個金球獎和第一座世界足球先生獎盃
  11. 如果你因為錯誤的發音導致被某些人嘲笑甚至重大的利益損失,我在這裡鄭重宣告我不負任何責任
  12. 如果你是一名運維,確保這句話別讓你的老闆看到
  13. Unix 作者 Ken Thompsen
  14. 是指“甜蜜的” —— 《嗨翻 C 語言》
  15. 此處 .* 為正規表示式:匹配除換行符外的 0 個或多個任意字元。為了避免廣告嫌疑,請讀者自行想象
  16. 是指“塔瑪德” —— 自創人名(靈感來自於 勞倫斯·阿爾瑪-塔德瑪 爵士 —— 英國維多利亞時代的知名畫家,他的作品以豪華描繪古代世界(中世紀前)而聞名)。如果你覺得名字有點兒怪怪的(其實你不應該感覺到怪,你應該知道本屆世界盃巴西的頭號球星 Neymar 吧?),可以把這句話理解成“元芳,你怎麼看?”
  17. 有些商家居然把乾貨翻譯成 fuck fruit!!!真是莫名其妙!這實在是太業餘的行為了
  18. 我個人喜歡把 sed_script 的檔名起成 xxx.sed 的格式,讓人一眼就能看明白,至於你,隨便,我管不著
  19. 其實就是懶

相關文章