web-dev-qa-db-fra.com

Comment échapper à des personnages non-échappés avec sed?

J'aimerais utiliser sed pour échapper à toutes les occurrences non échappées d'un caractère, par exemple "&", dans une chaîne contenue dans la variable text. Ce que je fais c'est

text='one&two\&three'
sed 's/\([^\\]\)&/\1\\&/g' <<< "${text}"

et j'attends que la sortie soit one\&two\&three. Cependant, ce que je reçois est

one\e&two\&three

Ce que je (essaie de) faire:

  • le modèle de recherche \([^\\]\)& doit correspondre à toute occurrence de & non précédé d'une barre oblique inversée et stocker le caractère qui précède & dans \1
  • le modèle de remplacement \1\\& devrait mettre une barre oblique inverse entre & et le caractère précédent, mais il agit comme \\\1& pour une raison étrange

Qu'est-ce que je fais mal ici?

2
AndreasT

Pourquoi votre commande échoue:

Tu l'as fait:

sed 's/\([^\\]\)&/\1\\&/g' <<< "${text}"
  • [^\\]\ correspond à n'importe quel caractère sauf \, et le place dans le groupe 1 correspondant, puis & correspond à un littéral &. Donc, pour one&two\&three, cela correspond à e avant le premier &, placez-le dans le groupe capturé 1. Pour le & avant three, cela ne correspond pas. comme \ est avant &

  • Dans le remplacement, vous avez utilisé \1\\&, la sortie devient donc one\e&two\&three car:

    • \1 est remplacé par e
    • alors deux \\s sont traités comme un seul \. cela nous donne e\ jusqu'à maintenant
    • alors & correspondra à la correspondance complète c'est-à-dire e& c'est-à-dire & ne sera pas échappé comme vous le pensiez
  • Ainsi, la partie correspondante, à savoir e& est remplacée par e\e&

    Vous obtiendrez le résultat souhaité si vous utilisiez un autre \ avant & (Comme deux \\ en font un \, vous en aurez donc besoin avant & :

    sed 's/\([^\\]\)&/\1\\\&/g' <<<"${text}"
    

    Comme sed d'Ubuntu prend en charge ERE (expression régulière étendue), vous pouvez utiliser l'option -E ou -r pour l'activer afin de supprimer le ()s lors de la capture:

    sed -E 's/([^\\])&/\1\\\&/g' <<<"${text}"
    

Autre approche:

Tout d'abord, supprimez \s avant tout &s, puis ajoutez \ avant tout &:

sed -E 's/[\]+(&)/\1/g; s/&/\\&/g'

Ceci est composé de deux déclarations sed:

  • s/[\]+(&)/\1/g supprime tous les \s avant & de la chaîne (ligne)

  • s/&/\\&/g ajoute un \ trop à tous & dans la chaîne (ligne)


Exemple:

% text='one&two\&three'                       

% sed 's/\([^\\]\)&/\1\\\&/g' <<< "${text}"
one\&two\&three

% sed -E 's/([^\\])&/\1\\\&/g' <<< "${text}" 
one\&two\&three

% sed -E 's/[\]+(&)/\1/g; s/&/\\&/g' <<<"$text"
one\&two\&three
4
heemayl