sed
BSD / macOS Sed vs. GNU Sed vs. POSIX Sed specifikationen
Sök…
Introduktion
För att citera från @ SnoringFrogs begäran om ämnesskapande:
"En av de största gotcherna som använder sed är skript som misslyckas (eller lyckas på ett oväntat sätt) eftersom de var skrivna för det ena och inte det andra. Enkel nedläggning av de större skillnaderna skulle vara bra."
Anmärkningar
macOS använder BSD- versionen av sed
[1] , som i många avseenden skiljer sig från GNU- sed
versionen som levereras med Linux- distros.
Deras gemensamma nämnare är den funktionalitet som POSIX föreskriver : se POSIX sed
specifikationen.
Det mest bärbara tillvägagångssättet är att endast använda POSIX-funktioner , vilket dock begränsar funktionaliteten :
Notera att POSIX specificerar stöd endast för grundläggande reguljära uttryck , som har många begränsningar (t.ex. inget stöd för
|
(växling) alls, inget direkt stöd för+
och?
) Och olika undantagskrav.- Varning: GNU
sed
(utan-r
), stöder\|
,\+
och\?
, som INTE är POSIX-kompatibel; använd--posix
att inaktivera (se nedan).
- Varning: GNU
Så här använder du bara POSIX-funktioner :
(båda versionerna): använd bara alternativen
-n
och-e
(notera, använd inte-E
eller-r
att aktivera support för utökade reguljära uttryck)GNU
sed
: lägg till alternativ--posix
att säkerställa POSIX-funktionalitet (du behöver inte strikt detta, men utan det kan du avslutas oavsiktligt använda funktioner som inte är POSIX utan att märka; förbehåll :--posix
själva--posix
är inte POSIX-kompatibel )Att använda endast POSIX-funktioner innebär skärpta formateringskrav (går förbi många bekvämligheter tillgängliga i GNU-
sed
):- Kontrollkaraktersekvenser som
\n
och\t
stöds vanligtvis INTE. - Etiketter och grenkommandon (t.ex.
b
) måste följas av en faktisk ny linje eller fortsättning via ett separat-e
alternativ. - Se nedan för mer information.
- Kontrollkaraktersekvenser som
Båda versionerna implementerar dock tillägg till POSIX-standarden:
- vilka tillägg de implementerar skiljer sig (GNU
sed
implementerar mer). - även de tillägg som de båda implementerar skiljer sig delvis i syntax .
Om du behöver stödja BÅDE plattformar (diskussion om skillnader):
Oförenliga funktioner:
Användning av alternativet
-i
utan argument (uppdatering på plats utan säkerhetskopiering) är oförenligt:- BSD
sed
: MÅSTE använda-i ''
- GNU
sed
: MÅSTE använda bara-i
(motsvarande:-i''
) - att använda-i ''
fungerar INTE.
- BSD
-i
aktiverar på ett förnuftigt sätt linjenummering per-input-fil i GNU-sed
och senaste versioner av BSD-sed
(t.ex. på FreeBSD 10), men gör INTE på macOS från och med 10.12 .
Observera att i avsaknad av-i
alla versioner numrerar kumulativt över inputfiler.Om den sista inmatningsraden inte har en efterföljande ny linje (och skrivs ut):
- BSD
sed
: lägger alltid till en ny linje på utgången, även om ingångslinjen inte slutar i en. - GNU
sed
: bevarar statusen för efterföljande ny linje , dvs. den lägger till en ny linje endast om inmatningsraden slutade på en.
- BSD
Gemensamma funktioner:
- Om du begränsar dina
sed
skript till vad BSD-sed
stöder fungerar de i allmänhet också i GNU-sed
- med det anmärkningsvärda undantaget att använda plattformspecifika utvidgade regex-funktioner med-E
. Uppenbarligen kommer du också att avstå från tillägg som är specifika för GNU-versionen. Se nästa avsnitt.
- Om du begränsar dina
Riktlinjer för plattformsstöd (OS X / BSD, Linux), drivna av de strängare kraven i BSD-versionen :
Observera att korthåren macOS och Linux ibland används nedan för att hänvisa till BSD- och GNU-versionerna av sed
, eftersom de är aktieversionerna på varje plattform. Det är dock möjligt att installera GNU- sed
på macOS, till exempel med hjälp av Homebrew med brew install gnu-sed
.
Obs : Förutom när -r
och -E
flaggorna används ( utvidgade regexer), betyder instruktionerna nedan att skriva POSIX-kompatibla sed
skript.
För POSIX-överensstämmelse måste du begränsa dig till POSIX BRE ( grundläggande reguljära uttryck) , som tyvärr, som namnet antyder, är helt grundläggande.
Varning : antar inte att\|
,\+
och\?
stöds: Medan GNU-sed
stöder dem (såvida inte--posix
används), gör BSD-sed
inte det - dessa funktioner är inte POSIX-kompatibla.
Medan\+
och\?
kan emuleras på POSIX-kompatibelt sätt:
\{1,\}
för\+
,
\{0,1\}
för\?
,
\|
(växling) kan tyvärr inte.För mer kraftfulla reguljära uttryck, använd
-E
(snarare än-r
) för att stödja ERE: er ( utvidgade reguljära uttryck) (GNU-sed
dokumenterar inte-E
, men det fungerar där som ett alias av-r
; nyare version av BSD-sed
, som på FreeBSD 10, stöder nu också-r
, men macOS-versionen från och med 10.12 gör det inte ).
Varning : Även om användning av-r
/-E
innebär att ditt kommando per definition inte är POSIX-kompatibelt, måste du fortfarande begränsa dig till POSIX ERE (utvidgade regelbundna uttryck) . Tyvärr betyder detta att du inte kommer att kunna använda flera användbara konstruktioner, särskilt:- ordbegränsande påståenden, eftersom de är plattformspecifika (t.ex.
\<
på Linux,[[:<]]
på OS X). - bakreferenser i reguljära uttryck (i motsats till "bakreferenser" för att fånga gruppmatchningar i ersättningssträngen för
s
funktionssamtal), eftersom BSD-sed
inte stöder dem i utökade regexer (men underligt nog gör det i grundläggande , där de är POSIX-mandat).
- ordbegränsande påståenden, eftersom de är plattformspecifika (t.ex.
Escape-sekvenser med kontrolltecken som
\n
och\t
:I regexer (både i mönster för radval och det första argumentet till
s
funktionen) antar du att endast\n
känns igen som en flygsekvens (sällan används, eftersom mönsterutrymmet vanligtvis är en enda rad (utan att avsluta\n
), men inte inuti en karaktärsklass , så att t.ex.[^\n]
inte fungerar; (om din ingång inte innehåller några kontrollkar. annat än\t
, kan du emulera[^\n]
med[[:print:][:blank:]]
; annars dela kontrollfält i som bokstäver [2] ) - inkluderar i allmänhet kontrolltecken som bokstäver , antingen via skarvade ANSI C-citerade strängar (t.ex.$'\t'
) i skal som stöder det (bash,
ksh,zsh
) eller via kommandosubstitutioner medprintf
(t.ex."$(printf '\t')"
) .- Endast Linux:
sed 's/\t/-/' <<<$'a\tb' # -> 'a-b'
- OSX och Linux:
sed 's/'$'\t''/-/' <<<$'a\tb' # ANSI C-quoted string
sed 's/'"$(printf '\t')"'/-/' <<<$'a\tb' # command subst. with printf
- Endast Linux:
I ersättningssträngar som används med
s
kommandot antar du att INGEN kontroll-karaktärsutrymningssekvenser stöds , så, återigen, inkludera kontrollkar. som bokstäver , som ovan.- Endast Linux:
sed 's/-/\t/' <<<$'ab' # -> 'a<tab>b'
- macOS och Linux:
sed 's/-/'$'\t''/' <<<'a-b'
sed 's/-/'"$(printf '\t')"'/' <<<'a-b'
- Endast Linux:
Gör för textargumenten till
i
ocha
funktion : använd inte sekvenser för kontrolltecken - se nedan.
Etiketter och förgrenings: etiketter såväl som etiketten-namn argumentet till
b
ocht
funktioner måste följas av antingen av en bokstavlig newline eller en splitsad-i$'\n'
. Alternativt kan du använda flera-e
alternativ och avsluta varje rätt efter etikettnamnet.- Endast Linux:
sed -n '/a/ bLBL; d; :LBL p' <<<$'a\nb' # -> 'a'
- macOS och Linux:
- ELLER (faktiska nyheter):
sed -n '/a/ bLBL d; :LBL p' <<<$'a\nb'
- ELLER (splitsade
$\n
instanser):
sed -n '/a/ bLBL'$'\n''d; :LBL'$'\n''p' <<<$'a\nb'
- ELLER (flera
-e
alternativ):
sed -n -e '/a/ bLBL' -e 'd; :LBL' -e 'p' <<<$'a\nb'
- ELLER (faktiska nyheter):
- Endast Linux:
Funktioner
i
ocha
för att infoga / lägga till text : följ funktionsnamnet med\
, följt antingen av en bokstavlig nyrad eller en inskriven$'\n'
innan du anger textargumentet.- Endast Linux:
sed '1 i new first line' <<<$'a\nb' # -> 'new first line<nl>a<nl>b'
- OSX och Linux:
sed -e '1 i\'$'\n''new first line' <<<$'a\nb'
- Notera:
- Utan
-e
är textargumentet oförklarligt inte nylinjeslutet vid utdata på macOS (bug?). - Använd inte kontrollteckenflugter som
\n
och\t
i textargumentet, eftersom de endast stöds på Linux. - Om textargumentet därför har faktiska inre nyheter,
\
-bild av dem. - Om du vill placera ytterligare kommandon efter textargumentet måste du avsluta det med en (oöverskådad) ny linje (vare sig bokstavlig eller skarvad i), eller fortsätta med ett separat
-e
alternativ (detta är ett allmänt krav som gäller alla versioner) .
- Utan
- Endast Linux:
Inside funktionslistor (flera funktionsanrop innesluten i
{...}
), se till att också avsluta den sista funktionen, före den avslutande}
, med;
.- Endast Linux:
-
sed -n '1 {p;q}' <<<$'a\nb' # -> 'a'
-
- macOS och Linux:
-
sed -n '1 {p;q;}' <<<$'a\nb'
-
- Endast Linux:
GNU- sed
specifika funktioner saknas helt från BSD- sed
:
GNU-funktioner du kommer att missa om du behöver stödja båda plattformarna:
Olika regex-matchning och substitutionsalternativ (båda i mönster för radval och det första argumentet till
s
funktionen):- Den
I
alternativet för skiftlägesokänslig regex matchning (otroligt, BSDsed
stöder inte detta alls). - Alternativet
M
för matchning med flera linjer (där^
/$
matchar start / slut på varje rad ) - För ytterligare alternativ som är specifika för
s
funktionen, se https://www.gnu.org/software/sed/manual/sed.html#The-_0022s_0022-Command
- Den
Escape-sekvenser
Substitutionrelaterade flyktningssekvenser som
\u
i ersättningsargumentet för funktionens///
som möjliggör substringmanipulation , inom gränser; t.ex.sed 's/^./\u&/' <<<'dog' # -> 'Dog'
- se http://www.gnu.org/software/sed/manual/sed.html#The-_0022s_0022 -KommandoEscape-sekvenser med kontrolltecken: utöver
\n
,\t
, ..., codepoint-baserade flykt; till exempel representerar alla följande utrymmen (hex., oktal, decimal) en enda citat ('
):\x27
,\o047
,\d039
- se https://www.gnu.org/software/sed/manual/ sed.html # Escapes
Adressförlängningar , till exempel
first~step
att matcha varje steg-rad,addr, +N
att matcha N-rader efteraddr
, ... - se http://www.gnu.org/software/sed/manual/sed. html # Adresser
[1] MacOS sed
versionen är äldre än versionen på andra BSD-liknande system som FreeBSD och PC-BSD. Tyvärr betyder det att du inte kan anta att funktioner som fungerar i FreeBSD, till exempel, kommer att fungera [samma] på macOS.
[2] Den ANSI C-citerade strängen $'\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'
innehåller alla ASCII-kontrolltecken utom \n
(och NUL), så du kan använda det i kombination med [:print:]
för en ganska robust emulering av [^\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'']
Byt ut alla nya linjer med flikar
Obs: För korthet använder kommandona här-strängar ( <<<
) och ANSI C-citerade strängar ( $'...'
) . Båda dessa skalfunktioner fungerar i bash
, ksh
och zsh
.
# GNU Sed
$ sed ':a;$!{N;ba}; s/\n/\t/g' <<<$'line_1\nline_2\nline_3'
line_1 line_2 line_3
# BSD Sed equivalent (multi-line form)
sed <<<$'line_1\nline_2\nline_3' '
:a
$!{N;ba
}; s/\n/'$'\t''/g'
# BSD Sed equivalent (single-line form, via separate -e options)
sed -e ':a' -e '$!{N;ba' -e '}; s/\n/'$'\t''/g' <<<$'line 1\nline 2\nline 3'
BSD Sed antecknar:
Observera behovet av att avsluta etiketter (
:a
) och förgreningskommandon (ba
) antingen med faktiska nylinjer eller med separata-e
alternativ.Eftersom flyttningssekvenser för kontrolltecken som
\t
inte stöds i ersättningssträngen splitsas en ANSI C-citerad flik bokstavlig i ersättningssträngen.
(I regex- delen känner BSD Sed endast igen\n
som en utrymningssekvens).
Lägg till bokstavlig text på en rad med funktionen 'a'
Obs: För korthet använder kommandona här-strängar ( <<<
) och ANSI C-citerade strängar ( $'...'
) . Båda dessa skalfunktioner fungerar i bash
, ksh
och zsh
.
# GNU Sed
$ sed '1 a appended text' <<<'line 1'
line 1
appended text
# BSD Sed (multi-line form)
sed '1 a\
appended text' <<<'line 1'
# BSD Sed (single-line form via a Bash/Ksh/Zsh ANSI C-quoted string)
sed $'1 a\\\nappended text' <<<'line 1'
Lägg märke till hur BSD Seed kräver en \
följt av en faktisk ny linje för att skicka texten som ska bifogas.
Detsamma gäller för de relaterade funktionerna i
(infoga) och c
(radera och infoga).