BSD macOS Sed 與 GNU Sed 對比 POSIX Sed 規範
**macOS 使用了 sed [1] 的 BSD 版本,它在很多方面都與 Linux 發行版附帶的 GNU sed 版本不同。 ** ****
它們的共同點是 POSIX 規定的功能 :參見 POSIX sed 規範。
在最輕便的方法是使用 POSIX 才有的功能,然而,限制功能 :
-
值得注意的是,POSIX 僅 指定對基本正規表示式的****支援**,這些正規表示式具有許多限制(例如,根本不支援
|(交替),不直接支援+和?)以及不同的轉義要求。- 警告: GNU
sed(無-r),確實支援\|,\+和\?,這是不符合 POSIX 標準; 使用--posix禁用 (見下文)。
- 警告: GNU
-
僅使用 POSIX 功能 :
-
(兩個版本): 僅使用
-n和-e選項(特別是,不要使用-E或-r來啟用對擴充套件正規表示式的支援 ) -
GNU
sed:新增選項--posix,以確保只有 POSIX 的功能(你不嚴格需要這一點,但沒有它,你最終可能會不小心使用了非 POSIX 功能沒有注意到; 警告 :--posix本身是不符合 POSIX 標準) -
僅使用 POSIX 功能意味著更嚴格的格式化要求(放棄 GNU
sed中提供的許多便利):- 通常不支援控制字元序列,例如
\n和\t。 - 標籤和分支命令(例如,
b) 必須後跟實際的換行符或通過單獨的-e選項繼續。 - 請參閱下文了解詳情。
- 通常不支援控制字元序列,例如
-
但是,這兩個版本都實現了 POSIX 標準的擴充套件** :**
- 什麼他們執行的副檔名不同 (GNU
sed實現更多)。 - 甚至那些他們都實現的擴充套件部分語法也不同。
如果你需要支援 BOTH 平臺(討論差異):
-
不相容的功能:
-
使用不帶引數的 **
-i選項 **** (沒有備份的就地更新)是不相容的:- BSD
sed:必須使用-i '' - GNU
sed:必須使用-i(相當於:-i'') - 使用-i ''不起作用。
- BSD
-
-i**** 在 GNUsed和 BSDsed的最新版本 (例如,在 FreeBSD 10 上) 明智地開啟每個輸入檔案行編號,但是從 10.12 開始不在 macOS 上。請注意,在沒有-i的情況下,所有版本的數字都會在輸入檔案中累積**。 ** ****
-
如果最後輸入線並不*具有尾隨換行符* (和列印):
- BSD
sed: 總是在輸出上附加換行符,即使輸入行沒有以一個結尾。 - GNU
sed: 保留尾部換行符狀態,即僅當輸入行以一個結尾時才附加換行符。
- BSD
-
-
共同特徵:
- 如果你將
sed指令碼限制為 BSDsed所支援的指令碼,它們通常也可以在 GNUsed中工作 - 除了使用-E使用特定於平臺的擴充套件正規表示式功能之外。顯然,你還將放棄特定於 GNU 版本的擴充套件。見下一節。
- 如果你將
跨平臺支援指南 (OS X / BSD,Linux),受 BSD 版本更嚴格的要求驅動 :
請注意,下面偶爾使用短序 macOS 和 Linux 來分別引用 sed 的 BSD 和 GNU 版本,因為它們是每個平臺上的庫存版本。但是,可以在 macOS 上安裝 GNU sed,例如,使用帶有 brew install gnu-sed 的 Homebrew 。
注意 : 除非使用 -r 和 -E 標誌 ( 擴充套件正則表示式),否則下面的說明相當於編寫符合 POSIX 標準的 sed 指令碼。
-
對於 POSIX 相容性,你必須限制自己使用 POSIX BRE( 基本正規表示式) ,不幸的是,顧名思義,它非常基本。
警告 :不要認為支援\|,\+和\?:雖然 GNUsed支援它們(除非使用--posix),BSDsed不支援 - 這些功能不符合 POSIX 標準。
雖然\+和\?可以以符合 POSIX 標準的方式進行模擬 :
\{1,\}為\+,
\{0,1\}為\?,但
\|(交替) 不能。 -
對於更強大的正規表示式,使用
-E(而不是-r)來支援 ERE( 擴充套件正規表示式) (GNUsed不記錄-E,但它確實作為-r的別名工作; 更新版本的 BSDsed,例如 on FreeBSD 10,現在也支援-r,但是 10.12 的 macOS 版本沒有 )。
警告 :儘管使用-r/-E意味著你的命令根據定義並不符合 POSIX,但你仍必須限制自己使用 POSIX ERE(擴充套件正規表示式) 。遺憾的是,這意味著你將無法使用幾種有用的結構,特別是:- 字邊界斷言,因為它們是特定於平臺的 (例如,Linux 上的
\<,OS X 上的[[:<]])。 - 正規表示式中的反向引用 (而不是在
s函式呼叫的替換字串中對捕獲組匹配的反向引用),因為 BSDsed在擴充套件正則表示式中不支援它們 (但是,奇怪的是,在基本的,它們是 POSIX 規定的)。
- 字邊界斷言,因為它們是特定於平臺的 (例如,Linux 上的
-
控制字元轉義序列,如
\n和\t:-
在正則表示式 (用於行選擇的模式和
s函式的第一個引數)中,假設只有\n被識別為轉義序列(很少使用,因為模式空間通常是單行 (不終止\n),但不是在一個字元類中,所以,例如,[^\n]不起作用;(如果你的輸入不包含控制字元。除了\t,你可以用[[:print:][:blank:]]模擬[^\n];否則,拼接控制字元作為文字 [2] ) - 通常,將控制字元包含為文字**,或者通過拼接的 ANSI C 引用的字串 (例如,$'\t')支援它(bash,ksh,zsh),或者通過使用printf命令替換 (例如,$(printf '\t'))** 。- 僅限 Linux:
sed 's/\t/-/' <<<$'a\tb' # -> 'a-b' - OSX 和 Linux:
sed 's/'$'\t''/-/' <<<$'a\tb' # ANSI C-quoted string
sed 's/'"$(printf '\t')"'/-/' <<<$'a\tb' # command subst. with printf
- 僅限 Linux:
-
在與
s命令一起使用的替換字串中,假設支援 NO 控制字元轉義序列,因此,再次包括控制字元。作為文字,如上所述。- 僅限 Linux:
sed 's/-/\t/' <<<$'a-b' # -> 'a<tab>b' - macOS 和 Linux:
sed 's/-/'$'\t''/' <<<'a-b'
sed 's/-/'"$(printf '\t')"'/' <<<'a-b'
- 僅限 Linux:
-
同上的文字引數到
i和a功能 : 不使用控制字元序列 -見下文。
-
-
標籤和分支 :標籤以及
b和t函式的 label-name 引數必須後跟文字換行符或拼接的$'\n'。或者,使用多個-e選項並在標籤名稱後面的每個選項終止。- 僅限 Linux:
sed -n '/a/ bLBL; d; :LBL p' <<<$'a\nb' # -> 'a' - macOS 和 Linux:
- (實際換行):
sed -n '/a/ bLBL d; :LBL p' <<<$'a\nb' - 或(拼接
$\n例項):
sed -n '/a/ bLBL'$'\n''d; :LBL'$'\n''p' <<<$'a\nb' - 或(多個
-e選項):
sed -n -e '/a/ bLBL' -e 'd; :LBL' -e 'p' <<<$'a\nb'
- (實際換行):
- 僅限 Linux:
-
函式
i和a用於插入/附加文字 : 按照\的函式名稱, 在指定文字引數之前跟隨文字換行符或拼接的$'\n'** 。- 僅限 Linux:
sed '1 i new first line' <<<$'a\nb' # -> 'new first line<nl>a<nl>b' - OSX 和 Linux:
sed -e '1 i\'$'\n''new first line' <<<$'a\nb' - 注意:
- 如果沒有
-e,text 引數莫名其妙地不會在 macOS 輸出上換行(bug?)。 - 不要在 text 引數中**使用控制字元轉義符,**例如
\n和\t,因為它們僅在 Linux 上受支援。 - 因此,如果 text 引數具有實際的內部換行符,則
\- 轉義它們。 - 如果要在 text 引數後面新增其他命令,則必須使用(非轉義)換行符(無論是文字還是拼接)終止它,或者繼續使用單獨的
-e選項(這是適用於所有版本的一般要求)。
- 如果沒有
- 僅限 Linux:
-
在函式列表內 (
{...}中包含多個函式呼叫),請務必在關閉}之前使用;終止最後一個函式。- 僅限 Linux:
sed -n '1 {p;q}' <<<$'a\nb' # -> 'a'
- macOS 和 Linux:
sed -n '1 {p;q;}' <<<$'a\nb'
- 僅限 Linux:
**** BSD sed 完全缺少 GNU sed 特有的功能 :
如果你需要支援這兩個平臺,你將錯過的 GNU 功能:
-
各種正規表示式匹配和替換選項 (用於行選擇的模式和
s函式的第一個引數):- 用於 case-INsensitive 正規表示式匹配的
I選項(令人難以置信的是,BSDsed根本不支援這一點)。 - 用於多行匹配的
M選項(其中^/$匹配每行的開頭/結尾 ) - 有關
s功能特有的其他選項,請參閱 https://www.gnu.org/software/sed/manual/sed.html#The-_0022s_0022-Command
- 用於 case-INsensitive 正規表示式匹配的
-
轉義序列
-
替換相關的轉義序列,如
\u,在s///函式的替換引數中允許子字串操作,在限制範圍內; 例如,sed 's/^./\u&/' <<<'dog' # -> 'Dog'- 請參閱 http://www.gnu.org/software/sed/manual/sed.html#The-_0022s_0022-Command -
控制字元轉義序列:除了
\n,\t,…,基於程式碼點的轉義; 例如,所有以下轉義(十六進位制,八進位制,十進位制)代表單引號('):\x27,\o047,\d039- 請參閱 https://www.gnu.org/software/sed/manual/sed.html #Escapes
-
-
地址擴充套件,例如
first~step匹配每一步,addr, +N匹配addr之後的 N 行,… - 請參閱 http://www.gnu.org/software/sed/manual/sed.html#Addresses
[1] macOS sed 版本比其他類似 BSD 的系統(如 FreeBSD 和 PC-BSD)的版本舊。不幸的是,這意味著你不能假設在 FreeBSD 中工作的功能在 macOS 上可以[相同]工作。
[2] ANSI C 引用的字串 $'\001\002\003\004\005\006\007\010\011\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\177'包含除\n(和 NUL)之外的所有 ASCII 控制字元,因此你可以將它與 [:print:] 結合使用,以實現非常強大的 [^\n] 模擬:
'[[:print:]'$'\001\002\003\004\005\006\007\010\011\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\177'']