web-dev-qa-db-fra.com

De quels caractères ai-je besoin pour m'échapper lors de l'utilisation de sed dans un script sh?

Prenez le script suivant:

#!/bin/sh
sed 's/(127\.0\.1\.1)\s/\1/' [some file]

Si j'essaye d'exécuter ceci dans sh (dash ici), cela échouera à cause des parenthèses, qui doivent être échappées. Mais j'ai pas besoin d'échapper aux barres obliques inverses elles-mêmes (entre les octets, ou dans le \s ou \1). Quelle est la règle ici? Et quand dois-je utiliser {...} ou [...]? Y a-t-il une liste de ce que je fais et dont je n'ai pas besoin de m'échapper?

271
detly

Il y a deux niveaux d'interprétation ici: le Shell et sed.

Dans le Shell, tout entre guillemets simples est interprété littéralement, à l'exception des guillemets simples eux-mêmes. Vous pouvez effectivement avoir un guillemet simple entre guillemets simples en écrivant '\'' (fermer le guillemet simple, un guillemet simple littéral, ouvrir le guillemet simple).

Sed utilise expressions régulières de base . Dans un BRE, afin de les traiter littéralement, les caractères $.*[\^ doivent être cités en les précédant d'une barre oblique inverse, sauf à l'intérieur des jeux de caractères ([…]). Lettres, chiffres et (){}+?| ne doit pas être cité (vous pouvez vous en tirer en citant certaines dans certaines implémentations). Les séquences \(, \), \n, et dans certaines implémentations \{, \}, \+, \?, \| et les autres barres obliques inverses + alphanumériques ont des significations spéciales. Vous pouvez vous en tirer sans citer $^ dans certaines positions dans certaines implémentations.

De plus, vous avez besoin d'une barre oblique inverse avant / s'il doit apparaître dans l'expression régulière en dehors des expressions entre crochets. Vous pouvez choisir un autre caractère comme délimiteur en écrivant, par exemple, s~/dir~/replacement~ ou \~/dir~p; vous aurez besoin d'une barre oblique inverse avant le délimiteur si vous souhaitez l'inclure dans le BRE. Si vous choisissez un caractère qui a une signification particulière dans un BRE et que vous souhaitez l'inclure littéralement, vous aurez besoin de trois barres obliques inverses; Je ne recommande pas cela, car il peut se comporter différemment dans certaines implémentations.

En bref, pour sed 's/…/…/':

  • Écrivez l'expression régulière entre guillemets simples.
  • Utilisation '\'' pour finir avec une seule citation dans l'expression régulière.
  • Mettez une barre oblique inverse avant $.*/[\]^ et uniquement ces caractères (mais pas les expressions entre crochets). (Techniquement, vous ne devriez pas mettre de barre oblique inverse avant ] mais je ne connais pas d'implémentation qui traite ] et \] différemment en dehors des expressions entre crochets.)
  • À l'intérieur d'une expression entre crochets, pour - pour être traité littéralement, assurez-vous qu'il est le premier ou le dernier ([abc-] ou [-abc], ne pas [a-bc]).
  • À l'intérieur d'une expression entre crochets, pour ^ à traiter littéralement, assurez-vous que ce n'est pas pas en premier (utilisez [abc^], ne pas [^abc]).
  • Inclure ] dans la liste des caractères correspondant à une expression entre crochets, faites-en le premier caractère (ou le premier après ^ pour un ensemble nié): []abc] ou [^]abc] (ne pas [abc]] ni [abc\]]).

Dans le texte de remplacement:

  • & et \ doivent être cités en les précédant d'une barre oblique inverse, tout comme le délimiteur (généralement /) et les nouvelles lignes.
  • \ suivi d'un chiffre a une signification particulière. \ suivi d'une lettre a une signification spéciale (caractères spéciaux) dans certaines implémentations, et \ suivi d'un autre caractère signifie \c ou c selon l'implémentation.
  • Avec des guillemets simples autour de l'argument (sed 's/…/…/'), utilisation '\'' pour mettre une seule citation dans le texte de remplacement.

