grepping、awking、sedding 和 pipeline 是任何類 Unix 作業系統的使用者的日常例程,可能是在命令列上,也可能是在 shell 腳本中(統稱為過濾器今後)。
從本質上講,當使用“標準”Unix CLI 程式和 shell 內建程式(統稱為命令從現在開始),過濾器在每個過濾器步驟中都需要精確的 stdin、stdout 和 stderr 預期格式才能正常運作。下面我將某些命令的這種精確預期格式稱為該命令的 API。
作為一個有Web開發背景的人,我將這種資料收集和資料處理在技術上與網頁抓取- 只要數據表示有最輕微的變化,這種技術就非常不穩定。
我現在的問題與 Unix 指令 API 的穩定性有關。
- 類 Unix 作業系統中的命令在輸入和輸出方面是否遵循正式的標準化?
- 歷史上是否存在對某些重要命令的更新導致破壞使用該命令的舊版本構建的某些過濾器的功能的情況?
- Unix 命令是否隨著時間的推移而成熟,絕對不可能以可能破壞某些過濾器的方式進行更改?
- 如果過濾器可能因命令 API 的變更而有時會損壞,作為開發人員,我該如何保護我的過濾器免受此問題的影響?
答案1
POSIX 2008 標準有一個部分描述“殼牌和公用事業”。一般來說,如果你堅持這一點,你的腳本應該是相當面向未來的,除了可能的棄用之外,但這些幾乎不會在一夜之間發生,所以你應該有足夠的時間來更新你的腳本。
在某些情況下,單一實用程式的輸出格式在不同平台和版本之間差異很大,POSIX 標準可能包括一個通常稱為-p
或的選項-P
,該選項指定保證且可預測的輸出格式。這方面的一個例子是time
公用事業,其實現差異很大。如果您需要穩定的 API/輸出格式,您可以使用time -p
.
如果您需要使用 POSIX 標準未涵蓋的過濾器實用程序,那麼您幾乎會受到發行版打包者/上游開發人員的擺佈,就像您在進行網頁抓取時受到遠端 Web 開發人員的擺佈一樣。
答案2
我會嘗試根據我的經驗來回答。
命令並不真正遵守正式規範,但它們確實遵守使用和產生面向行文本的要求。
是的當然。在 GNU 實用程式成為事實上的標準之前,許多供應商都會有奇怪的輸出,特別是在
ps
和 方面ls
。這造成了很多痛苦。如今,只有 HP 提供超級古怪的命令。從歷史上看,Berkeley Software Distribution (BSD) 實用程式是與過去的重大突破。 POSIX 規範與過去決裂,但現在已被廣泛接受。隨著時間的推移,Unix 指令確實已經成熟。破壞一些為舊版本編寫的腳本仍然不是不可能的。想想 UTF-8 作為文字檔案編碼的最新趨勢。此更改需要更改基本實用程序,例如
tr
.過去,簡單文字幾乎總是 ASCII(或接近的字元),因此大寫字母形成數字範圍,小寫字母也是如此。對於 UTF-8 來說,情況不再如此,因此tr
可以接受不同的命令列選項來指定「大寫」或「字母數字」等內容。「加固」過濾器的最佳方法之一是不依賴特定的文字佈局。例如,不要執行
cut -c10-24
,這取決於行的位置。改為使用cut -f2
,這會刪除第二個製表符分隔的欄位。awk
將任何輸入行分成 $1、$2、$3...,預設以空格分隔。依賴諸如“字段”之類的高級概念,而不是諸如列位置之類的低級概念。另外,使用正規表示式:sed
並且awk
都可以使用正規表示式執行不關心輸入變化的操作。另一個技巧是將輸入處理為過濾器可以挑剔的格式。用於tr -cs '[a-zA-z0-9]' '[\n]'
將文字分成每行一個單詞,不帶標點符號。在這種情況下,您只是不關心輸入文字是什麼樣的。
答案3
首先,非常簡短地回答您的問題:
- 輸入/輸出約定的正式標準化:不
- 過去因輸出變化而造成的損壞:是的
- 絕對不可能打破未來的過濾器:不
- 我如何保護自己免受改變的影響:保守一點
當您說“API”時,您使用的術語(無論好壞)意味著過濾器輸入/輸出約定過於正式。非常廣泛地(我的意思是“非常”),易於過濾的數據的主要約定是
- 每個輸入行都是一個完整的記錄
- 在每個記錄中,欄位由已知的分隔符號分隔
一個典型的例子是 /etc/passwd 的格式。但是,這些預設約定在某種程度上被違反的情況可能比嚴格遵守的情況要多。
- 有許多過濾器(通常用 awk 或 perl 寫)可以解析多行輸入格式。
- 有許多輸入模式(例如,/var/log/messages)沒有明確定義的欄位結構,並且必須使用更通用的基於正規表示式的技術。
您的第四個問題,如何保護自己免受輸出結構變化的影響,實際上是您唯一可以做的問題。
- 作為@jw013 說,看看posix標準是怎麼說的。當然,posix 並沒有指定您想要用作輸入來源的所有命令。
- 如果您希望您的腳本是可移植的,請盡量避免您碰巧安裝的某個命令的任何版本的特性。例如,標準 unix 命令的許多 GNU 版本都有非標準擴充。這些可能很有用,但如果您想要最大的可移植性,則應該避免使用它們。
- 嘗試了解哪些命令參數子集和輸出格式在跨平台上趨於穩定。不幸的是,這需要隨著時間的推移訪問多個平台,因為這些差異不會被記錄在任何地方,即使是非正式的。
最後,您無法完全保護自己免受您擔心的問題的影響,並且沒有一個地方可以尋找某個命令應該做什麼的「明確」聲明。對於許多 shell 腳本,尤其是那些為個人或小規模使用而編寫的腳本,這根本不是問題
答案4
只有事實上的 IO 標準——空格和 null 分隔的輸出。
至於相容性,我們通常會轉而檢查各個過濾器的版本號碼。並不是說它們改變了太多,而是當您想要使用全新功能並且仍然希望腳本在舊版本上運行時,您必須以某種方式“ifdef”它。除了手動編寫測試案例之外,實際上沒有能力報告機制。