sed
BSD / macOS Sed vs. GNU Sed par rapport à la spécification POSIX Sed
Recherche…
Introduction
Pour citer la demande de création de sujet de @ SnoringFrog:
«L'un des plus gros problèmes rencontrés avec sed est celui des scripts qui échouent (ou réussissent de manière inattendue) car ils ont été écrits pour l'un et non pour l'autre.
Remarques
macOS utilise la version BSD de sed
[1] , qui diffère à plusieurs égards de la version GNU sed
fournie avec les distributions Linux .
Leur dénominateur commun est la fonctionnalité décrétée par POSIX : voir la spécification POSIX sed
.
L' approche la plus portable consiste à utiliser uniquement les fonctionnalités POSIX , qui limitent toutefois les fonctionnalités :
Notamment, POSIX spécifie que le soutien pour les expressions régulières de base, qui ont de nombreuses limites (par exemple, pas de support pour
|
(alternance) du tout, pas de soutien direct pour les+
et?
) Et des exigences différentes qui s'échappent.- Caveat: GNU
sed
(sans-r
), pris en charge par\|
,\+
et\?
, qui n'est PAS compatible POSIX; utiliser--posix
pour désactiver (voir ci-dessous).
- Caveat: GNU
Pour utiliser uniquement les fonctionnalités POSIX :
(les deux versions): utilisez uniquement les options
-n
et-e
(notamment, n'utilisez pas-E
ou-r
pour activer la prise en charge des expressions régulières étendues )GNU
sed
: possibilité d' ajouter--posix
pour assurer la fonctionnalité POSIX uniquement (vous ne strictement pas besoin, mais sans elle , vous pourriez finir par utiliser , par inadvertance , dispose de chambres non-POSIX sans se rendre compte, mise en garde:--posix
lui - même est non conforme aux spécifications POSIX )L'utilisation des fonctionnalités uniquement POSIX implique des exigences de formatage plus strictes (renonçant à de nombreuses fonctionnalités disponibles dans GNU
sed
):- Les séquences de caractères de contrôle telles que
\n
et\t
sont généralement pas prises en charge. - Les étiquettes et les commandes de ramification (par exemple,
b
) doivent être suivies d'un retour à la ligne réelle ou la continuation par l' intermédiaire d' un séparé-e
option. - Voir ci-dessous pour plus de détails.
- Les séquences de caractères de contrôle telles que
Cependant, les deux versions implémentent des extensions au standard POSIX:
- quelles extensions ils implémentent diffèrent (GNU
sed
implémente davantage). - même ces extensions qu'elles implémentent toutes deux diffèrent partiellement par leur syntaxe .
Si vous avez besoin de supporter les deux plates-formes (discussion des différences):
Caractéristiques incompatibles :
L'utilisation de l' option
-i
sans argument (mise à jour sur place sans sauvegarde) est incompatible:- BSD
sed
: DOIT utiliser-i ''
- GNU
sed
: DOIT utiliser juste-i
(équivalent:-i''
) - utiliser-i ''
ne fonctionne PAS.
- BSD
-i
tourne sensiblement la numérotation des lignes fichier par-entrée dans GNUsed
et les versions récentes de BSDsed
(par exemple, sur FreeBSD 10), mais pas sur Macos de 10,12.
Notez qu'en l'absence de-i
toutes les versions numérotent les lignes de manière cumulative sur les fichiers d'entrée.Si la dernière ligne en entrée ne comporte pas de nouvelle ligne (et est imprimée):
- BSD
sed
: ajoute toujours une nouvelle ligne à la sortie, même si la ligne de saisie ne se termine pas par une. - GNU
sed
: conserve le statut trailing-newline , c'est-à-dire qu'il ajoute une nouvelle ligne uniquement si la ligne d'entrée se termine par une.
- BSD
Caractéristiques communes :
- Si vous limitez vos scripts
sed
à ce que supporte BSDsed
, ils fonctionneront également sous GNUsed
, à l'exception notable des fonctionnalités regex étendues spécifiques à la plate-forme avec-E
. Evidemment, vous allez également renoncer à des extensions spécifiques à la version GNU. Voir la section suivante.
- Si vous limitez vos scripts
Directives pour la prise en charge multi-plateformes (OS X / BSD, Linux), guidées par les exigences plus strictes de la version BSD :
Notez que les raccourcis macOS et Linux sont occasionnellement utilisés ci-dessous pour faire référence respectivement aux versions BSD et GNU de sed
, car ce sont les versions stock sur chaque plate-forme. Cependant, il est possible d'installer GNU sed
sur macOS, par exemple, en utilisant Homebrew avec brew install gnu-sed
.
Remarque : Sauf lorsque les indicateurs -r
et -E
sont utilisés (expressions rationnelles étendues ), les instructions ci-dessous reviennent à écrire des scripts sed
conformes à POSIX .
Pour la conformité POSIX, vous devez vous limiter aux POSIX (expressions régulières de base ) , qui, malheureusement, sont assez simples, comme leur nom l'indique.
Avertissement : ne présumez pas que\|
,\+
et\?
sont supportés: bien que GNUsed
supporte (sauf si--posix
est utilisé), BSDsed
ne le fait pas - ces fonctionnalités ne sont pas compatibles avec POSIX.
Alors que\+
et\?
peut être émulé de manière compatible POSIX:
\{1,\}
pour\+
,
\{0,1\}
pour\?
,
\|
(alternance) ne peut malheureusement pas.Pour des expressions régulières plus puissantes, utilisez
-E
(plutôt que-r
) pour prendre en charge les ERE (expressions régulières étendues ) (GNUsed
ne documente pas-E
, mais il fonctionne comme alias de-r
; version plus récente de BSDsed
, comme sur FreeBSD 10, maintenant supporte également-r
, mais la version macOS à partir de 10.12 ne le fait pas .
Avertissement: Même si l' utilisation de-r
/-E
signifie que votre commande est par définition non conforme aux spécifications POSIX, vous devez toujours vous limiter à ERE (POSIX Les expressions régulières étendues) . Malheureusement, cela signifie que vous ne pourrez pas utiliser plusieurs constructions utiles, notamment:- assertions de limites de mots, car elles sont spécifiques à la plate-forme (par exemple,
\<
sous Linux,[[:<]]
sous OS X). - références arrière à l' intérieur des expressions régulières (par opposition aux « références arrière » pour les matches de capture-groupe dans la chaîne de remplacement
s
appels de fonction), car BSDsed
ne les supporte pas regexes étendus (mais, curieusement, le fait en ceux de base , où ils sont mandatés POSIX).
- assertions de limites de mots, car elles sont spécifiques à la plate-forme (par exemple,
Séquences d'échappement de caractère de contrôle telles que
\n
et\t
:Dans regexes ( à la fois dans les modèles de sélection de ligne et le premier argument de la
s
fonction), on suppose que seule\n
est reconnue comme une séquence d'échappement (rarement utilisé, étant donné que l'espace de travail est généralement une seule ligne (sans terminer\n
), mais pas à l'intérieur d'une classe de caractères , de sorte que, par exemple,[^\n]
ne fonctionne pas (si votre entrée ne contient aucun caractère de contrôle autre que\t
, vous pouvez émuler[^\n]
avec[[:print:][:blank:]]
; sinon, les caractères de contrôle splice. in en tant que littéraux [2] ) - incluent généralement les caractères de contrôle en tant que littéraux , soit par des chaînes citées ANSI ($'\t'
) dans les shells qui le supportent (bash,
ksh,zsh
), ou via des substitutions de commandes en utilisantprintf
(par exemple,"$(printf '\t')"
) .- Linux uniquement:
sed 's/\t/-/' <<<$'a\tb' # -> 'a-b'
- OSX et Linux:
sed 's/'$'\t''/-/' <<<$'a\tb' # ANSI C-quoted string
sed 's/'"$(printf '\t')"'/-/' <<<$'a\tb' # command subst. with printf
- Linux uniquement:
Dans les chaînes de remplacement utilisées avec la commande
s
, supposons qu'aucune séquence d'échappement de caractère de contrôle n'est prise en charge , donc, à nouveau, incluez des caractères de contrôle. comme littéraux , comme ci-dessus.- Linux uniquement:
sed 's/-/\t/' <<<$'ab' # -> 'a<tab>b'
- MacOS et Linux:
sed 's/-/'$'\t''/' <<<'a-b'
sed 's/-/'"$(printf '\t')"'/' <<<'a-b'
- Linux uniquement:
Idem pour les arguments textuels aux fonctions
i
eta
: n'utilisez pas de séquences de caractères de contrôle - voir ci-dessous.
Les étiquettes et les branchements : les étiquettes ainsi que l' argument label-name des fonctions
b
ett
doivent être suivis d'une ligne littérale ou d'un$'\n'
. Vous pouvez également utiliser plusieurs options-e
et terminer chacune après le nom de l'étiquette.- Linux uniquement:
sed -n '/a/ bLBL; d; :LBL p' <<<$'a\nb' # -> 'a'
- MacOS et Linux:
- SOIT (nouvelles lignes actuelles):
sed -n '/a/ bLBL d; :LBL p' <<<$'a\nb'
- OU (
$\n
instances épissées):
sed -n '/a/ bLBL'$'\n''d; :LBL'$'\n''p' <<<$'a\nb'
- OU (options
-e
multiples):
sed -n -e '/a/ bLBL' -e 'd; :LBL' -e 'p' <<<$'a\nb'
- SOIT (nouvelles lignes actuelles):
- Linux uniquement:
Fonctions
i
eta
pour insérer / ajouter du texte: suivre le nom de la fonction par\
, soit suivie par une nouvelle ligne littérale ou un épissé en$'\n'
avant de préciser l'argument texte.- Linux uniquement:
sed '1 i new first line' <<<$'a\nb' # -> 'new first line<nl>a<nl>b'
- OSX et Linux:
sed -e '1 i\'$'\n''new first line' <<<$'a\nb'
- Remarque:
- Sans
-e
, l'argument de texte n'est inexplicablement pas terminé par une nouvelle ligne en sortie sur macOS (bug?). - N'utilisez pas les caractères d'échappement tels que
\n
et\t
dans l'argument texte, car ils ne sont pris en charge que sous Linux. - Par conséquent, si l'argument de texte comporte des nouvelles lignes intérieures,
\
-escape les. - Si vous souhaitez placer des commandes supplémentaires après l'argument textuel, vous devez le terminer par une nouvelle ligne (non échapée) (littérale ou intégrée) ou continuer avec une option
-e
distincte (il s'agit d'une exigence générale qui s'applique à toutes les versions). .
- Sans
- Linux uniquement:
À l'intérieur des listes de fonctions (appels de fonctions multiples inclus dans
{...}
), veillez également à terminer la dernière fonction, avant la fermeture}
, avec;
.- Linux uniquement:
-
sed -n '1 {p;q}' <<<$'a\nb' # -> 'a'
-
- MacOS et Linux:
-
sed -n '1 {p;q;}' <<<$'a\nb'
-
- Linux uniquement:
Les caractéristiques spécifiques à GNU sed
sont absentes de BSD sed
:
Les fonctionnalités GNU qui vous manqueront si vous devez supporter les deux plates-formes:
Diverses options de correspondance et de substitution des expressions rationnelles (à la fois dans les modèles pour la sélection des lignes et dans le premier argument de la fonction
s
):- L'option
I
pour l'appariement de regex sensible à la casse (incroyablement, BSDsed
ne le supporte pas du tout). - L'option
M
pour la correspondance de plusieurs lignes (où^
/$
correspond au début / à la fin de chaque ligne ) - Pour plus d'options spécifiques à la fonction
s
, voir https://www.gnu.org/software/sed/manual/sed.html#The-_0022s_0022-Command
- L'option
Séquences d'échappement
Séquences d'échappement liées à la substitution telles que
\u
dans l'argument de remplacement de la fonctions///
qui permettent la manipulation de sous-chaînes , dans des limites; par exemple,sed 's/^./\u&/' <<<'dog' # -> 'Dog'
- voir http://www.gnu.org/software/sed/manual/sed.html#The-_0022s_0022 -CommanderSéquences d'échappement de caractères de contrôle: en plus de
\n
,\t
, ..., échappements basés sur des points de code; Par exemple, toutes les échappements suivants (hex., octal, décimal) représentent un guillemet simple ('
):\x27
,\o047
,\d039
- voir https://www.gnu.org/software/sed/manual/ sed.html # S'échappe
Les extensions d'adresse , telles que
first~step
pour faire correspondre chaque étape,addr, +N
pour correspondre à N lignes aprèsaddr
, ... - voir http://www.gnu.org/software/sed/manual/sed. html # Adresses
[1] La version macOS sed
est antérieure à la version des autres systèmes de type BSD tels que FreeBSD et PC-BSD. Malheureusement, cela signifie que vous ne pouvez pas supposer que les fonctionnalités fonctionnant sous FreeBSD, par exemple, fonctionneront de la même manière sous macOS.
[2] La chaîne $'\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'
caractères ANSI C-quoted $'\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'
contient tous les caractères de contrôle ASCII sauf \n
(et NUL), vous pouvez donc l'utiliser en combinaison avec [:print:]
pour une émulation assez robuste de [^\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'']
Remplacer toutes les nouvelles lignes par des onglets
Note: Pour des raisons de brièveté, les commandes utilisent les chaînes here-strings ( <<<
) et les guillemets ANSI ( $'...'
) . Ces deux fonctionnalités de shell fonctionnent sous bash
, ksh
et 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'
Notes BSD Sed:
Notez la nécessité de terminer les étiquettes (
:a
) et les commandes de branchement (ba
) avec des nouvelles lignes ou avec des options-e
distinctes.Comme les séquences d'échappement de caractère de contrôle telles que
\t
ne sont pas prises en charge dans la chaîne de remplacement, un littéral de tabulation ANSI C-cité est épissé dans la chaîne de remplacement.
(Dans la partie regex , BSD Sed ne reconnaît que\n
comme séquence d'échappement).
Ajouter du texte littéral à une ligne avec la fonction 'a'
Note: Pour des raisons de brièveté, les commandes utilisent les chaînes here-strings ( <<<
) et les guillemets ANSI ( $'...'
) . Ces deux fonctionnalités de shell fonctionnent sous bash
, ksh
et 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'
Notez que BSD Seed nécessite un \
suivi d'une nouvelle ligne pour transmettre le texte à ajouter.
Il en va de même pour les fonctions i
(insert) et c
(delete et insert) associées.