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'']