BSD macOS Sed 與 GNU Sed 對比 POSIX Sed 規範

**macOS 使用了 sed [1]BSD 版本,它在很多方面都與 Linux 發行版附帶的 GNU sed 版本不同。 ** ****

它們的共同點POSIX 規定的功能 :參見 POSIX sed 規範。

最輕便的方法使用 POSIX 才有的功能,然而,限制功能

  • 值得注意的是,POSIX 僅 指定基本正規表示式的****支援**,這些正規表示式具有許多限制(例如,根本不支援|(交替),不直接支援+?)以及不同的轉義要求。

    • 警告: GNU sed(無 -r),確實支援\|\+\?,這是不符合 POSIX 標準; 使用 --posix 禁用 (見下文)。
  • 僅使用 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 ''不起作用。
    • -i **** 在 GNU sedBSD sed最新版本 (例如,在 FreeBSD 10 上) 明智地開啟每個輸入檔案行編號,但是從 10.12 開始不在 macOS 上。請注意,在沒有 -i 的情況下,所有版本的數字都會在輸入檔案中累積**。 ** ****


    • 如果最後輸入線並不*具有尾隨換行符* (和列印):

      • BSD sed總是在輸出上附加換行符,即使輸入行沒有以一個結尾。
      • GNU sed保留尾部換行符狀態,即僅當輸入行以一個結尾時才附加換行符。
  • 共同特徵:

    • 如果你將 sed 指令碼限制為 BSD sed 所支援的指令碼,它們通常也可以在 GNU sed 中工作 - 除了使用 -E 使用特定於平臺的擴充套件正規表示式功能之外。顯然,你還將放棄特定於 GNU 版本的擴充套件。見下一節。

跨平臺支援指南 (OS X / BSD,Linux),受 BSD 版本更嚴格的要求驅動

請注意,下面偶爾使用短序 macOSLinux 來分別引用 sed 的 BSD 和 GNU 版本,因為它們是每個平臺上的庫存版本。但是,可以在 macOS 上安裝 GNU sed,例如,使用帶有 brew install gnu-sedHomebrew

注意除非使用 -r-E 標誌擴充套件正則表示式),否則下面的說明相當於編寫符合 POSIX 標準的 sed 指令碼。

  • 對於 POSIX 相容性,你必須限制自己使用 POSIX BRE( 基本正規表示式) ,不幸的是,顧名思義,它非常基本。
    警告 :不要認為支援\|\+\?:雖然 GNU sed 支援它們(除非使用 --posix),BSD sed 不支援 - 這些功能符合 POSIX 標準。
    雖然 \+\? 可以符合 POSIX 標準的方式進行模擬
    \{1,\}\+
    \{0,1\}\?,但
    \|(交替) 不能

  • 對於更強大的正規表示式,使用 -E (而不是 -r)來支援 ERE( 擴充套件正規表示式) (GNU sed 不記錄 -E,但它確實作為 -r 的別名工作; 更新版本的 BSD sed,例如 on FreeBSD 10,現在也支援 -r,但是 10.12 的 macOS 版本沒有 )。
    警告 :儘管使用 -r / -E 意味著你的命令根據定義並不符合 POSIX,但你仍必須限制自己使用 POSIX ERE(擴充套件正規表示式) 。遺憾的是,這意味著你將無法使用幾種有用的結構,特別是:

    • 字邊界斷言,因為它們是特定平臺的 (例如,Linux 上的\<,OS X 上的 [[:<]])。
    • 正規表示式中的反向引用 (而不是在 s 函式呼叫的替換字串中對捕獲組匹配的反向引用),因為 BSD sed擴充套件正則表示式中不支援它們 (但是,奇怪的是,在基本的,它們是 POSIX 規定的)。
  • 控制字元轉義序列,如\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
    • 在與 s 命令一起使用的替換字串中假設支援 NO 控制字元轉義序列,因此,再次包括控制字元。作為文字,如上所述。

      • 僅限 Linux:
        sed 's/-/\t/' <<<$'a-b' # -> 'a<tab>b'
      • macOS Linux:
        sed 's/-/'$'\t''/' <<<'a-b'
        sed 's/-/'"$(printf '\t')"'/' <<<'a-b'
    • 同上的文字引數ia 功能不使用控制字元序列 -見下文。

  • 標籤和分支 :標籤以及 bt 函式的 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'
  • 函式 ia 用於插入/附加文字按照\的函式名稱, 在指定文字引數之前跟隨文字換行符或拼接的 $'\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:
      • sed -n '1 {p;q}' <<<$'a\nb' # -> 'a'
    • macOS Linux:
      • sed -n '1 {p;q;}' <<<$'a\nb'

**** BSD sed 完全缺少 GNU sed 特有的功能

如果你需要支援這兩個平臺,你將錯過的 GNU 功能:

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