Si l'expression régulière ou le texte de remplacement provient d'une variable Shell, n'oubliez pas que

  • L'expression régulière est un BRE, pas une chaîne littérale.
  • Dans l'expression régulière, une nouvelle ligne doit être exprimée par \n (qui ne correspondra jamais sauf si vous avez un autre code sed ajoutant des caractères de nouvelle ligne à l'espace de motif). Mais notez que cela ne fonctionnera pas à l'intérieur des expressions entre crochets avec certaines implémentations sed.
  • Dans le texte de remplacement, &, \ et les nouvelles lignes doivent être citées.
  • Le délimiteur doit être cité (mais pas à l'intérieur des expressions entre crochets).
  • Utilisez des guillemets doubles pour l'interpolation: sed -e "s/$BRE/$REPL/".
309

Le problème que vous rencontrez n'est pas dû à l'interpolation et aux échappements de Shell - c'est parce que vous essayez d'utiliser la syntaxe d'expression régulière étendue sans passer sed le -r ou --regexp-extended option.

Changez votre ligne sed de

sed 's/(127\.0\.1\.1)\s/\1/' [some file]

à

sed -r 's/(127\.0\.1\.1)\s/\1/' [some file]

et cela fonctionnera comme je crois que vous avez l'intention.

Par défaut, sed utilise des expressions régulières de base (pensez au style grep), qui nécessiteraient la syntaxe suivante:

sed 's/\(127\.0\.1\.1\)[ \t]/\1/' [some file]
45
R Perrin

À moins que vous ne vouliez interpoler une variable Shell dans l'expression sed, utilisez des guillemets simples pour l'expression entière, car ils font que tout le reste est interprété tel quel, y compris les barres obliques inverses.

Donc, si vous voulez que sed voit s/\(127\.0\.1\.1\)\s/\1/ mettre des guillemets simples autour de lui et le Shell ne touchera pas les parenthèses ou les barres obliques inverses qu'il contient. Si vous devez interpoler une variable Shell, mettez uniquement cette partie entre guillemets. Par exemple.

sed 's/\(127\.0\.1\.1\)/'"$ip"'/'

Cela vous évitera de vous souvenir des métacaractères Shell qui ne sont pas échappés par des guillemets doubles.

18
Kyle Jones

Je pense qu'il vaut la peine de mentionner que, alors que sed est basé sur le standard POSIX, qui spécifie le support uniquement pour l'expression régulière de base (BRE), deux versions différentes de la commande sed existent réellement - BSD (Mac OS) et GNU (distributions Linux) . Chaque version implémente des extensions similaires et uniques au standard POSIX et peut affecter les fonctionnalités de sed sur différentes plates-formes. Par conséquent, la syntaxe appropriée de la commande sed, fonctionnant comme prévu sur un système, peut en fait se traduire par des résultats complètement différents sur un autre. Cela peut entraîner un comportement inattendu en ce qui concerne l'utilisation de caractères d'échappement et spéciaux.

Ces extensions au standard POSIX ont tendance à être plus répandues sur la version GNU de sed, offrant souvent la commodité d'un formatage moins strict, en particulier par rapport à la version BSD. Cependant, bien que GNU sed permette la fonctionnalité de certains caractères spéciaux, ils ne sont toujours pas conformes à POSIX. De plus, la seule vraie différence entre l'expression régulière de base et l'expression régulière (ERE), dans GNU sed, est le comportement des caractères spéciaux suivants:

"?", "+", Parenthèses, accolades ("{}") et "|"

Bien que cela puisse être le cas, certains caractères spéciaux ont une prise en charge limitée ou inexistante sur BSD sed, tels que "|", "?" Et "+", car il adhère plus étroitement aux normes de syntaxe POSIX. L'inclusion de ces caractères, d'une manière similaire à celle de GNU sed, entraînera souvent des problèmes de portabilité et de fonctionnalité des scripts utilisant sed. Il convient également de noter que la syntaxe POSIX BRE ne définit pas de signification pour certaines séquences d'échappement, notamment:\|, +,\?, `,\',\<,>,\b,\B,\w et\W ,.

Pour ceux qui exécutent la version BSD/Mac OS de sed, l'émulation du comportement de certains caractères spéciaux peut être un peu délicate, mais cela peut être fait dans la plupart des cas. Par exemple, + pourrait être émulé de façon conforme à POSIX comme ceci: {1,} et \? ressemblerait à ceci: {0,1} Les séquences de caractères de contrôle, cependant, ne sont généralement pas prises en charge. Dans la mesure du possible, il est certainement plus facile d'utiliser GNU sed, mais si vous avez besoin de fonctionnalités sur les deux plates-formes, n'oubliez pas d'utiliser uniquement les fonctionnalités POSIX, pour garantir la portabilité. Si vous êtes un utilisateur Mac et souhaitez profiter de GNU sed par opposition à BSD sed, vous pouvez essayer d'installer Homebrew et de télécharger GNU sed via la ligne de commande avec: $ brew install gnu-sed.

Pour conclure, les différences de version peuvent vraiment dicter à quoi pourrait ressembler la syntaxe appropriée, ou quels caractères sont nécessaires pour s'échapper. J'espère que cela fournit un contexte supplémentaire pour la question initiale ainsi que la réponse acceptée, et aide les autres à réfléchir à la façon de procéder, en fonction de l'objectif final de leur utilisation de script et de commande.

0
forthelulz