sed
BSD / macOS Sed versus GNU Sed versus de POSIX Sed-specificatie
Zoeken…
Invoering
Om te citeren uit het verzoek van @ SnoringFrog om een onderwerp te maken:
"Een van de grootste problemen met het gebruik van sed zijn scripts die falen (of op een onverwachte manier slagen) omdat ze voor de een en niet voor de ander zijn geschreven. Een eenvoudige afbouw van de grotere verschillen zou goed zijn."
Opmerkingen
macOS gebruikt de BSD- versie van sed
[1] , die in veel opzichten verschilt van de GNU sed
versie die wordt geleverd met Linux- distros.
Hun gemene deler is de functionaliteit bepaald door POSIX : zie de POSIX sed
spec.
De meest draagbare aanpak is om alleen POSIX-functies te gebruiken , wat echter de functionaliteit beperkt :
Met name, POSIX specificeert ondersteunen alleen voor basis reguliere expressies, die veel beperkingen (bijvoorbeeld geen ondersteuning voor
|
(afwisseling) helemaal niet, geen directe ondersteuning voor+
en?
) En de verschillende ontsnappen eisen.- Waarschuwing: GNU
sed
(zonder-r
), ondersteunt\|
,\+
en\?
, die NIET POSIX-compatibel is; gebruik--posix
om uit te schakelen (zie hieronder).
- Waarschuwing: GNU
Alleen POSIX-functies gebruiken :
(beide versies): gebruik alleen de opties
-n
en-e
(met name, gebruik geen-E
of-r
om ondersteuning voor uitgebreide reguliere expressies in te schakelen)GNU
sed
: add optie--posix
POSIX-only functionaliteit te verzekeren (je hoeft niet strikt dit nodig, maar zonder dat je zou kunnen komen, per ongeluk met behulp van niet-POSIX voorzien zonder het te merken; caveat:--posix
zelf wordt niet POSIX-compliant )Het gebruik van alleen POSIX-functies betekent strengere opmaakvereisten (waardoor veel gemakken beschikbaar zijn in GNU
sed
):- Controletekensreeksen zoals
\n
en\t
worden over het algemeen NIET ondersteund. - Labels en vertakkingscommando's (bijv.
b
) moeten worden gevolgd door een daadwerkelijke nieuwe regel of voortzetting via een afzonderlijke optie-e
. - Zie hieronder voor details.
- Controletekensreeksen zoals
Beide versies implementeren echter uitbreidingen van de POSIX-standaard:
- welke extensies ze implementeren verschilt (GNU
sed
implementeert meer). - zelfs die extensies die ze beide implementeren, verschillen gedeeltelijk in syntaxis .
Als u BEIDE platforms moet ondersteunen (bespreking van verschillen):
Incompatibele functies:
Het gebruik van de optie
-i
zonder argument (ter plaatse bijwerken zonder back-up) is niet compatibel:- BSD
sed
: MOET-i ''
- GNU
sed
: MOET gewoon-i
(equivalent:-i''
) - het gebruik van-i ''
werkt NIET.
- BSD
-i
schakelt regelnummering per ingangsbestand verstandig in GNUsed
en recente versies van BSDsed
(bijvoorbeeld op FreeBSD 10) in, maar NIET op macOS vanaf 10.12 .
Merk op dat bij afwezigheid van-i
alle versies cumulatief getallenregels zijn voor alle invoerbestanden.Als de laatste ingangslijn heeft geen opvolgende newline (en afgedrukt):
- BSD
sed
: voegt altijd een nieuwe regel toe aan de uitvoer, zelfs als de invoerregel niet eindigt op één. - GNU
sed
: behoudt de status van de volglijn-newline , dwz het voegt alleen een nieuwe regel toe als de invoerregel eindigde in één.
- BSD
Gemeenschappelijke kenmerken:
- Als u uw
sed
scripts beperkt tot wat BSDsed
ondersteunt, zullen ze over het algemeen ook werken in GNUsed
- met de opmerkelijke uitzondering van het gebruik van platformspecifieke uitgebreide regex-functies met-E
. Vanzelfsprekend ziet u ook af van extensies die specifiek zijn voor de GNU-versie. Zie volgende sectie.
- Als u uw
Richtlijnen voor platformoverschrijdende ondersteuning (OS X / BSD, Linux), aangedreven door de strengere eisen van de BSD-versie :
Merk op dat de shorthands MacOS en Linux nu en dan hieronder zijn gebruikt om te verwijzen naar de BSD en GNU versies van sed
, respectievelijk, omdat ze de voorraad versies op elk platform. Het is echter mogelijk om GNU sed
op macOS te installeren, bijvoorbeeld met behulp van Homebrew met brew install gnu-sed
.
Opmerking : Behalve wanneer de vlaggen -r
en -E
worden gebruikt ( uitgebreide regexes), komen de onderstaande instructies neer op het schrijven van POSIX-compatibele sed
scripts.
Voor POSIX compliance, moet je jezelf te beperken tot POSIX Retourenveloppen (basis reguliere expressies) , die, helaas, zoals de naam al doet vermoeden, heel basic.
Voorbehoud : ga er niet vanuit dat\|
,\+
en\?
worden ondersteund: hoewel GNUsed
ondersteunt (tenzij--posix
wordt gebruikt), doet BSDsed
dat niet - deze functies zijn niet POSIX-compatibel.
Terwijl\+
en\?
kan worden geëmuleerd op POSIX-conforme wijze:
\{1,\}
voor\+
,
\{0,1\}
voor\?
,
\|
(afwisseling) kan helaas niet.Voor krachtigere reguliere expressies, gebruik
-E
(in plaats van-r
) ter ondersteuning van ERE's ( uitgebreide reguliere expressies) (GNUsed
documenteert-E
, maar het werkt daar wel als alias van-r
; nieuwere versie van BSDsed
, zoals op FreeBSD 10, ondersteunt nu ook-r
, maar de macOS-versie vanaf 10.12 niet ).
Voorbehoud : Hoewel het gebruik van-r
/-E
betekent dat uw opdracht per definitie niet POSIX-compatibel is, moet u zich toch beperken tot POSIX ERE's (uitgebreide reguliere expressies) . Helaas betekent dit dat je niet in staat zult zijn om verschillende bruikbare constructies te gebruiken, met name:- woordgebonden beweringen, omdat ze platformspecifiek zijn (bijvoorbeeld
\<
op Linux,[[:<]]
op OS X). - back-referenties in reguliere expressies (in tegenstelling tot de "back-referenties" voor het vastleggen van groepswedstrijden in de vervangende string van
s
functieaanroepen), omdat BSDsed
ze niet ondersteunt in uitgebreide regexen (maar, vreemd genoeg, doet dit in basiscodes , waar ze POSIX-verplicht zijn).
- woordgebonden beweringen, omdat ze platformspecifiek zijn (bijvoorbeeld
Escape-reeksen met controletekens zoals
\n
en\t
:Neem in regexen (zowel in patronen voor lijnselectie als het eerste argument voor de functie
s
) aan dat alleen\n
wordt herkend als een escape-reeks (zelden gebruikt, omdat de patroonruimte meestal een enkele lijn is (zonder terminatie\n
), maar niet binnen een tekenklasse , zodat bijvoorbeeld[^\n]
niet werkt; (als uw invoer geen controletekens bevat. Anders dan\t
, kunt u[^\n]
emuleren met[[:print:][:blank:]]
; anders, splits controle karakters in als letterlijke [2] ) - omvatten in het algemeen controletekens als letterlijke tekens, hetzij via gesplitste ANSI C-geciteerde strings (bijv.$'\t'
) in shells die het ondersteunen (bash,
ksh,zsh
), of via opdrachtvervangingen metprintf
(bijv."$(printf '\t')"
) .- Alleen Linux:
sed 's/\t/-/' <<<$'a\tb' # -> 'a-b'
- OSX en Linux:
sed 's/'$'\t''/-/' <<<$'a\tb' # ANSI C-quoted string
sed 's/'"$(printf '\t')"'/-/' <<<$'a\tb' # command subst. with printf
- Alleen Linux:
Neem in vervangende tekenreeksen die worden gebruikt met het commando
s
, aan dat er GEEN escape-escape-reeksen worden ondersteund , dus nogmaals, controletekens. als letterlijk , zoals hierboven.- Alleen Linux:
sed 's/-/\t/' <<<$'ab' # -> 'a<tab>b'
- macOS en Linux:
sed 's/-/'$'\t''/' <<<'a-b'
sed 's/-/'"$(printf '\t')"'/' <<<'a-b'
- Alleen Linux:
Idem voor de tekst argumenten om de
i
ena
functies: gebruik geen controle-tekenreeksen - zie hieronder.
Labels en vertakking: labels, alsmede de label-naam argument om de
b
ent
functies moeten worden gevolgd door een letterlijke nieuwe regel of een gesplitst-in$'\n'
. U kunt ook meerdere-e
opties gebruiken en elk recht na de labelnaam beëindigen.- Alleen Linux:
sed -n '/a/ bLBL; d; :LBL p' <<<$'a\nb' # -> 'a'
- macOS en Linux:
- OF (actuele nieuwe regels):
sed -n '/a/ bLBL d; :LBL p' <<<$'a\nb'
- OF (ingesloten
$\n
instanties):
sed -n '/a/ bLBL'$'\n''d; :LBL'$'\n''p' <<<$'a\nb'
- OF (meerdere
-e
opties):
sed -n -e '/a/ bLBL' -e 'd; :LBL' -e 'p' <<<$'a\nb'
- OF (actuele nieuwe regels):
- Alleen Linux:
Functies
i
ena
voor het invoegen / toevoegen van tekst : volg de functienaam door\
, gevolgd door een letterlijke nieuwe regel of een gesplitste$'\n'
voordat u het tekstargument opgeeft.- Alleen Linux:
sed '1 i new first line' <<<$'a\nb' # -> 'new first line<nl>a<nl>b'
- OSX en Linux:
sed -e '1 i\'$'\n''new first line' <<<$'a\nb'
- Notitie:
- Zonder
-e
wordt het tekstargument op onverklaarbare wijze niet door een nieuwe regel beëindigd bij uitvoer op macOS (bug?). - Gebruik geen control-character escapes zoals
\n
en\t
in het tekstargument, omdat ze alleen worden ondersteund op Linux. - Als het tekstargument dus echte nieuwe binnenregels heeft,
\
-escape ze. - Als u extra opdrachten achter het tekstargument wilt plaatsen, moet u deze beëindigen met een (niet-geschaalde) nieuwe regel (letterlijk of gespliced in) of doorgaan met een afzonderlijke optie
-e
(dit is een algemene vereiste die op alle versies van toepassing is) .
- Zonder
- Alleen Linux:
Binnen de functie lijsten (meervoudige functie oproepen ingesloten in
{...}
), moet u ook de laatste functie te beëindigen, voor de afsluitende}
, met;
.- Alleen Linux:
-
sed -n '1 {p;q}' <<<$'a\nb' # -> 'a'
-
- macOS en Linux:
-
sed -n '1 {p;q;}' <<<$'a\nb'
-
- Alleen Linux:
GNU sed
specifieke functies ontbreken in BSD sed
helemaal:
GNU-functies die je misloopt als je beide platforms moet ondersteunen:
Verschillende regex-matching- en vervangingsopties (zowel in patronen voor lijnselectie als het eerste argument voor de
s
functie):- De
I
optie voor case-INgevoelige regex-matching (ongelooflijk, BSDsed
ondersteunt dit helemaal niet). - De
M
optie voor het matchen van meerdere regels (waarbij^
/$
overeenkomen met het begin / einde van elke regel ) - Zie https://www.gnu.org/software/sed/manual/sed.html#The-_0022s_0022-Command voor extra opties die specifiek zijn voor de functie
s
- De
Escape-reeksen
Aan substitutie gerelateerde escape-reeksen, zoals
\u
in het vervangingsargument van de functies///
die manipulatie van substring binnen limieten mogelijk maken; bijv.sed 's/^./\u&/' <<<'dog' # -> 'Dog'
- zie http://www.gnu.org/software/sed/manual/sed.html#The-_0022s_0022 -CommandoEscape-reeksen controlekarakters: naast
\n
,\t
, ... codepoint-gebaseerde escapes; alle volgende escapes (hex., octaal, decimaal) vertegenwoordigen bijvoorbeeld een enkele quote ('
):\x27
,\o047
,\d039
- zie https://www.gnu.org/software/sed/manual/ sed.html # Escapes
Adressextensies , zoals de
first~step
die overeenkomt met elke stap-de regel,addr, +N
om te matchen met N regels naaddr
, ... - zie http://www.gnu.org/software/sed/manual/sed. html # Adressen
[1] De macOS sed
versie is ouder dan de versie op andere BSD-achtige systemen zoals FreeBSD en PC-BSD. Helaas betekent dit dat je niet kunt aannemen dat functies die in FreeBSD werken, bijvoorbeeld [hetzelfde] zullen werken op macOS.
[2] De ANSI C-geciteerde string $'\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'
bevat alle ASCII-controletekens behalve \n
(en NUL), dus u kunt het gebruiken in combinatie met [:print:]
voor een behoorlijk robuuste emulatie van [^\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'']
Vervang alle nieuwe regels door tabbladen
Opmerking: voor de duidelijkheid gebruiken de opdrachten hier-strings ( <<<
) en ANSI C-geciteerde strings ( $'...'
) . Beide shell-functies werken in bash
, ksh
en 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 merkt op:
Let op de noodzaak om labels (
:a
) en vertakkingscommando's (ba
) te beëindigen met daadwerkelijke nieuwe regels of met afzonderlijke-e
opties.Aangezien control tekens escapereeksen zoals
\t
worden niet ondersteund in de vervangende tekenreeks wordt een ANSI C geciteerde tab letterlijke gesplitst in de vervangingstekenreeks.
(In het regex- gedeelte herkent BSD Sed alleen\n
als een escape-reeks).
Voeg letterlijke tekst toe aan een regel met functie 'a'
Opmerking: voor de duidelijkheid gebruiken de opdrachten hier-strings ( <<<
) en ANSI C-geciteerde strings ( $'...'
) . Beide shell-functies werken in bash
, ksh
en 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'
Merk op dat BSD Seed een \
vereist, gevolgd door een daadwerkelijke nieuwe regel om de tekst door te voegen.
Hetzelfde geldt voor de bijbehorende functies i
(invoegen) en c
(verwijderen en invoegen).